Android 自定義View練手Demo(一)實現(xiàn)圓角遮罩效果

Android 自定義View系列文章

Android自定義View實現(xiàn)圓角遮罩效果

一圖勝千言,有一個遮罩就會凸顯出重點區(qū)域

1-1.jpg

本文通過兩種方式來實現(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

這里面我們用到的是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.源碼地址

RoundRectCoverView.kt

7.原文地址

Android自定義View實現(xiàn)圓角遮罩效果

8.參考文章

hencoder

PorterDuff.Mode

推薦一下我開源的項目 WanAndroid 客戶端

WanAndroidJetpack 架構圖

wanandroid-arch.jpg
  • 一個純 Android 學習項目国章,WanAndroid 客戶端。
  • 項目采用 MVVM 架構豆村,用 Kotlin 語音編寫液兽。
  • Android Jetpack 的大量使用包括但不限于LifecycleLiveData掌动、ViewModel邻遏、Databinding府瞄、RoomConstraintLayout等,未來可能會更多数苫。
  • 采用 RetrofitKotlin-Coroutine 協(xié)程進行網(wǎng)絡交互宝与。
  • 加載圖片 Glide 主流加載圖片框架镇辉。
  • 數(shù)據(jù)存儲主要用到了 Room 和騰訊的 MMKV雕沿。

Kotlin + MVVM + Jetpack + Retrofit + Glide 的綜合運用,是學習 MMVM 架構的不錯的項目妖碉。

此項目本身也是一個專門學習 Android 相關知識的 APP涌庭,歡迎下載體驗!

源碼地址(附帶下載鏈接)

WanAndroidJetpack

APP 整體概覽

wan-gif.gif

喜歡的點個 Stars欧宜,有問題的請?zhí)?Issues坐榆。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市冗茸,隨后出現(xiàn)的幾起案子席镀,更是在濱河造成了極大的恐慌,老刑警劉巖夏漱,帶你破解...
    沈念sama閱讀 212,080評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件豪诲,死亡現(xiàn)場離奇詭異,居然都是意外死亡麻蹋,警方通過查閱死者的電腦和手機跛溉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,422評論 3 385
  • 文/潘曉璐 我一進店門焊切,熙熙樓的掌柜王于貴愁眉苦臉地迎上來扮授,“玉大人芳室,你說我怎么就攤上這事∩膊” “怎么了堪侯?”我有些...
    開封第一講書人閱讀 157,630評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長荔仁。 經(jīng)常有香客問我伍宦,道長,這世上最難降的妖魔是什么乏梁? 我笑而不...
    開封第一講書人閱讀 56,554評論 1 284
  • 正文 為了忘掉前任次洼,我火速辦了婚禮,結果婚禮上遇骑,老公的妹妹穿的比我還像新娘卖毁。我一直安慰自己,他們只是感情好落萎,可當我...
    茶點故事閱讀 65,662評論 6 386
  • 文/花漫 我一把揭開白布亥啦。 她就那樣靜靜地躺著,像睡著了一般练链。 火紅的嫁衣襯著肌膚如雪翔脱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,856評論 1 290
  • 那天媒鼓,我揣著相機與錄音届吁,去河邊找鬼。 笑死绿鸣,一個胖子當著我的面吹牛瓷产,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播枚驻,決...
    沈念sama閱讀 39,014評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼濒旦,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了再登?” 一聲冷哼從身側響起尔邓,我...
    開封第一講書人閱讀 37,752評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎锉矢,沒想到半個月后梯嗽,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,212評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡沽损,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,541評論 2 327
  • 正文 我和宋清朗相戀三年灯节,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,687評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡炎疆,死狀恐怖卡骂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情形入,我是刑警寧澤全跨,帶...
    沈念sama閱讀 34,347評論 4 331
  • 正文 年R本政府宣布,位于F島的核電站亿遂,受9級特大地震影響浓若,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蛇数,卻給世界環(huán)境...
    茶點故事閱讀 39,973評論 3 315
  • 文/蒙蒙 一挪钓、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧耳舅,春花似錦诵原、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,777評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至辑畦,卻和暖如春吗蚌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背纯出。 一陣腳步聲響...
    開封第一講書人閱讀 32,006評論 1 266
  • 我被黑心中介騙來泰國打工蚯妇, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人暂筝。 一個月前我還...
    沈念sama閱讀 46,406評論 2 360
  • 正文 我出身青樓箩言,卻偏偏與公主長得像,于是被迫代替她去往敵國和親焕襟。 傳聞我的和親對象是個殘疾皇子陨收,可洞房花燭夜當晚...
    茶點故事閱讀 43,576評論 2 349