Home » Android » imageview – Why don't set CircleImageView of android support v4 library public?

imageview – Why don't set CircleImageView of android support v4 library public?

Posted by: admin June 15, 2020 Leave a comment

Questions:

I have noticed android.support.v4.widget.CircleImageView for long. Every time I want to use an ImageView in a round shape, CircleImageView would appear in my mind. I have tried to use it for many times, but every time, I failed. Because the access permission of android.support.v4.widget.CircleImageView is default which means only classes in the same package with CircleImageView, namely, android.support.v4.widget, are able to access it.

I can’t understand now that round ImageView is used in common, why don’t set CircleImageView to public so that developers don’t have to override an ImageView into a Round ImageView? Isn’t it that Google Android team force us to reinvent wheels?

Or, don’t I know this CircleImageView well?

Any tips will be appreciated. Thanks in advance.

How to&Answers:

I tried to copy and paste source codes of android.support.v4.widget.CircleImageView to make it public just like this:

package me.danielpan.youtubelike.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RadialGradient;
import android.graphics.Shader;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.support.v4.view.ViewCompat;
import android.view.animation.Animation;
import android.widget.ImageView;

/**
 * Private class created to work around issues with AnimationListeners being
 * called before the animation is actually complete and support shadows on older
 * platforms.
 *
 * @hide
 */
public class CircleImageView extends ImageView {

    private static final int KEY_SHADOW_COLOR = 0x1E000000;
    private static final int FILL_SHADOW_COLOR = 0x3D000000;
    // PX
    private static final float X_OFFSET = 0f;
    private static final float Y_OFFSET = 1.75f;
    private static final float SHADOW_RADIUS = 3.5f;
    private static final int SHADOW_ELEVATION = 4;

    private Animation.AnimationListener mListener;
    private int mShadowRadius;

    public CircleImageView(Context context, int color, final float radius) {
        super(context);
        final float density = getContext().getResources().getDisplayMetrics().density;
        final int diameter = (int) (radius * density * 2);
        final int shadowYOffset = (int) (density * Y_OFFSET);
        final int shadowXOffset = (int) (density * X_OFFSET);

        mShadowRadius = (int) (density * SHADOW_RADIUS);

        ShapeDrawable circle;
        if (elevationSupported()) {
            circle = new ShapeDrawable(new OvalShape());
            ViewCompat.setElevation(this, SHADOW_ELEVATION * density);
        } else {
            OvalShape oval = new OvalShadow(mShadowRadius, diameter);
            circle = new ShapeDrawable(oval);
            ViewCompat.setLayerType(this, ViewCompat.LAYER_TYPE_SOFTWARE, circle.getPaint());
            circle.getPaint().setShadowLayer(mShadowRadius, shadowXOffset, shadowYOffset,
                    KEY_SHADOW_COLOR);
            final int padding = mShadowRadius;
            // set padding so the inner image sits correctly within the shadow.
            setPadding(padding, padding, padding, padding);
        }
        circle.getPaint().setColor(color);
        setBackgroundDrawable(circle);
    }

    private boolean elevationSupported() {
        return android.os.Build.VERSION.SDK_INT >= 21;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (!elevationSupported()) {
            setMeasuredDimension(getMeasuredWidth() + mShadowRadius*2, getMeasuredHeight()
                    + mShadowRadius*2);
        }
    }

    public void setAnimationListener(Animation.AnimationListener listener) {
        mListener = listener;
    }

    @Override
    public void onAnimationStart() {
        super.onAnimationStart();
        if (mListener != null) {
            mListener.onAnimationStart(getAnimation());
        }
    }

    @Override
    public void onAnimationEnd() {
        super.onAnimationEnd();
        if (mListener != null) {
            mListener.onAnimationEnd(getAnimation());
        }
    }

    /**
     * Update the background color of the circle image view.
     *
     * @param colorRes Id of a color resource.
     */
    public void setBackgroundColorRes(int colorRes) {
        setBackgroundColor(getContext().getResources().getColor(colorRes));
    }

    @Override
    public void setBackgroundColor(int color) {
        if (getBackground() instanceof ShapeDrawable) {
            ((ShapeDrawable) getBackground()).getPaint().setColor(color);
        }
    }

    private class OvalShadow extends OvalShape {
        private RadialGradient mRadialGradient;
        private Paint mShadowPaint;
        private int mCircleDiameter;

        public OvalShadow(int shadowRadius, int circleDiameter) {
            super();
            mShadowPaint = new Paint();
            mShadowRadius = shadowRadius;
            mCircleDiameter = circleDiameter;
            mRadialGradient = new RadialGradient(mCircleDiameter / 2, mCircleDiameter / 2,
                    mShadowRadius, new int[] {
                    FILL_SHADOW_COLOR, Color.TRANSPARENT
            }, null, Shader.TileMode.CLAMP);
            mShadowPaint.setShader(mRadialGradient);
        }

        @Override
        public void draw(Canvas canvas, Paint paint) {
            final int viewWidth = CircleImageView.this.getWidth();
            final int viewHeight = CircleImageView.this.getHeight();
            canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2 + mShadowRadius),
                    mShadowPaint);
            canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2), paint);
        }
    }
}

It looks well, right? It has no customized attributes and seems able to be used as a normal ImageView.

But if you have tried it, you will find out that NoSuchMethodException is thrown. This exception implies that necessary constructors are not overridden. So that you can’t even instantiate it as a normal View.

After reading these source codes, I realize that CircleImageView only adds shadow behind ImageView, whose result is not a RoundCornerImageView or RoundImageView. So if I want a RoundImageView, I have to forget this class and implement this effect by overriding an ImageView.

At last, there is the file comment, which points out the use of android.support.v4.widget.CircleImageView:

Private class created to work around issues with AnimationListeners
being called before the animation is actually complete and support
shadows on older platforms.

And I hope no one will ask such a stupid question again and let it end here, ^_^, Haha~

Answer:

As per the documentation this class is private class used for work around and we cannot instantiate it. May be that class will be removed sooner I suspect.

There are ways to create the circular background for a view.

file : drawable/contact_badge_round.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">

    <size android:width="32dp" android:height="32dp"/>

    <gradient android:centerColor="#123456" <!--Put your custom color for bg -->
              android:startColor="#123456"
              android:endColor="#123456"
        />

</shape>

then in your layout create a Button and set the background as contact_badge_round

 <ImageView
        android:id="@+id/roundContact"
        android:layout_width="32dp"
        android:layout_height="32dp"
        android:src="@drawalbe/your_image"
        android:gravity="center"
        android:background="@drawable/background_new_entity_symbol"
       />