Kotlin实战练习——自定义圆形图片三种实现方式

Kotlin实战练习——自定义圆形图片三种实现方式

前言

如今Kotlin越来越重要,本人也开始了Kotlin的学习。为了检测学习效果,加深学习印象,同时回顾一下以前的一些知识点,决定从写一个自定义圆形图片开始入手。圆形图片写法有很多,这里介绍最主要的三种方式,如果能掌握这三种方式,那么其他的自定义控件应该也都很好实现了。

1. XferMode方式

知识点

1. 关于XferMode

XferMode是一种图片重叠时的处理方式,主要是在图片重叠时,由用户来选取如何对重叠的图片进行处理,XferMode的模式有如下几种:

Kotlin实战练习——自定义圆形图片三种实现方式_第1张图片

目前,XferMode有三个子类:AvoidXfermode, PixelXorXfermodePorterDuffXfermode,其中前两个已过时,现在一般用PorterDuffXfermode

2. Canvas和Bitmap的关系

Canvas类似于画板,Bitmap类似于画布,最终我们呈现给别人看的东西,是画布上的内容,也就是Bitmap,但是我们要在Bitmap上画东西,则需要画板的支撑。

3. drawable

drawable主要作用是提供一个可绘制区域和draw函数用来绘制需要的图像、颜色等,在ImageView内,我们只需要关心它的两个方法:

  • setBounds: 可绘制区域;
  • draw: 将内容绘制到画板上的画布中。

实现思路

  1. 先获取一个圆形的Bitmap对象;

  2. 将这个圆形的Bitmap对象覆盖在图片上;

  3. 通过XferMode中的DST_IN模式,选取圆形Bitmap和图片的相交部分。

实现代码

1.初始化画笔、圆形图片半径和XferMode
private var mPaint: Paint? = null
private var mWidth: Int = 0
private var mCircleBitmap: Bitmap? = null
private var xferMode : PorterDuffXfermode? = null

constructor(context: Context?) : this(context, null)
constructor(context: Context?, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int): super(context, attrs, defStyleAttr) {
    init(context)
}

fun init(context: Context?) {
    mPaint = Paint(Paint.ANTI_ALIAS_FLAG)
    xferMode = PorterDuffXfermode(PorterDuff.Mode.DST_IN)
}

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    mWidth = if(width > height) height else width
    setMeasuredDimension(mWidth, mWidth)
}
2.获取一个圆形图片
// 1.先获取一个圆形图片
if(mCircleBitmap == null) {
    mCircleBitmap = Bitmap.createBitmap(mWidth, mWidth, Bitmap.Config.ARGB_8888)
    var circleCanvas = Canvas(mCircleBitmap)
    mPaint?.reset()
    mPaint?.style = Paint.Style.FILL
    circleCanvas.drawCircle(mWidth/2f, mWidth/2f, mWidth/2f, mPaint)
}
3.将圆形Bitmap覆盖到图片上
// 2.将圆形图片和源图片相交,截取相交部分
var drawableHeight = drawable?.intrinsicHeight ?: 0
var drawableWidth = drawable?.intrinsicWidth ?: 0
var drawableBitmap = Bitmap.createBitmap(drawableWidth, drawableHeight, Bitmap.Config.ARGB_8888)
var drawableCanvas = Canvas(drawableBitmap)
drawable?.setBounds(0, 0, mWidth, maxHeight)
drawable?.draw(drawableCanvas)

mPaint?.reset()
mPaint?.xfermode = xferMode
drawableCanvas.drawBitmap(mCircleBitmap, 0f, 0f, mPaint)
mPaint?.xfermode = null
4.将合成后的圆形图片显示到控件中
// 3.将合成后的圆形图片显示到控件中
canvas?.drawBitmap(drawableBitmap, 0f, 0f, mPaint)

2. BitmapShader方式

知识点

1. BitmapShader介绍

