文章目录

      • 引言
      • 第一式:ClipPath
          • 裁剪画布后,再画上图片:Canvas.clipPath--> Canvas.drawBitmap
      • 第二式:BitmapShader
          • 设置画笔的着色器,直接画图:paint.setShader --> canvas.drawCicle/drawRoundRect
      • 第三式:PorterDuffXfermode
          • 画布绘制的合成模式,也就是两张图片相交后的显示模式

引言

项目开发中显示头像大多都是圆形,以前使用第三方框架RoundedImageView 实现圆形头像,后来glide支持裁剪后,基本都使用它来实现图片的圆形、圆角。前阵子产品提了个需求,做一个“缩放”的动画,然而却是从圆形逐渐放大,放一张效果图:

这就需要自定义去实现了。那么在Android系统,又是如何实现图片裁剪的呢?本文梳理了三招,请看:

第一式:ClipPath

裁剪画布后,再画上图片:Canvas.clipPath–> Canvas.drawBitmap

1.Canvas中提供了对画布的裁剪。clipPath()对具体路径进行裁剪,还有clipRect和clipRegion等扩展方法。裁剪的模式有多种,默认是INTERSECT,交集。
注意clipPath在api18之前不支持硬件加速,所以最好处理下适配。

/**  * Intersect the current clip with the specified path. *  * * @param path The path to intersect with the current clip  * * @return     true if the resulting clip is non-empty  */ public boolean clipPath(@NonNull Path path) {         return clipPath(path, Region.Op.INTERSECT);  }

2.默认画布区域是A,裁剪区域是B,各模式如下:

Region.Op DIFFERENCE显示A-BRegion.Op INTERSECT显示 A与B的交集Region.Op REPLACE显示BRegion.Op REVERSE_DIFFERENCE显示B-ARegion.Op UNION显示A与B的并集Region.Op XOR显示 A与B的并集-A与B的交集

3.测试效果,参考代码(渐变效果)

 class ClipPathImage : View, Runnable {    private var mPaint = Paint()    private var bitmap = BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher)    private var innerRadius = 0    private var halfHeight = height / 2    private var mHandler = android.os.Handler()    constructor(context: Context) : super(context) {        init()    }    constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet) {        init()    }    constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int) : super(        context,        attributeSet,        defStyleAttr    ) {        init()    }    private fun init() {        mPaint.isAntiAlias = true        mPaint.style = Paint.Style.FILL        mPaint.color = ContextCompat.getColor(context, R.color.colorAccent)    }    /**     * 设置图片     */    fun setImageResource(drawable: Int) {        bitmap = BitmapFactory.decodeResource(resources, drawable)        innerRadius = 0        invalidate()    }    override fun onDraw(canvas: Canvas?) {        super.onDraw(canvas)        canvas?.drawColor(ContextCompat.getColor(context, R.color.gray1))        halfHeight = height / 2        //裁剪画布        if (innerRadius > 0 && innerRadius < halfHeight) {            val path = Path()            path.addCircle(halfHeight.toFloat(), halfHeight.toFloat(), innerRadius.toFloat(), Path.Direction.CW)            canvas?.clipPath(path)        }        if (bitmap != null) {            canvas?.drawBitmap(bitmap, 0f, 0f, mPaint)        }    }    /**     * 开启动画     */    fun startScaleAnim(drawable: Int) {        bitmap = BitmapFactory.decodeResource(resources, drawable)        innerRadius = height / 10        invalidate()        mHandler.postDelayed(this, 100)    }    override fun run() {        if (innerRadius < halfHeight) run {            innerRadius = innerRadius + height / 10            postInvalidate()            handler.postDelayed(this, 100)        } else {            innerRadius = halfHeight            postInvalidate()        }    }}

4.效果图:

第二式:BitmapShader

设置画笔的着色器,直接画图:paint.setShader --> canvas.drawCicle/drawRoundRect

1.着色器的模式有三种,是图片长宽不足时的处理方案

  • CLAMP 拉伸
  • REPEAT 重复
  • MIRROR 镜像

2.实现步骤:

  • 创建bitmapShader对象
  • 设置画笔的shader
  • 在画布上画出圆形或矩形

3.参考代码(渐变效果)

class ShaderImage(context: Context, attrs: AttributeSet?) : View(context, attrs), Runnable {    private var mPaint = Paint()    private var bitmap = BitmapFactory.decodeResource(resources, com.anan.testkotlin.R.mipmap.ic_launcher)    private var innerRadius = 0    private var halfHeight = height / 2    private var mHandler = android.os.Handler()    init {        mPaint.isAntiAlias = true//        mPaint.style = Paint.Style.FILL//        mPaint.color = ContextCompat.getColor(context, R.color.colorAccent)        setLayerType(View.LAYER_TYPE_HARDWARE, null);    }    /**     * 设置图片     */    fun setImageResource(drawable: Int) {        bitmap = BitmapFactory.decodeResource(resources, drawable)        innerRadius = 0        invalidate()    }    override fun onDraw(canvas: Canvas?) {        super.onDraw(canvas)        canvas?.drawColor(ContextCompat.getColor(context, com.anan.testkotlin.R.color.white))        halfHeight = height / 2        if (bitmap != null) {            //设置shader            if (innerRadius > 0 && innerRadius < halfHeight) {                setBitmapShader()                canvas?.drawCircle(halfHeight.toFloat(), halfHeight.toFloat(), innerRadius.toFloat(), mPaint)            } else {                canvas?.drawBitmap(bitmap, 0f, 0f, mPaint)            }        }    }    private fun setBitmapShader() {        var mBitmapShader = BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)        mPaint.shader = mBitmapShader    }    /**     * 开启动画     */    fun startScaleAnim(drawable: Int) {        bitmap = BitmapFactory.decodeResource(resources, drawable)        innerRadius = height / 10        invalidate()        mHandler.postDelayed(this, 100)    }    override fun run() {        if (innerRadius < halfHeight) run {            innerRadius = innerRadius + height / 10            postInvalidate()            handler.postDelayed(this, 100)        } else {            innerRadius = halfHeight            postInvalidate()        }    }}

4.效果图:

第三式:PorterDuffXfermode

画布绘制的合成模式,也就是两张图片相交后的显示模式

请看下篇。

更多相关文章

  1. android 显示进度的按钮
  2. Android学习心得(五)——GOOGLE MAP
  3. ImageView显示图片时,上下出现多余空白。
  4. Android文本输入框EditText方法说明和属性
  5. ✿Android(安卓)3.1 --- 久违的 USB、mtp、rtp
  6. Android(安卓)Studio 3.5.2版本中Kotlin代码不显示代码补齐自动
  7. Android(安卓)Fragment动态创建时replace()和add()方法的区别
  8. android中Canvas使用drawBitmap绘制图片
  9. Android(安卓)studio git 中文乱码 不显示中文

随机推荐

  1. 用c语言求1到1000的同构数
  2. c++优先队列用法详解
  3. c语言数组求和的方法
  4. c语言中如何用do...while语句求1到100的
  5. c语言如何求余数
  6. c++如何设置全局变量
  7. c语言和java语法有区别吗?
  8. c语言字符串结束标志是什么
  9. c++如何从函数返回数组
  10. C#正则表达式元字符详解