196dfc170940d2f7218317242115eda1273863966Mindy Pereira/*
296dfc170940d2f7218317242115eda1273863966Mindy Pereira * Copyright (C) 2014 The Android Open Source Project
396dfc170940d2f7218317242115eda1273863966Mindy Pereira *
496dfc170940d2f7218317242115eda1273863966Mindy Pereira * Licensed under the Apache License, Version 2.0 (the "License");
596dfc170940d2f7218317242115eda1273863966Mindy Pereira * you may not use this file except in compliance with the License.
696dfc170940d2f7218317242115eda1273863966Mindy Pereira * You may obtain a copy of the License at
796dfc170940d2f7218317242115eda1273863966Mindy Pereira *
896dfc170940d2f7218317242115eda1273863966Mindy Pereira *      http://www.apache.org/licenses/LICENSE-2.0
996dfc170940d2f7218317242115eda1273863966Mindy Pereira *
1096dfc170940d2f7218317242115eda1273863966Mindy Pereira * Unless required by applicable law or agreed to in writing, software
1196dfc170940d2f7218317242115eda1273863966Mindy Pereira * distributed under the License is distributed on an "AS IS" BASIS,
1296dfc170940d2f7218317242115eda1273863966Mindy Pereira * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1396dfc170940d2f7218317242115eda1273863966Mindy Pereira * See the License for the specific language governing permissions and
1496dfc170940d2f7218317242115eda1273863966Mindy Pereira * limitations under the License.
1596dfc170940d2f7218317242115eda1273863966Mindy Pereira */
1696dfc170940d2f7218317242115eda1273863966Mindy Pereira
1796dfc170940d2f7218317242115eda1273863966Mindy Pereirapackage android.support.v4.widget;
1896dfc170940d2f7218317242115eda1273863966Mindy Pereira
1996dfc170940d2f7218317242115eda1273863966Mindy Pereiraimport android.content.Context;
2096dfc170940d2f7218317242115eda1273863966Mindy Pereiraimport android.graphics.Canvas;
21c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereiraimport android.graphics.Color;
2296dfc170940d2f7218317242115eda1273863966Mindy Pereiraimport android.graphics.Paint;
2396dfc170940d2f7218317242115eda1273863966Mindy Pereiraimport android.graphics.RadialGradient;
2496dfc170940d2f7218317242115eda1273863966Mindy Pereiraimport android.graphics.Shader;
2596dfc170940d2f7218317242115eda1273863966Mindy Pereiraimport android.graphics.drawable.ShapeDrawable;
2696dfc170940d2f7218317242115eda1273863966Mindy Pereiraimport android.graphics.drawable.shapes.OvalShape;
2796dfc170940d2f7218317242115eda1273863966Mindy Pereiraimport android.support.v4.view.ViewCompat;
2896dfc170940d2f7218317242115eda1273863966Mindy Pereiraimport android.view.animation.Animation;
2996dfc170940d2f7218317242115eda1273863966Mindy Pereiraimport android.widget.ImageView;
3096dfc170940d2f7218317242115eda1273863966Mindy Pereira
3196dfc170940d2f7218317242115eda1273863966Mindy Pereira/**
3296dfc170940d2f7218317242115eda1273863966Mindy Pereira * Private class created to work around issues with AnimationListeners being
33dd27d55a5152f86c07758067e587bdd5b719abafMindy Pereira * called before the animation is actually complete and support shadows on older
34dd27d55a5152f86c07758067e587bdd5b719abafMindy Pereira * platforms.
35dd27d55a5152f86c07758067e587bdd5b719abafMindy Pereira *
36dd27d55a5152f86c07758067e587bdd5b719abafMindy Pereira * @hide
3796dfc170940d2f7218317242115eda1273863966Mindy Pereira */
3896dfc170940d2f7218317242115eda1273863966Mindy Pereiraclass CircleImageView extends ImageView {
3996dfc170940d2f7218317242115eda1273863966Mindy Pereira
4096dfc170940d2f7218317242115eda1273863966Mindy Pereira    private static final int KEY_SHADOW_COLOR = 0x1E000000;
4196dfc170940d2f7218317242115eda1273863966Mindy Pereira    private static final int FILL_SHADOW_COLOR = 0x3D000000;
4296dfc170940d2f7218317242115eda1273863966Mindy Pereira    // PX
4396dfc170940d2f7218317242115eda1273863966Mindy Pereira    private static final float X_OFFSET = 0f;
4496dfc170940d2f7218317242115eda1273863966Mindy Pereira    private static final float Y_OFFSET = 1.75f;
4596dfc170940d2f7218317242115eda1273863966Mindy Pereira    private static final float SHADOW_RADIUS = 3.5f;
4696dfc170940d2f7218317242115eda1273863966Mindy Pereira    private static final int SHADOW_ELEVATION = 4;
4796dfc170940d2f7218317242115eda1273863966Mindy Pereira
4896dfc170940d2f7218317242115eda1273863966Mindy Pereira    private Animation.AnimationListener mListener;
49c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira    private int mShadowRadius;
5096dfc170940d2f7218317242115eda1273863966Mindy Pereira
5196dfc170940d2f7218317242115eda1273863966Mindy Pereira    public CircleImageView(Context context, int color, final float radius) {
5296dfc170940d2f7218317242115eda1273863966Mindy Pereira        super(context);
5396dfc170940d2f7218317242115eda1273863966Mindy Pereira        final float density = getContext().getResources().getDisplayMetrics().density;
5496dfc170940d2f7218317242115eda1273863966Mindy Pereira        final int diameter = (int) (radius * density * 2);
5596dfc170940d2f7218317242115eda1273863966Mindy Pereira        final int shadowYOffset = (int) (density * Y_OFFSET);
5696dfc170940d2f7218317242115eda1273863966Mindy Pereira        final int shadowXOffset = (int) (density * X_OFFSET);
57c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira
58c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira        mShadowRadius = (int) (density * SHADOW_RADIUS);
59c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira
6096dfc170940d2f7218317242115eda1273863966Mindy Pereira        ShapeDrawable circle;
61c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira        if (elevationSupported()) {
6296dfc170940d2f7218317242115eda1273863966Mindy Pereira            circle = new ShapeDrawable(new OvalShape());
6396dfc170940d2f7218317242115eda1273863966Mindy Pereira            ViewCompat.setElevation(this, SHADOW_ELEVATION * density);
6496dfc170940d2f7218317242115eda1273863966Mindy Pereira        } else {
65c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira            OvalShape oval = new OvalShadow(mShadowRadius, diameter);
6696dfc170940d2f7218317242115eda1273863966Mindy Pereira            circle = new ShapeDrawable(oval);
6796dfc170940d2f7218317242115eda1273863966Mindy Pereira            ViewCompat.setLayerType(this, ViewCompat.LAYER_TYPE_SOFTWARE, circle.getPaint());
68c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira            circle.getPaint().setShadowLayer(mShadowRadius, shadowXOffset, shadowYOffset,
69c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira                    KEY_SHADOW_COLOR);
70d9df810a8e9ac28148c853249f0951329053c784Mindy Pereira            final int padding = mShadowRadius;
7196dfc170940d2f7218317242115eda1273863966Mindy Pereira            // set padding so the inner image sits correctly within the shadow.
7296dfc170940d2f7218317242115eda1273863966Mindy Pereira            setPadding(padding, padding, padding, padding);
7396dfc170940d2f7218317242115eda1273863966Mindy Pereira        }
7496dfc170940d2f7218317242115eda1273863966Mindy Pereira        circle.getPaint().setColor(color);
7596dfc170940d2f7218317242115eda1273863966Mindy Pereira        setBackgroundDrawable(circle);
7696dfc170940d2f7218317242115eda1273863966Mindy Pereira    }
7796dfc170940d2f7218317242115eda1273863966Mindy Pereira
78c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira    private boolean elevationSupported() {
79a52784195525cdb1f2bb4d8dde1b8b314f480957Chet Haase        return android.os.Build.VERSION.SDK_INT >= 21;
80c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira    }
81c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira
82c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira    @Override
83c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
84c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
85c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira        if (!elevationSupported()) {
86c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira            setMeasuredDimension(getMeasuredWidth() + mShadowRadius*2, getMeasuredHeight()
87c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira                    + mShadowRadius*2);
88c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira        }
89c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira    }
90c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira
9196dfc170940d2f7218317242115eda1273863966Mindy Pereira    public void setAnimationListener(Animation.AnimationListener listener) {
9296dfc170940d2f7218317242115eda1273863966Mindy Pereira        mListener = listener;
9396dfc170940d2f7218317242115eda1273863966Mindy Pereira    }
9496dfc170940d2f7218317242115eda1273863966Mindy Pereira
9596dfc170940d2f7218317242115eda1273863966Mindy Pereira    @Override
9696dfc170940d2f7218317242115eda1273863966Mindy Pereira    public void onAnimationStart() {
9796dfc170940d2f7218317242115eda1273863966Mindy Pereira        super.onAnimationStart();
9896dfc170940d2f7218317242115eda1273863966Mindy Pereira        if (mListener != null) {
9996dfc170940d2f7218317242115eda1273863966Mindy Pereira            mListener.onAnimationStart(getAnimation());
10096dfc170940d2f7218317242115eda1273863966Mindy Pereira        }
10196dfc170940d2f7218317242115eda1273863966Mindy Pereira    }
10296dfc170940d2f7218317242115eda1273863966Mindy Pereira
10396dfc170940d2f7218317242115eda1273863966Mindy Pereira    @Override
10496dfc170940d2f7218317242115eda1273863966Mindy Pereira    public void onAnimationEnd() {
10596dfc170940d2f7218317242115eda1273863966Mindy Pereira        super.onAnimationEnd();
10696dfc170940d2f7218317242115eda1273863966Mindy Pereira        if (mListener != null) {
10796dfc170940d2f7218317242115eda1273863966Mindy Pereira            mListener.onAnimationEnd(getAnimation());
10896dfc170940d2f7218317242115eda1273863966Mindy Pereira        }
10996dfc170940d2f7218317242115eda1273863966Mindy Pereira    }
11096dfc170940d2f7218317242115eda1273863966Mindy Pereira
11196dfc170940d2f7218317242115eda1273863966Mindy Pereira    /**
11296dfc170940d2f7218317242115eda1273863966Mindy Pereira     * Update the background color of the circle image view.
113d9df810a8e9ac28148c853249f0951329053c784Mindy Pereira     *
114d9df810a8e9ac28148c853249f0951329053c784Mindy Pereira     * @param colorRes Id of a color resource.
11596dfc170940d2f7218317242115eda1273863966Mindy Pereira     */
116d9df810a8e9ac28148c853249f0951329053c784Mindy Pereira    public void setBackgroundColorRes(int colorRes) {
117d9df810a8e9ac28148c853249f0951329053c784Mindy Pereira        setBackgroundColor(getContext().getResources().getColor(colorRes));
118d9df810a8e9ac28148c853249f0951329053c784Mindy Pereira    }
119d9df810a8e9ac28148c853249f0951329053c784Mindy Pereira
120d9df810a8e9ac28148c853249f0951329053c784Mindy Pereira    @Override
121d9df810a8e9ac28148c853249f0951329053c784Mindy Pereira    public void setBackgroundColor(int color) {
12296dfc170940d2f7218317242115eda1273863966Mindy Pereira        if (getBackground() instanceof ShapeDrawable) {
123d9df810a8e9ac28148c853249f0951329053c784Mindy Pereira            ((ShapeDrawable) getBackground()).getPaint().setColor(color);
12496dfc170940d2f7218317242115eda1273863966Mindy Pereira        }
12596dfc170940d2f7218317242115eda1273863966Mindy Pereira    }
126c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira
127c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira    private class OvalShadow extends OvalShape {
128c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira        private RadialGradient mRadialGradient;
129c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira        private Paint mShadowPaint;
130c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira        private int mCircleDiameter;
131c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira
132c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira        public OvalShadow(int shadowRadius, int circleDiameter) {
133c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira            super();
134c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira            mShadowPaint = new Paint();
135c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira            mShadowRadius = shadowRadius;
136c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira            mCircleDiameter = circleDiameter;
137c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira            mRadialGradient = new RadialGradient(mCircleDiameter / 2, mCircleDiameter / 2,
138c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira                    mShadowRadius, new int[] {
139c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira                            FILL_SHADOW_COLOR, Color.TRANSPARENT
140c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira                    }, null, Shader.TileMode.CLAMP);
141c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira            mShadowPaint.setShader(mRadialGradient);
142c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira        }
143c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira
144c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira        @Override
145c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira        public void draw(Canvas canvas, Paint paint) {
146c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira            final int viewWidth = CircleImageView.this.getWidth();
147c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira            final int viewHeight = CircleImageView.this.getHeight();
148c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira            canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2 + mShadowRadius),
149c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira                    mShadowPaint);
150c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira            canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2), paint);
151c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira        }
152c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira    }
15396dfc170940d2f7218317242115eda1273863966Mindy Pereira}
154