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

文章目录

      • 1.PorterDuffXfermode 简介
      • 2.PorterDuffXfermode 类型介绍
      • 3.PorterDuffXfermode 使用场景
            • 圆形头像
            • 叠加图片还可以实现橡皮擦和加载效果等
      • 4.PorterDuffXfermode 在Drawable中的应用

1.PorterDuffXfermode 简介

在《Android 图片裁剪之三剑式(一)》中讲述了图片裁剪的2种方式(clipPath/bitmaShader),本文探索最后一式裁剪:PorterDuffXfermode 。
PorterDuffXfermode 是画布绘制的合成模式,也就是两张图像混合后的显示模式,与setColorFilter很相似,我们可以使用它画出各种神奇的效果。

2.PorterDuffXfermode 类型介绍

API中为我们提供了18中模式:

  • Mode.CLEAR 清除图像
  • Mode.SRC 只显示源图像(上层图片)
  • Mode.DST 只显示目标图像(下层图片)
  • Mode.SRC_IN 只在源图像和目标图像相交的地方绘制【源图像】
  • Mode.DST_IN 只在源图像和目标图像相交的地方绘制【目标图像】
  • Mode.SRC_OUT 只在源图像和目标图像不相交的地方绘制【源图像】
  • Mode.DST_OUT 只在源图像和目标图像不相交的地方绘制【目标图像】
  • Mode.SRC_ATOP 在源图像和目标图像相交的地方绘制【源图像】,在不相交的地方绘制【目标图像】
  • Mode.DST_ATOP 在源图像和目标图像相交的地方绘制【目标图像】,在不相交的地方绘制【源图像】
  • Mode.SRC_OVER 将源图像放在目标图像上方
  • Mode.DST_OVER 将目标图像放在源图像上方
  • Mode.XOR 在源图像和目标图像相交的地方之外绘制它们
  • Mode.DARKEN 变暗,较深的颜色覆盖较浅的颜色,若两者深浅程度相同则混合
  • Mode.LIGHTEN 变亮,与DARKEN相反
  • Mode.OVERLAY 叠加
  • Mode.SCREEN 滤色,色调均和,保留两个图层中较白的部分,较暗的部分被遮盖
  • Mode.MULTIPLY 正片叠底,源图像素颜色值乘以目标图像素颜色值除以255得到混合后图像像素颜色值
  • Mode.ADD 饱和相加,对图像饱和度进行相加
    网上效果图:
    Android 图片裁剪之三剑式(二)_第1张图片
    我测试了常见的几种:
    Android 图片裁剪之三剑式(二)_第2张图片

3.PorterDuffXfermode 使用场景

圆形头像

主要采用了SRC_IN模式来实现,图标图片是圆形bitmap,源头像是具体图片,先看效果:
Android 图片裁剪之三剑式(二)_第3张图片
主要代码:

class XfermodeImage(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()

    private var xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)

    init {
        mPaint.isAntiAlias = true
        mPaint.style = Paint.Style.FILL
        //设定是否使用图像抖动处理,会使绘制出来的图片颜色更加平滑和饱满,图像更加清晰
        mPaint.isDither = true
        //加快显示速度,本设置项依赖于dither和xfermode的设置
        mPaint.isFilterBitmap = true
    }

    /**
     * 设置图片
     */
    fun setImageResource(drawable: Int) {
        bitmap = BitmapFactory.decodeResource(resources, drawable)
        invalidate()
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)

        /**
         * 设置View的离屏缓冲。在绘图的时候新建一个“层”,所有的操作都在该层而不会影响该层以外的图像
         * 必须设置,否则设置的PorterDuffXfermode会无效
         */
        val sc = canvas!!.saveLayer(0f, 0f, width.toFloat(), height.toFloat(), mPaint, Canvas.ALL_SAVE_FLAG)

        halfHeight = height / 2

        // 先画目标图,再画原图
        if (bitmap != null) {
            
            if (innerRadius > 0 && innerRadius < halfHeight) {
                canvas?.drawBitmap(getCircleBitmap(innerRadius), 0f, 0f, mPaint)
                mPaint.xfermode = xfermode
            }

            canvas?.drawBitmap(bitmap, 0f, 0f, mPaint)
            mPaint.xfermode = null
        }

        /**
         * 还原画布,与canvas.saveLayer配套使用
         */
        canvas.restoreToCount(sc)
    }

    private fun getCircleBitmap(radius: Int): Bitmap {
        val bm = Bitmap.createBitmap(height, height, Bitmap.Config.ARGB_8888)
        val c = Canvas(bm)
        val p = Paint(Paint.ANTI_ALIAS_FLAG)
        p.color = Color.WHITE
        c.drawCircle(halfHeight.toFloat(), halfHeight.toFloat(), radius.toFloat(), p)
        return bm
    }


    /**
     * 开启动画
     */
    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 在Drawable中的应用

主要是setColorFilter(@ColorInt int color, @NonNull PorterDuff.Mode mode)方法,该方法给图片添加一个颜色图层,实现混合效果。
如:图片变暗

BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), coverImageBitmap);
 //进行对应像素的比较,取较暗值,如果色值相同则进行混合
bitmapDrawable.mutate().setColorFilter(ContextCompat.getColor(mContext, R.color.me_forground), PorterDuff.Mode.DARKEN);
 iv_head_bg.setBackground(bitmapDrawable);

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