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有三個子類:AvoidXfermode, PixelXorXfermode和PorterDuffXfermode,其中前兩個已過時,現(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)思路
先獲取一個圓形的
Bitmap對象;將這個圓形的
Bitmap對象覆蓋在圖片上;通過
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)于Shader和BitmapShader的更多介紹,請自行百度。
2. Matrix
Matrix有多種用法,我們這里僅僅是用它的縮放功能,目的是讓圖片的大小大于或等于控件的大小,以免圖片填充不滿產(chǎn)生拉伸效果!
實現(xiàn)思路
原理: 給畫筆設(shè)置一個BitmapShader,就好像畫布上已經(jīng)存在一張隱藏的Bitmap,畫筆畫哪個地方,哪個地方的畫面就顯示出來。
將
drawable轉(zhuǎn)換成Bitmap;新建
BitmapShader,將Bitmap設(shè)置進(jìn)去,并通過Matrix將Bitmap縮放到和控件大小一樣(不能小于控件大?。悦猱a(chǎn)生拉伸效果;畫圓形圖片,將"隱藏的"圖片顯示出來。
實現(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)思路
先將
drawable轉(zhuǎn)換成bitmap;給畫板設(shè)置繪圖區(qū)域為圓形;
繪畫
實現(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()