Android 自定義View系列文章
- Android 自定義View練手Demo(一)實現(xiàn)圓角遮罩效果
- Android 自定義View練手Demo(二)實現(xiàn)圓形頭像效果
- Android 自定義View練手Demo(三)實現(xiàn)微信拍一拍的動畫效果
Android自定義View實現(xiàn)圓角遮罩效果
一圖勝千言,有一個遮罩就會凸顯出重點區(qū)域
本文通過兩種方式來實現(xiàn)這種效果睬棚,來達到自定義View練手的效果
此效果的用途
- 在裁剪圖片伊者,確定裁剪范圍
- 在APP中引導用戶,突顯某個區(qū)域
這是一個麻雀雖小五臟俱全的小Demo了,非常適合練手田弥。
1.引言
通過本文可以學習到
- Canvas和Paint 的常用且實用的 API
- Xfermode的使用
- View級別的離屏緩沖的開啟方式
- Canvas的離屏緩沖和View的離屏緩沖的區(qū)別
- 如何給自定義View設置自定義屬性的使用
2.第一種實現(xiàn)方式
class RoundRectCoverView(context: Context, attrs: AttributeSet) : View(context, attrs) {
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
private var mPadding = 40.dp //間距
private var mRoundCorner = 10.dp //圓角矩形的角度
private var mCoverColor = "#99000000".toColorInt()//遮罩的顏色
private val porterDuffXfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_OUT)
init {
//開啟View級別的離屏緩沖,并關閉硬件加速峡竣,使用軟件繪制
setLayerType(LAYER_TYPE_SOFTWARE, null)
}
override fun onDraw(canvas: Canvas) {
//先畫一個圓角矩形,也就是透明區(qū)域(Destination image)
canvas.drawRoundRect(mPadding, mPadding, width - mPadding, height - mPadding, mRoundCorner, mRoundCorner, paint)
//設置遮罩的顏色
paint.color = mCoverColor
//設置paint的 xfermode 為PorterDuff.Mode.SRC_OUT
paint.xfermode = porterDuffXfermode
//畫遮罩的矩形(Source image)
canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint)
//清空paint 的 xfermode
paint.xfermode = null
}
上面代碼中的注釋已經(jīng)寫的很清楚了现恼,這里說一下 setLayerType(LAYER_TYPE_SOFTWARE, null)
是開啟View級別的離屏緩沖,就是拿出整個View大小的一塊區(qū)域奢驯,這塊區(qū)域是透明的申钩。那么你就可能會好奇,為啥要拿出這么一塊透明的區(qū)域呢瘪阁?因為使用PorterDuff.Mode時候需要用到撒遣。
這里面我們用到的是PorterDuff.Mode.SRC_OUT,正好對應的是Source Out這種模式是我們想要的
- 注意Destination image是第一次畫的圓角矩形罗洗,需要畫在一個透明的View中愉舔,這就需要離屏緩沖
- Source image是畫的帶顏色的遮罩也就是整個View
- PorterDuff.Mode.SRC_OUT,類似于把后畫的和先前畫的重疊的部分伙菜,摳出去扔掉轩缤,結果就是我們要的效果
3.第二種實現(xiàn)方式
class RoundRectCoverView(context: Context, attrs: AttributeSet) : View(context, attrs) {
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
private var mPadding = 40.dp //間距
private var mRoundCorner = 10.dp //圓角矩形的角度
private var mCoverColor = "#99000000".toColorInt()//遮罩的顏色
private val bounds = RectF()
private val porterDuffXfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_OUT)
private val clipPath = Path()
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
//設置離屏緩沖的范圍
bounds.set(0f, 0f, width.toFloat(), height.toFloat())
//設置Clip Path的矩形區(qū)域
clipPath.addRoundRect(mPadding, mPadding, width - mPadding, height - mPadding, mRoundCorner, mRoundCorner, Path.Direction.CW)
}
override fun onDraw(canvas: Canvas) {
//Canvas的離屏緩沖
val count = canvas.saveLayer(bounds, paint)
//KTX的擴展函數(shù)相當于對Canvas的 save 和 restore 操作
canvas.withSave {
//畫遮罩的顏色
canvas.drawColor(mCoverColor)
//按Path來裁切
canvas.clipPath(clipPath)
//畫鏤空的范圍
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.SRC)
}
//把離屏緩沖的內(nèi)容,繪制到View上去
canvas.restoreToCount(count)
}
}
解釋一下,在onSizeChanged方法中贩绕,我們設置了離屏緩沖的范圍火的,注意這個范圍夠用就行,太大了耗費性能淑倾。同時設置了Clip Path裁剪的矩形區(qū)域馏鹤。
onDraw方法中
首先開啟離屏緩沖(注意這里開啟的是Canvas的離屏緩沖)這范圍就是bounds的范圍
畫出完整的遮罩的顏色
然后把Canvas裁切出一個圓角矩形
然后在話一個透明的顏色,模式指定為PorterDuff.Mode.SRC
把離屏緩沖的內(nèi)容,繪制到View上去
注意:記得開離屏緩沖娇哆,否則結果可能不是你想要的
4.設置自定義屬性
4.1首先在values文件夾下湃累,創(chuàng)建一個attrs.xml的文件(當然你也可以叫別的名字,但這個是規(guī)范)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="RoundRectCoverView">
<attr name="roundCorner" format="dimension" />
<attr name="roundPadding" format="dimension" />
<attr name="roundCoverColor" format="color" />
</declare-styleable>
</resources>
聲明你想再xml中能設置的屬性碍讨,比如我這里聲明了治力,圓角的大小,圓角矩形的padding值勃黍,以及遮罩的顏色
4.2在代碼中動態(tài)獲取xml中設置的值
init {
//通過TypeArray 獲取 xml 配置的屬性
val ta = context.obtainStyledAttributes(attrs, R.styleable.RoundRectCoverView)
mPadding = ta.getDimension(R.styleable.RoundRectCoverView_roundPadding, 40.dp)
mRoundCorner = ta.getDimension(R.styleable.RoundRectCoverView_roundCorner, 10.dp)
mCoverColor = ta.getColor(R.styleable.RoundRectCoverView_roundCoverColor, "#99000000".toColorInt())
ta.recycle()
}
- 通過TypeArray獲取宵统,聲明的屬性值
- 賦值給成員變量,下面使用的時候就直接使用了
- 回收TypeArray
4.3在xml代碼直接使用
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="0dp"
android:layout_height="0dp"
android:src="@drawable/captain_america"
android:scaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.jhb.customviewcollection.RoundRectCoverView.RoundRectCoverView
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:roundCorner="10dp"http://角度
app:roundCoverColor="#aa000000"http://遮罩顏色
app:roundPadding="30dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
5.總結
- 離屏緩沖的兩種方式區(qū)別以及開啟方式
- Canvas.saveLayer() 可以做短時的離屏緩沖覆获,Google不建議使用马澈,因為每次繪制都需要拿出一塊來瓢省,耗費性能
- View.setLayerType()是直接把整個
View
都繪制在離屏緩沖中,Google建議這樣做痊班。setLayerType(LAYER_TYPE_HARDWARE)
是使用 GPU 來緩沖勤婚,setLayerType(LAYER_TYPE_SOFTWARE)
是直接直接用一個Bitmap
來緩沖。
- PorterDuff.Mode的類型涤伐,已經(jīng)使用蛔六,需要有透明的背景色
- 自定義屬性的方式
這是一個簡單且使用的Demo,很適合練手废亭!
6.源碼地址
7.原文地址
8.參考文章
推薦一下我開源的項目 WanAndroid 客戶端
WanAndroidJetpack 架構圖
- 一個純 Android 學習項目国章,WanAndroid 客戶端。
- 項目采用
MVVM
架構豆村,用Kotlin
語音編寫液兽。 - Android Jetpack 的大量使用包括但不限于
Lifecycle
、LiveData
掌动、ViewModel
邻遏、Databinding
府瞄、Room
、ConstraintLayout
等,未來可能會更多数苫。 - 采用
Retrofit
和Kotlin-Coroutine
協(xié)程進行網(wǎng)絡交互宝与。 - 加載圖片
Glide
主流加載圖片框架镇辉。 - 數(shù)據(jù)存儲主要用到了
Room
和騰訊的MMKV
雕沿。
Kotlin + MVVM + Jetpack + Retrofit + Glide 的綜合運用,是學習 MMVM 架構的不錯的項目妖碉。
此項目本身也是一個專門學習 Android 相關知識的 APP涌庭,歡迎下載體驗!
源碼地址(附帶下載鏈接)
APP 整體概覽
喜歡的點個 Stars欧宜,有問題的請?zhí)?Issues坐榆。