Android 图片裁剪之三剑式(一)

文章目录

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

引言

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

这就需要自定义去实现了。那么在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-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的交集

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

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

请看下篇。

你可能感兴趣的:(Android,UI)