Android(安卓)图片裁剪之三剑式(一)
16lz
2021-01-26
文章目录
- 引言
- 第一式: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
画布绘制的合成模式,也就是两张图片相交后的显示模式
请看下篇。
更多相关文章
- android 显示进度的按钮
- Android学习心得(五)——GOOGLE MAP
- ImageView显示图片时,上下出现多余空白。
- Android文本输入框EditText方法说明和属性
- ✿Android(安卓)3.1 --- 久违的 USB、mtp、rtp
- Android(安卓)Studio 3.5.2版本中Kotlin代码不显示代码补齐自动
- Android(安卓)Fragment动态创建时replace()和add()方法的区别
- android中Canvas使用drawBitmap绘制图片
- Android(安卓)studio git 中文乱码 不显示中文