自定义圆角图片控件(Xfermode方式)

苹果都放弃自己的棱角了。。。

看惯了方方正正的图片,乍一看到圆角图片觉得挺漂亮的。可当满世界都是圆角图片的时候,我又开始怀念那些棱角了~之前仓促的写过一个,今天拿过来又修改了一下,现在贴在这里,以方便以后ctrl+c、ctrl+v~~~~~

一、目标

    自定义一个图片控件,有圆形和圆角两种选择。控件的行为和ImageView一致

二、思路

    扩展ImageView控件,重写其onDraw方法。一开始还想重写onMeasure方法,如果显示圆形图片强制宽高相等,没能行得通(代码中会说明)。圆角图片以Xfermode方式实现,关于这个知识点大家可以参考这篇文章《setXfermode属性》。

三、实现

    控件类RoundImageView:

/**
 * 圆角或者圆形图片控件扩展自ImageView<br/>
 * 说明:<br/>
 * 1.仅重写了绘制流程,即onDraw()方法<br/>
 * 2.如果使用圆形图片需要设置固定的尺寸
 * 3.支持普通、圆形、圆角三种样式
 * 4.可设置圆角半径  默认是10dp
 * 5.无法为背景添加圆角
 * Created by 95 on 2016/1/11.
 */
public class RoundImageView extends ImageView
{
    private static final String TAG = RoundImageView.class.getSimpleName();
    private Paint mPaint;
    private WeakReference<Bitmap> mWeakBitmap;

    /**
     * 图片的类型,圆形or圆角
     */
    private int type;
    public static final int TYPE_NORMAL = 0;
    public static final int TYPE_CIRCLE = 1;
    public static final int TYPE_ROUND = 2;

    /**
     * 圆角大小的默认值
     */
    private static final int DEFAULT_RADIUS = 10;
    /**
     * 圆角的大小
     */
    private int mRadius;

    public RoundImageView(Context context)
    {
        this(context, null);
    }

    public RoundImageView(Context context, AttributeSet attrs)
    {
        super(context, attrs);

        mPaint = new Paint();
        mPaint.setAntiAlias(true);

        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RoundImageView);

