Android自定义View-圆形图片控件

个人博客

http://www.milovetingting.cn

Android自定义View-圆形图片控件

前言

在日常开发中,圆形的图片效果还是很常见的。可以通过给Paint设置Xfermode来实现,这里简单记录如下。

实现

实现圆形效果的核心是PorterDuffXfermode,对于PorterDuffXfermode,这里不展开,可以查询相关资料。

核心代码

//绘制背景
canvas.drawCircle(mSize / 2, mSize / 2, mSize / 2, mPaint);
//设置模式为:显示背景层和上层的交集,且显示上层图像
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
//绘制要显示的图像
canvas.drawBitmap(mSrcBitmap, 0, 0, mPaint);
//重置Xfermode
mPaint.setXfermode(null);

自定义属性

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CircleView">
        <!--定义资源-->
        <attr name="src" format="reference" />
        <!--定义类型-->
        <attr name="type" format="enum">
            <!--圆形-->
            <enum name="round" value="1" />
            <!--矩形-->
            <enum name="rect" value="2" />
        </attr>
    </declare-styleable>
</resources>

自定义控件

public class CircleView extends View {

    private static final int DEFAULT_SIZE = 200;

    private static final int DEFAULT_RADIUS = 20;

    private static final int TYPE_ROUND = 1;

    private static final int TYPE_RECT = 2;

    private int mSize;

    private int mResourceId;

    private int mType;

    private Paint mPaint;

    private Bitmap mSrcBitmap;

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

    public CircleView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CircleView);
        mResourceId = ta.getResourceId(R.styleable.CircleView_src, R.mipmap.ic_launcher);
        mType = ta.getInt(R.styleable.CircleView_type, TYPE_ROUND);
        ta.recycle();
        init();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = getMeasureSize(widthMeasureSpec);
        int height = getMeasureSize(heightMeasureSpec);
        mSize = Math.min(width, height);
        setMeasuredDimension(mSize, mSize);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //绘制背景
        if (mSrcBitmap == null) {
            mSrcBitmap = getScaleBitmap();
        }
        if (mType == TYPE_ROUND) {
            canvas.drawCircle(mSize / 2, mSize / 2, mSize / 2, mPaint);
        } else if (mType == TYPE_RECT) {
            canvas.drawRoundRect(0, 0, mSize, mSize, DEFAULT_RADIUS, DEFAULT_RADIUS, mPaint);
        }
        //设置模式为:显示背景层和上层的交集,且显示上层图像
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        //绘制要显示的图像
        canvas.drawBitmap(mSrcBitmap, 0, 0, mPaint);
        //重置Xfermode
        mPaint.setXfermode(null);
    }

    private void init() {
        //禁用硬件加速,否则可能无法绘制圆形
        setLayerType(LAYER_TYPE_HARDWARE, null);
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL);
    }

    private int getMeasureSize(int measureSpec) {
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);
        return mode == MeasureSpec.EXACTLY ? size : DEFAULT_SIZE;
    }

    /**
     * 获取缩放后的Bitmap
     *
     * @return
     */
    private Bitmap getScaleBitmap() {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(getResources(), mResourceId, options);
        options.inSampleSize = calcSampleSize(options, mSize, mSize);
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeResource(getResources(), mResourceId, options);
    }

    /**
     * 计算缩放比例
     *
     * @param option
     * @param width
     * @param height
     * @return
     */
    private int calcSampleSize(BitmapFactory.Options option, int width, int height) {
        int originWidth = option.outWidth;
        int originHeight = option.outHeight;
        int sampleSize = 1;
        while ((originWidth = originWidth >> 1) > width && (originHeight = originHeight >> 1) > height) {
            sampleSize = sampleSize << 1;
        }
        return sampleSize;
    }
}

注意:如果没有圆形的效果,那么可能需要禁用硬件加速:setLayerType(LAYER_TYPE_HARDWARE, null)

布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <com.wangyz.custom.CircleView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        app:src="@drawable/image" />

    <com.wangyz.custom.CircleView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_margin="10dp"
        app:src="@drawable/image" />

    <com.wangyz.custom.CircleView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_margin="10dp"
        app:src="@drawable/image"
        app:type="rect" />

</LinearLayout>

效果

demo.png

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值