bitmapshader是用来给Paint施加一个渲染效果,它是shader的子类。初始化的时候,需要对它设置TileMode,一共有三种模式:

  • CLAMP: 拉伸。若图片没有填充满布局,会将x,y轴的最后一个像素拉伸到布局边缘;
  • REPEAT: 重复。若图片没有填充满布局,会将图片进行x/y轴的重复;
  • MIRROR: 镜像。若图片没有填充满布局,则会将图片进行镜像复制。

BitmapShader是给画笔的渲染,它是从控件左上角开始渲染的,而不是从绘画开始的地方进行渲染的!

关于ShaderBitmapShader的更多介绍,请自行百度。

2. Matrix

Matrix有多种用法,我们这里仅仅是用它的缩放功能,目的是让图片的大小大于或等于控件的大小,以免图片填充不满产生拉伸效果!

实现思路

原理: 给画笔设置一个BitmapShader,就好像画布上已经存在一张隐藏的Bitmap,画笔画哪个地方,哪个地方的画面就显示出来。

  1. drawable转换成Bitmap

  2. 新建BitmapShader,将Bitmap设置进去,并通过MatrixBitmap缩放到和控件大小一样(不能小于控件大小),以免产生拉伸效果;

  3. 画圆形图片,将"隐藏的"图片显示出来。

实现代码

1.初始化

同方法一

2.将drawable转换成bitmap
if(mShaderBitmap == null) {
    if(drawable is BitmapDrawable) {
        var d = drawable as? BitmapDrawable
        mShaderBitmap = d?.bitmap
    } else {
        var drawableHeight = drawable.intrinsicHeight
        var drawableWidth = drawable.intrinsicWidth
        mShaderBitmap = Bitmap.createBitmap(drawableWidth, drawableHeight, Bitmap.Config.ARGB_8888)
        var drawableCanvas = Canvas(mShaderBitmap);
        drawable.setBounds(0, 0, drawableWidth, drawableHeight)
        drawable.draw(drawableCanvas);
    }
}
3.设置BitmapShader
if(mBitmapShader == null) {
    mBitmapShader = BitmapShader(mShaderBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
    // 设置缩放大小,保证bitmap的大小要大于控件的大小(以免产生拉伸效果!)
    var bitmapWidth = Math.min(mShaderBitmap!!.width, mShaderBitmap!!.height)
    var scale = mWidth * 1.0f / bitmapWidth
    mMatrix = Matrix()
    mMatrix?.setScale(scale, scale)
    mBitmapShader?.setLocalMatrix(mMatrix);
}
4.给画笔设置BitmapShader
mPaint?.shader = mBitmapShader;
5.将圆形图片画在画板上
canvas?.drawCircle(mWidth / 2.0f, mWidth / 2.0f, mWidth / 2.0f, mPaint)

3.ClipPath方式

知识点

1.canvas.clipPath()

裁剪功能,设置canvas的绘画区域。
重要: ClipPath()方法不支持硬件加速!!!

实现思路

  1. 先将drawable转换成bitmap

  2. 给画板设置绘图区域为圆形;

  3. 绘画

实现代码

1. 先将drawable转换成bitmap
if(mClipPathBitmap == null) {
    if(drawable is BitmapDrawable) {
        var d = drawable as? BitmapDrawable
        mClipPathBitmap = d?.bitmap
    } else {
        var h = drawable.intrinsicHeight
        var w = drawable.intrinsicWidth
        mClipPathBitmap = Bitmap.createBitmap(w,h,Bitmap.Config.ARGB_8888)
        var drawableCanvas = Canvas(mClipPathBitmap);
        drawable.setBounds(0 ,0, mWidth, mWidth)
        drawable.draw(drawableCanvas)
    }
}
2. 给画板设置绘图区域为圆形
var path = Path()
path.addCircle(mWidth / 2.0f, mWidth / 2.0f,mWidth / 2.0f,Path.Direction.CCW)
3.绘图
canvas?.save()
canvas?.clipPath(path)
canvas?.drawBitmap(mClipPathBitmap, 0f, 0f, mPaint);
canvas?.restore()

你可能感兴趣的:(Android)