美文网首页
Kotlin实战练习——自定义圆形图片三种实现方式

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

作者: TokyoZ | 来源:发表于2019-04-30 15:36 被阅读0次

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

    前言

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

    1. XferMode方式

    知识点

    1. 关于XferMode

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

    XferMode

    目前,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()

    相关文章

      网友评论

          本文标题:Kotlin实战练习——自定义圆形图片三种实现方式

          本文链接:https://www.haomeiwen.com/subject/vnjvnqtx.html