1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.support.v4.widget; 18 19import android.content.Context; 20import android.graphics.Canvas; 21import android.graphics.Color; 22import android.graphics.Paint; 23import android.graphics.RadialGradient; 24import android.graphics.Shader; 25import android.graphics.drawable.ShapeDrawable; 26import android.graphics.drawable.shapes.OvalShape; 27import android.support.v4.view.ViewCompat; 28import android.view.animation.Animation; 29import android.widget.ImageView; 30 31/** 32 * Private class created to work around issues with AnimationListeners being 33 * called before the animation is actually complete and support shadows on older 34 * platforms. 35 * 36 * @hide 37 */ 38class CircleImageView extends ImageView { 39 40 private static final int KEY_SHADOW_COLOR = 0x1E000000; 41 private static final int FILL_SHADOW_COLOR = 0x3D000000; 42 // PX 43 private static final float X_OFFSET = 0f; 44 private static final float Y_OFFSET = 1.75f; 45 private static final float SHADOW_RADIUS = 3.5f; 46 private static final int SHADOW_ELEVATION = 4; 47 48 private Animation.AnimationListener mListener; 49 private int mShadowRadius; 50 51 public CircleImageView(Context context, int color, final float radius) { 52 super(context); 53 final float density = getContext().getResources().getDisplayMetrics().density; 54 final int diameter = (int) (radius * density * 2); 55 final int shadowYOffset = (int) (density * Y_OFFSET); 56 final int shadowXOffset = (int) (density * X_OFFSET); 57 58 mShadowRadius = (int) (density * SHADOW_RADIUS); 59 60 ShapeDrawable circle; 61 if (elevationSupported()) { 62 circle = new ShapeDrawable(new OvalShape()); 63 ViewCompat.setElevation(this, SHADOW_ELEVATION * density); 64 } else { 65 OvalShape oval = new OvalShadow(mShadowRadius, diameter); 66 circle = new ShapeDrawable(oval); 67 ViewCompat.setLayerType(this, ViewCompat.LAYER_TYPE_SOFTWARE, circle.getPaint()); 68 circle.getPaint().setShadowLayer(mShadowRadius, shadowXOffset, shadowYOffset, 69 KEY_SHADOW_COLOR); 70 final int padding = mShadowRadius; 71 // set padding so the inner image sits correctly within the shadow. 72 setPadding(padding, padding, padding, padding); 73 } 74 circle.getPaint().setColor(color); 75 setBackgroundDrawable(circle); 76 } 77 78 private boolean elevationSupported() { 79 return android.os.Build.VERSION.SDK_INT >= 21; 80 } 81 82 @Override 83 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 84 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 85 if (!elevationSupported()) { 86 setMeasuredDimension(getMeasuredWidth() + mShadowRadius*2, getMeasuredHeight() 87 + mShadowRadius*2); 88 } 89 } 90 91 public void setAnimationListener(Animation.AnimationListener listener) { 92 mListener = listener; 93 } 94 95 @Override 96 public void onAnimationStart() { 97 super.onAnimationStart(); 98 if (mListener != null) { 99 mListener.onAnimationStart(getAnimation()); 100 } 101 } 102 103 @Override 104 public void onAnimationEnd() { 105 super.onAnimationEnd(); 106 if (mListener != null) { 107 mListener.onAnimationEnd(getAnimation()); 108 } 109 } 110 111 /** 112 * Update the background color of the circle image view. 113 * 114 * @param colorRes Id of a color resource. 115 */ 116 public void setBackgroundColorRes(int colorRes) { 117 setBackgroundColor(getContext().getResources().getColor(colorRes)); 118 } 119 120 @Override 121 public void setBackgroundColor(int color) { 122 if (getBackground() instanceof ShapeDrawable) { 123 ((ShapeDrawable) getBackground()).getPaint().setColor(color); 124 } 125 } 126 127 private class OvalShadow extends OvalShape { 128 private RadialGradient mRadialGradient; 129 private Paint mShadowPaint; 130 private int mCircleDiameter; 131 132 public OvalShadow(int shadowRadius, int circleDiameter) { 133 super(); 134 mShadowPaint = new Paint(); 135 mShadowRadius = shadowRadius; 136 mCircleDiameter = circleDiameter; 137 mRadialGradient = new RadialGradient(mCircleDiameter / 2, mCircleDiameter / 2, 138 mShadowRadius, new int[] { 139 FILL_SHADOW_COLOR, Color.TRANSPARENT 140 }, null, Shader.TileMode.CLAMP); 141 mShadowPaint.setShader(mRadialGradient); 142 } 143 144 @Override 145 public void draw(Canvas canvas, Paint paint) { 146 final int viewWidth = CircleImageView.this.getWidth(); 147 final int viewHeight = CircleImageView.this.getHeight(); 148 canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2 + mShadowRadius), 149 mShadowPaint); 150 canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2), paint); 151 } 152 } 153} 154