Android自定义View之轻松实现圆角和圆形图片

Android自定义View系列

  • Android自定义View注意事项
  • Android自定义View之Paint绘制文字和线
  • Android自定义View之Canvas
  • Android自定义View之图像的色彩处理
  • Android自定义View之双缓冲机制和SurfaceView
  • Android自定义View之Window、ViewRootImpl和View的三大流程
  • Android自定义View之事件分发机制总结
  • Android自定义View之requestLayout方法和invalidate方法

在日常开发过程中,图片的特效处理是一个很常见的需求。除了颜色特效,还有就是外形的特效,比如圆角图片,圆形图片等。今天我们就来学习下图片的外形特效相关的知识

Paint画笔特效

Paint有一个专门用于处理图片外形特效的API

//Paint.class
public Xfermode setXfermode(Xfermode xfermode) {
    long xfermodeNative = 0;
    if (xfermode != null)
        xfermodeNative = xfermode.native_instance;
    nSetXfermode(mNativePaint, xfermodeNative);
    mXfermode = xfermode;
    return xfermode;
}

在Android的SDK中Xfermode只有一个子类:PorterDuffXfermode

PorterDuffXfermode控制图像的混合模式,影响的是2个图层交集区域的显示方式。

//PorterDuff.class
public enum Mode {
    /** [0, 0] */
    CLEAR       (0),
    /** [Sa, Sc] */
    SRC         (1),
    /** [Da, Dc] */
    DST         (2),
    /** [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] */
    SRC_OVER    (3),
    /** [Sa + (1 - Sa)*Da, Rc = Dc + (1 - Da)*Sc] */
    DST_OVER    (4),
    /** [Sa * Da, Sc * Da] */
    SRC_IN      (5),
    /** [Sa * Da, Sa * Dc] */
    DST_IN      (6),
    /** [Sa * (1 - Da), Sc * (1 - Da)] */
    SRC_OUT     (7),
    /** [Da * (1 - Sa), Dc * (1 - Sa)] */
    DST_OUT     (8),
    /** [Da, Sc * Da + (1 - Sa) * Dc] */
    SRC_ATOP    (9),
    /** [Sa, Sa * Dc + Sc * (1 - Da)] */
    DST_ATOP    (10),
    /** [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc] */
    XOR         (11),
    /** [Sa + Da - Sa*Da,
         Sc*(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)] */
    DARKEN      (16),
    /** [Sa + Da - Sa*Da,
         Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)] */
    LIGHTEN     (17),
    /** [Sa * Da, Sc * Dc] */
    MULTIPLY    (13),
    /** [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] */
    SCREEN      (14),
    /** Saturate(S + D) */
    ADD         (12),
    OVERLAY     (15);

    Mode(int nativeInt) {
        this.nativeInt = nativeInt;
    }

    /**
     * @hide
     */
    public final int nativeInt;
}

这么多种,分别是什么效果呢?看看下面的效果图就明白了。

其中需要说明的是Dst表示底层图层,也可以理解为遮罩层,而Src则是原图

实现圆角图片效果

原理很简单,就是通过一个和图片大小一样的带有圆角的图层和图片图层叠加起来
(1)初始化画布Canvas和画笔Paint

Bitmap bitmap = Bitmap.createBitmap(src.getWidth(), src.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
paint.setAntiAlias(true);

(2)在画布上画一个带圆角的图层,图层尺寸和原图一样

//这里80为圆角的半径
canvas.drawRoundRect(new RectF(0, 0, src.getWidth(), src.getHeight()),80,80, paint);

(3)为Paint设置图形混合模式SRC_IN,SRC_IN模式从上面的模式效果图中我们可以看出叠加后显示的是重叠部分后面一张图的部分

paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));

(4)绘制图片,并把新的Bitmap对象设置给ImageView

canvas.drawBitmap(src, 0, 0, paint);
imageView.setImageBitmap(bitmap)

通过以上的简单几步,我们就得到了一个圆角图片的效果。

上面我们采用的混合显示模式是SRC_IN,如果我们采用的是SRC_OUT呢?我们得到的是什么效果?

如果你理解了上面效果示意图所展示的效果,我想这个问题应该不难。SRC_OUT模式就是原图去掉叠加的部分,在我们的例子中也就是四个圆角了。

其他的模式如果不太理解,大家可以按照上面的例子试试,看看实际的效果,可以加深理解。

总结
  • 通过Paint画笔实现图形特效的关键在于为setXfermode()方法设置合适的叠加效果
  • 而要理解叠加效果就需要明白在Paint的setXfermode()方法之前canvas画的对应的是Mode效果示意图中的Dst图层,在Paint的setXfermode()方法之后canvas画的图则对应的是Mode效果示意图中的Src图层

