您的位置:首页 > 其它

自定义View—使用clipPath或者BitmapShader实现圆角图片

2015-08-16 22:30 786 查看
实现圆角图片的方式有三种,上篇文章中是使用了Xfermode,这篇文章则将总结剩下的两种clipPath、BitmapShader。这里我们跟上一篇一样继承自ImageView。

公共部分

无论是使用哪种方法,都需要自定义的属性和在构造器中获得相应的属性,因此将这部分放到一起。

自定义属性

我们这个圆角图片可以定义图片的圆角度数,因此需要自定义这个属性如些:

[code]<?xml version="1.0" encoding="utf-8"?>
<resources>

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

</resources>


在构造器中获得自定义属性

[code]private static final String TAG = "RoundImageView";

    private Paint mPaint;

    private Xfermode xfermode;
    /** 图片缩放的比例 */
    private float scale = 1.0f;
    /** 圆角半径 */
    private float mRadius;
    /** 默认的圆角半径 */
    private static final int DEFAULT_RADIUS = 10;
    public RoundImageView(Context context) {
        this(context, null);
    }

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

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        TypedArray ta = context.getTheme().obtainStyledAttributes(attr, R.styleable.RoundImageView,
                0, 0);
        mRadius = ta.getDimensionPixelSize(R.styleable.RoundImageView_radius, (int) TypedValue
                .applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_RADIUS, getResources()
                        .getDisplayMetrics()));
        Log.e(TAG, "mRadius:" + mRadius);
        ta.recycle();
    }


获得Bitmap

我们绘制时,需要用到Bitmap,因此需要获得资源src对应的Bitmap。

[code]/**
     * 获得Bitmap
     */
    private Bitmap getBitmap() {
        Bitmap bm = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Config.ARGB_8888);
        Drawable drawable = getDrawable();
        Canvas drawableCanvas = new Canvas(bm);
        // 计算缩放
        int drawablewidth = drawable.getIntrinsicWidth();
        int drawableheight = drawable.getIntrinsicHeight();
        int width = getMeasuredWidth();
        int height = getMeasuredHeight();
        scale = Math.min(width * 1.0f / drawablewidth, height * 1.0f / drawableheight);
        Log.e(TAG, scale + "");
        // 缩放
        drawable.setBounds(0, 0, (int) (drawablewidth * scale), (int) (drawableheight * scale));
        drawable.draw(drawableCanvas);
        return bm;
    }


clipPath实现圆角图片

首先需要说的一点是clipPath在api18之前都不支持硬件加速,因此在构造器中需要关闭硬件加速,如下

[code]setLayerType(LAYER_TYPE_SOFTWARE, mPaint);


Canvas类中提供对画布的裁剪的方法,有clipPath对具体路径进行裁剪,有clipRect对具体矩形进行裁剪,有clipRegion对具体区域进行裁剪。我们先看clipPath的两个方法:


clipPath(Path path)

Intersect the current clip with the specified path.

clipPath(Path path, Region.Op op)

Modify the current clip with the specified path.



我们注意到第二个方法里有个参数Region.Op op,这是一个枚举值:

[code]public enum Op {
         DIFFERENCE(0),
         INTERSECT(1),
         UNION(2),
         XOR(3),
         REVERSE_DIFFERENCE(4),
         REPLACE(5);
}


我们把之前的区域设为A,裁剪的区域为B,则以上枚举值的意义如下:


Region.Op DIFFERENCE

显示A-B

Region.Op INTERSECT

显示 A与B的交集

Region.Op REPLACE

显示B

Region.Op REVERSE_DIFFERENCE

显示B-A

Region.Op UNION

显示A与B的并集

Region.Op XOR

显示 A与B的并集-A与B的交集



不带Op参数的clipPath(Path path)则默认为使用INTERSECT。

了解这些后,我们就可以使用clipPath、重写onDraw来实现圆角图片了。首先需要对canvas进行裁剪,如何裁剪呢?我们需要的是圆角的图片,因此需要将canvas裁剪成圆角矩形的canvas,使得draw出来的是圆角的矩形。

[code]@Override
    protected void onDraw(Canvas canvas) {
        canvas.save();
        Path path = new Path();
        path.addRoundRect(new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight()), mRadius,
                mRadius, Path.Direction.CW);
        // 先对canvas进行裁剪
        canvas.clipPath(path, Region.Op.INTERSECT);

        Bitmap bm = getBitmap();
        // 绘制bitmap
        canvas.drawBitmap(bm, 0, 0, mPaint);

        canvas.restore();
    }


分析:代码中的canvas.clipPath(path, Region.Op.INTERSECT);

之前的区域是整个canvas,而裁剪后的区域的圆角矩形,我们的目的是圆角矩形,因此传入的Op参数可以REPLACE,也可以是INTERSECT,或者不传参数。

使用BitmapShader实现圆角图片

我们这时可能会发现之前的两种方法都有其缺点,Xfermode需要我们绘制两次,而clipPath不支持硬件加速,因此我们将希望寄予BitmapShader。

圆角图片有很多开源的代码,例如

https://github.com/vinc3m1/RoundedImageView

这个提到:


does not use a clipPath which is not hardware accelerated and not anti-aliased.

does not use setXfermode to clip the bitmap and draw twice to the canvas.



而查看其源代码,我们可以发现它是用BitmapShader实现的,因此我们可以说BitmapShader真是不负众望。

BitmapShader是Shader的一个子类,Shader,我们可以理解为着色器,也就是说在设置完画笔的属性之后,再去绘制形状,会以找个着色器来着色。BitmapShader则允许这个着色器为Bitmap。

Shader是Paint的一个属性,可以通过paint.setShader()来设置。

我们先看看BitmapShader对象是如何创建的:

[code]bitmapShader = new BitmapShader(bm, TileMode.CLAMP, TileMode.CLAMP);


第一个参数是Bitmap,说明着色器的Bitmap来源,后面的两个参数为TileMode,取值有三种:


CLAMP 拉伸

REPEAT 重复

MIRROR 镜像



也就说,如果图片的长宽不足,可以选择以重复、拉伸、镜像的方式来填充。

接下来的事情就简单了,再设置完paint的bitmapshader,只需要绘制一个圆角矩形就好,paint就会以bitmap的方式来对这个矩形进行着色。

[code]    @Override
    protected void onDraw(Canvas canvas) {
        Bitmap bm = getBitmap();
        bitmapShader = new BitmapShader(bm, TileMode.CLAMP, TileMode.CLAMP);
        mPaint.setShader(bitmapShader);

        canvas.drawRoundRect(new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight()), mRadius,
                mRadius, mPaint);
    }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: