项目开发中显示头像大多都是圆形,以前使用第三方框架RoundedImageView 实现圆形头像,后来glide支持裁剪后,基本都使用它来实现图片的圆形、圆角。前阵子产品提了个需求,做一个“缩放”的动画,然而却是从圆形逐渐放大,放一张效果图:
这就需要自定义去实现了。那么在Android系统,又是如何实现图片裁剪的呢?本文梳理了三招,请看:
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.效果图:
1.着色器的模式有三种,是图片长宽不足时的处理方案
2.实现步骤:
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.效果图:
请看下篇。