文章目录
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 饱和相加,对图像饱和度进行相加
网上效果图:
我测试了常见的几种:
3.PorterDuffXfermode 使用场景
圆形头像
主要采用了SRC_IN模式来实现,图标图片是圆形bitmap,源头像是具体图片,先看效果:
主要代码:
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);