        mRadius = a.getDimensionPixelSize(R.styleable.RoundImageView_radius, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_RADIUS, getResources().getDisplayMetrics()));// 默认为10dp
        type = a.getInt(R.styleable.RoundImageView_type, TYPE_NORMAL);// 默认为normal
        a.recycle();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        /**
         * 如果类型是圆形,则强制改变view的宽高一致,以小值为准
         * 这种写法是有问题的
         * 我们本来是想在正常测量的基础上取最短边
         * 可下面的操作插入到了正常执行的流程当中   致使最终的结果出了问题
         */
        //        if (type == TYPE_CIRCLE)
        //        {
        //            int min = Math.min(getMeasuredWidth(), getMeasuredHeight());
        //            setMeasuredDimension(min, min);
        //        }
    }

    @Override
    protected void onDraw(Canvas canvas)
    {
        //普通模式  调用原生的绘制方法
        if (type == TYPE_NORMAL)
        {
            super.onDraw(canvas);
            return;
        }
        //如果当前未设置图片  直接返回
        if (getDrawable() == null) return;
        //在缓存中取出bitmap
        Bitmap bitmap = mWeakBitmap == null ? null : mWeakBitmap.get();
        //如果bitmap已经被回收,就重新创建
        if (bitmap == null || bitmap.isRecycled())
        {
            bitmap = createDesiredBitmap(drawableToBitmap(getDrawable()), type, getMeasuredWidth(), getMeasuredHeight(), mRadius);
            mWeakBitmap = new WeakReference<Bitmap>(bitmap);
        }
        //以为我们所有的绘制使用了同一个Paint  所以每次使用之前都要考虑这个状态
        mPaint.setXfermode(null);
        //把最终结果绘制到画布上
        canvas.drawBitmap(bitmap, 0.0f, 0.0f, mPaint);
    }


    /**
     * 生成圆角图片 使用Xfermode方式
     *
     * @param src           原图
     * @param type          round or circle
     * @param desiredWidth  控件的宽
     * @param desiredHeight 控件的高
     * @param radius        圆角半径
     * @return
     */
    private Bitmap createDesiredBitmap(Bitmap src, int type, int desiredWidth, int desiredHeight, int radius)
    {
        Bitmap target = Bitmap.createBitmap(desiredWidth, desiredHeight, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(target);
        mPaint.setXfermode(null);
        if (TYPE_CIRCLE == type)
        {
            canvas.drawCircle(desiredWidth * 1.0f / 2, desiredWidth * 1.0f / 2, desiredWidth * 1.0f / 2, mPaint);
        } else
        {
            RectF rectF = new RectF(0.0f, 0.0f, desiredWidth, desiredHeight);
            canvas.drawRoundRect(rectF, radius * 1.0f, radius * 1.0f, mPaint);
        }
        //缩放比例  保证图片的宽和高大于控件的宽和高
        float ratio = Math.max(desiredWidth * 1.0f / src.getWidth(), desiredHeight * 1.0f / src.getHeight());
        Bitmap tem = getScaledBitmap(src, ratio);
        //设置Xfermode
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        //保证控件展示的位置即为图片正中间的内容
        canvas.drawBitmap(tem, (desiredWidth - tem.getWidth()) * 1.0f / 2, (desiredHeight - tem.getHeight()) * 1.0f / 2, mPaint);
        //释放内存
        tem.recycle();
        return target;
    }

    /**
     * 缩放图片
     *
     * @param src   原图
     * @param ratio 缩放比例
     * @return
     */
    private Bitmap getScaledBitmap(Bitmap src, float ratio)
    {
        Matrix matrix = new Matrix();
        matrix.setScale(ratio, ratio, 0.0f, 0.0f);
        Bitmap target = Bitmap.createBitmap((int) (src.getWidth() * ratio), (int) (src.getHeight() * ratio), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(target);
        canvas.drawBitmap(src, matrix, mPaint);
        return target;
    }

    /**
     * drawable 转 bitmap
     *
     * @param drawable
     * @return
     */
    private Bitmap drawableToBitmap(Drawable drawable)
    {
        if (drawable instanceof BitmapDrawable)
        {
            return ((BitmapDrawable) drawable).getBitmap();
        }
        Bitmap target = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(target);
        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
        drawable.draw(canvas);
        return target;
    }
}

     自定义属性:

    <declare-styleable name="RoundImageView">
        <attr name="type" format="enum">
            <enum name="normal" value="0"/>
            <enum name="circle" value="1"/>
            <enum name="round" value="2"/>
        </attr>

        <attr name="radius" format="dimension"/>
    </declare-styleable>

 四、使用

    直接在布局文件当中使用:

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_horizontal"
        android:orientation="vertical">

        <!--原图-->
        <com.hsji.testptr.widget.RoundImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/lena"/>

        <!--圆角效果,圆角半径是15dp-->
        <com.hsji.testptr.widget.RoundImageView
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:layout_marginTop="10dp"
            android:src="@drawable/lena"
            app:radius="15dp"
            app:type="round"/>
        
        <!--非正方形圆角效果,圆角半径60dp-->
        <com.hsji.testptr.widget.RoundImageView
            android:layout_width="120dp"
            android:layout_height="80dp"
            android:layout_marginTop="10dp"
            android:src="@drawable/lena"
            app:radius="60dp"
            app:type="round"/>

        <!--圆形效果-->
        <com.hsji.testptr.widget.RoundImageView
            android:layout_width="120dp"
            android:layout_height="120dp"
            android:layout_marginTop="10dp"
            android:src="@drawable/lena"
            app:type="circle"/>
    </LinearLayout>

 下面是效果图:

原文地址:https://www.cnblogs.com/hsji/p/5121698.html