Kotlin實戰(zhàn)練習(xí)——自定義圓形圖片三種實現(xiàn)方式

Kotlin實戰(zhàn)練習(xí)——自定義圓形圖片三種實現(xiàn)方式

前言

如今Kotlin越來越重要,本人也開始了Kotlin的學(xué)習(xí)。為了檢測學(xué)習(xí)效果,加深學(xué)習(xí)印象,同時回顧一下以前的一些知識點,決定從寫一個自定義圓形圖片開始入手。圓形圖片寫法有很多,這里介紹最主要的三種方式,如果能掌握這三種方式,那么其他的自定義控件應(yīng)該也都很好實現(xiàn)了。

1. XferMode方式

知識點

1. 關(guān)于XferMode

XferMode是一種圖片重疊時的處理方式,主要是在圖片重疊時,由用戶來選取如何對重疊的圖片進(jìn)行處理,XferMode的模式有如下幾種:

XferMode

目前,XferMode有三個子類:AvoidXfermode, PixelXorXfermodePorterDuffXfermode,其中前兩個已過時,現(xiàn)在一般用PorterDuffXfermode。

2. Canvas和Bitmap的關(guān)系

Canvas類似于畫板,Bitmap類似于畫布,最終我們呈現(xiàn)給別人看的東西,是畫布上的內(nèi)容,也就是Bitmap,但是我們要在Bitmap上畫東西,則需要畫板的支撐。

3. drawable

drawable主要作用是提供一個可繪制區(qū)域和draw函數(shù)用來繪制需要的圖像、顏色等,在ImageView內(nèi),我們只需要關(guān)心它的兩個方法:

  • setBounds: 可繪制區(qū)域;
  • draw: 將內(nèi)容繪制到畫板上的畫布中。

實現(xiàn)思路

  1. 先獲取一個圓形的Bitmap對象;

  2. 將這個圓形的Bitmap對象覆蓋在圖片上;

  3. 通過XferMode中的DST_IN模式,選取圓形Bitmap和圖片的相交部分。

實現(xiàn)代碼

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的子類。初始化的時候,需要對它設(shè)置TileMode,一共有三種模式:

  • CLAMP: 拉伸。若圖片沒有填充滿布局,會將x,y軸的最后一個像素拉伸到布局邊緣;
  • REPEAT: 重復(fù)。若圖片沒有填充滿布局,會將圖片進(jìn)行x/y軸的重復(fù);
  • MIRROR: 鏡像。若圖片沒有填充滿布局,則會將圖片進(jìn)行鏡像復(fù)制。

BitmapShader是給畫筆的渲染,它是從控件左上角開始渲染的,而不是從繪畫開始的地方進(jìn)行渲染的!

關(guān)于ShaderBitmapShader的更多介紹,請自行百度。

2. Matrix

Matrix有多種用法,我們這里僅僅是用它的縮放功能,目的是讓圖片的大小大于或等于控件的大小,以免圖片填充不滿產(chǎn)生拉伸效果!

實現(xiàn)思路

原理: 給畫筆設(shè)置一個BitmapShader,就好像畫布上已經(jīng)存在一張隱藏的Bitmap,畫筆畫哪個地方,哪個地方的畫面就顯示出來。

  1. drawable轉(zhuǎn)換成Bitmap

  2. 新建BitmapShader,將Bitmap設(shè)置進(jìn)去,并通過MatrixBitmap縮放到和控件大小一樣(不能小于控件大?。悦猱a(chǎn)生拉伸效果;

  3. 畫圓形圖片,將"隱藏的"圖片顯示出來。

實現(xiàn)代碼

1.初始化

同方法一

2.將drawable轉(zhuǎn)換成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.設(shè)置BitmapShader
if(mBitmapShader == null) {
    mBitmapShader = BitmapShader(mShaderBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
    // 設(shè)置縮放大小,保證bitmap的大小要大于控件的大?。ㄒ悦猱a(chǎn)生拉伸效果!)
    var bitmapWidth = Math.min(mShaderBitmap!!.width, mShaderBitmap!!.height)
    var scale = mWidth * 1.0f / bitmapWidth
    mMatrix = Matrix()
    mMatrix?.setScale(scale, scale)
    mBitmapShader?.setLocalMatrix(mMatrix);
}
4.給畫筆設(shè)置BitmapShader
mPaint?.shader = mBitmapShader;
5.將圓形圖片畫在畫板上
canvas?.drawCircle(mWidth / 2.0f, mWidth / 2.0f, mWidth / 2.0f, mPaint)

3.ClipPath方式

知識點

1.canvas.clipPath()

裁剪功能,設(shè)置canvas的繪畫區(qū)域。
重要: ClipPath()方法不支持硬件加速?。。?/strong>

實現(xiàn)思路

  1. 先將drawable轉(zhuǎn)換成bitmap;

  2. 給畫板設(shè)置繪圖區(qū)域為圓形;

  3. 繪畫

實現(xiàn)代碼

1. 先將drawable轉(zhuǎn)換成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. 給畫板設(shè)置繪圖區(qū)域為圓形
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()
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容