Shader

Shader又被称之为着色器、渲染器,主要用来实现一系列的渐变、渲染效果。Android中一共有5种Shader,分别是BitmapShader(位图Shder)、LinearGradient(线性Shader)、RadialGradient(光束Shader)、SweepGradient(梯度Shader)、ComposeShader(混合Shader)

此外,Shader还提供了几种填充的模式

//Shader.class
public enum TileMode {
    /**
     * replicate the edge color if the shader draws outside of its
     * original bounds
     */
    CLAMP   (0),
    /**
     * repeat the shader's image horizontally and vertically
     */
    REPEAT  (1),
    /**
     * repeat the shader's image horizontally and vertically, alternating
     * mirror images so that adjacent images always seam
     */
    MIRROR  (2);

    TileMode(int nativeInt) {
        this.nativeInt = nativeInt;
    }
    final int nativeInt;
}
  • CLAMP:拉伸,最常用的一种模式
  • REPEAT:横向纵向不断重复
  • MIRROR:横向不断翻转重复、纵向不断翻转重复
BitmapShader(位图Shder)

BitmapShader(位图Shder)跟其他4种Shader不一样,它产生的是一个图像,有点类似PS中的图像填充渐变。它的作用就是通过Paint对画布进行指定的Bitmap填充。

BitmapShader一种常用的情景就是生成圆形图片

利用BitmapShader生成圆形图片

原理就是把要绘制的图片利用BitmapShader填充到圆形的画布上

(1)初始化画布Canvas和画笔Paint

Bitmap bitmap = Bitmap.createBitmap(src.getWidth(), src.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
paint.setAntiAlias(true);

(2)利用Bitmap生成BitmapShader,模式为拉伸模式

BitmapShader shader = new BitmapShader(src, Shader.TileMode.CLAMP,Shader.TileMode.CLAMP);

(3)将BitmapShader设置给Paint,便于后面绘制

paint.setShader(shader);

(4)利用设置好的Paint,在圆形的画布上填充图片,也就是Canvas要调用drawCircle方法

//这里圆形的半径我们用图片边长的一半
float radius = Math.min(src.getWidth()/2, src.getHeight()/2);
//这里圆形的圆点我们设在原图的中间
canvas.drawCircle(src.getWidth()/2, src.getHeight()/2, radius, paint);

通过以上简单的几步,我们就获得了图片的圆形效果

图里面上面的图是我们画出来的圆形图,下面的是开源库CircleImageView实现的圆形图,可见效果是一样的,难道CircleImageView也是用这个原理实现的?让我们一探究竟。

public class CircleImageView extends ImageView {
    ...

    @Override
    protected void onDraw(Canvas canvas) {
        if (mDisableCircularTransformation) {
            super.onDraw(canvas);
            return;
        }

        if (mBitmap == null) {
            return;
        }

        if (mCircleBackgroundColor != Color.TRANSPARENT) {
            canvas.drawCircle(mDrawableRect.centerX(), mDrawableRect.centerY(), mDrawableRadius, mCircleBackgroundPaint);
        }
        //这里就是绘制圆形图的地方
        canvas.drawCircle(mDrawableRect.centerX(), mDrawableRect.centerY(), mDrawableRadius, mBitmapPaint);
        if (mBorderWidth > 0) {
            canvas.drawCircle(mBorderRect.centerX(), mBorderRect.centerY(), mBorderRadius, mBorderPaint);
        }
    }

    private void setup() {
        ...

        //找到了,可见所采用的方法也是BitmapShader
        mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

        mBitmapPaint.setAntiAlias(true);
        mBitmapPaint.setShader(mBitmapShader);

       ...
    }

    ...
}

从上面CircleImageView的源码中我们可以看出,CircleImageView也是通过BitmapShader来实现图片的圆形特效的。有兴趣的朋友可以找来研究一下。

其他的填充渐变效果从字面意思就能看出来,大家有兴趣可以自己写个例子看看效果。

总结

  • 图形的外形特效主要是用到了Paint的setXfermode方法和setShader方法
  • setXfermode方法是通过设置不同的图层叠加模式来控制图形叠加后的效果,而setShader方法则是利用了类似PS中的图像填充渐变的理念为Paint设置了不同的填充渐变效果
  • 利用BitmapShader可以很方便地得到圆形的图片效果


欢迎关注我的微信公众号,和我一起每天进步一点点!

你可能感兴趣的:(Android自定义View)