實戰酷斃了的自定義View(一)

前言

可能有點狂了,阿里的offer還沒拿到手就已經拒了抖音的offer了,想一想音視頻可能得先放一段落,只能間歇性來更新了,由於最近確實對這方面有點迷茫,交叉面淘系大佬給我列出的知識體系確實過於龐大,我如今仍是得先學着前輩們先把Java層作好,再進階也不遲。先把精力主戰JavaFlutter了。不過以後項目可能儘量會用Kotlin來作,畢竟這是阿里如今的內部可能主流語言了,畢竟再雲棲大會上登場就是Kotlin而不是Javagit

Gtihub傳送門

Paint和Canvas的基礎使用

在以前的文章Android自定義View,你摸的透透的了?之中,咱們知道過了View的繪製流程、過分繪製啊之類的東西。這是基礎,可是如今說的PaintCanvas也是一種很是重要的基礎了,他們用通俗的話來講,就是筆和畫布,而這麼多的繪製哪個任務不是經過他們來完成的呢?github

以前咱們講過音視頻開發,那視頻的概念,其實咱們應該也已經有所瞭解了,就是一幀幀的圖片嘛,繪製的速度超過人眼,那麼就可以演變成視頻了。 面試

上面給出一個貝塞爾曲線的繪製過程,固然這個我已經用代碼實現了。有興趣的讀者能夠直接的去源碼中的PathView類中進行查看。canvas

其實我就是模擬了100次而已,可是你就會感受整個圖片他在動,這其實也是動畫實現的一種原理了。ide

這個貝塞爾曲線的模擬,其實能夠分爲幾個部分,兩個相同的部分就是貝塞爾曲線他一條線和一個點的模擬,還有就是中間點,也就是貝塞爾曲線的位置變換模擬了。函數

那在這裏咱們須要注意的地方就來了,如何獲取當前點的位置,顯然咱們只會使用PaintCanvas是遠遠不夠的,由於,咱們的小點顯然須要經過位置變換計算得來的。這裏咱們就要引入咱們一個類Path了。佈局

Path

Path,從字面意思咱們就知道,這是一個路徑的意思了。post

先來觀摩一下他的的函數有什麼,和Canvas可能不同的地方就是,從draw變到了add,可是這樣的區別,就能讓Canvas繪製更加複雜的圖形了,由於draw的對象都是已經封裝好的圓形、長方形啊等等,而經過add你就能繪製出各類奇形怪狀的形狀了,而貝塞爾曲線就是他們其中的一份子。學習

PathMeasure

什麼是PathMeasure呢?其實你關注上方,咱們講過了Path,可是終究仍是沒講到如何去進行一個位置的計算。而其實這個任務,就是交給咱們的這個PathMeasure來交接完成的。動畫

fun init(){
    pathMeasure = PathMeasure(path, false)
        pathMeasure?.length.let {
            pathLength = it
            mStep = it?.div(INVALIDATE_TIMES)
        }
}

override fun onDraw(canvas: Canvas?) {
    super.onDraw(canvas)
    if (pathLength!! >= mDistance!!) {
        mDistance?.let { pathMeasure?.getPosTan(it, mPos, mTan) }
        mDistance = mStep?.let { mDistance?.plus(it) } 
        canvas?.drawPoint(mPos?.get(0)!!, mPos?.get(1)!!, paintPoint!!)
        invalidate()
    }
}
複製代碼

具體代碼仍是須要看我已經完成的PathView,這裏作一個簡單的介紹,其實這個PathMeasure經過Path來計算長度,而後在經過咱們傳入的Distance來返回當前的PostionTan值,會有讀者問了Tan有什麼用,其實通常比較多數的用法就是就是一個角度的調整,若是使用一個點來顯示,確實沒什麼效果,可是若是是一個Bitmap來現實的,沒有Tan值,那麼咱們的圖片就不會取轉頭了。

而後經過獲取的PostionTan值來重繪,咱們就能夠完成咱們的貝塞爾曲線繪製了

刮刮卡效果實戰

以前已經知道了咱們的這個圖片如何去進行繪製,那這個時候咱們就繼續深化了。

首先第一個問題,什麼是刮刮卡?

小二上圖!!!

讀者: cool!!想知道想知道,這個咋實現呀。

小易: 我如今還不想告訴你,嘿嘿。

第二個問題,他有什麼組成部分?

你可能會說,蒙版和底層圖片唄。可是這個答案是,也不是。爲何呢?若是隻有蒙版和底層圖片,其實這個不可以徹底實現的,你可能只能實現到下面的步驟。

一個只能看,不能刮的刮刮卡,哈哈哈哈哈哈哈哈!!!

既然咱們已經說了,這兩個不夠,那確定還有其餘組成成分是須要咱們去進行考慮的了。其實他就是一支筆,這個刮的部分其實他是經過一支筆來完成的。

這裏咱們須要補充幾個知識點了。

離屏繪製

經過寫法以下:

override fun onDraw(canvas: Canvas?) {
    super.onDraw(canvas)
    val layerId = canvas?.saveLayer(
            null, null
        )
    canvas?.let { canvasIt ->
        paint?.let {paintIt->
            rectBitmap?.let { canvas.drawBitmap(it,0f,0f, paintIt) }
            paintIt.xfermode = xfermode
            circleBitmap?.let { canvas.drawBitmap(it, 50f, 50f,paintIt) }
            paintIt.xfermode = null
        }
    }
    layerId?.let { canvas.restoreToCount(it) }
}
複製代碼

經過一個短時的離屏緩衝,來完成兩個圖層的複合來實現。而這個複合就是經過一個layerId來完成斷定誰和誰是一家人。

Xfermode

這裏又來一個知識點了!!!複合是怎麼實現的???

下面給出一張圖片來解釋,這是Android內置的Xfermode,也就是複合模式是如何的,理解圖片的意思便可,也不用專門去學怎麼算的,歸根結底,就是對RGB某一個色塊的突出或者隱藏。

好了,知道了這幾個知識點,咱們迴歸正題,刮刮卡怎麼實現的問題?咱們知道了複合,知道了離屏繪製,怎麼用呢?

其實最後要討論的就是蒙版是什麼?底層圖片作什麼?

底層圖片天然是給咱們看的,咱們總不能手畫一下,而後被咱們抹掉吧,那這個圖層顯然是不能被咱們的覆蓋掉的。而蒙版呢,咱們須要經過離屏繪製,把圖片和咱們走過的路徑進行重疊消除,來完成看到圖片的效果。那這個效果應該是那種模式來完成呢?咱們看看上面給出的圖片,那個最合適。

刮的過程就是咱們的Dst和蒙版就是咱們的Src,這個是應該就是XOR這個模式是最符合咱們的要求的,咱們繪製過的和Src重疊的地方所有消去了。

可是看過了源碼的讀者估計會問一個問題,爲何我要建立一個DBitmap來完成這個任務?其實不實現也能夠的,可是你會出現下圖的狀況了,刮刮卡下方的圖層一片烏漆麻黑,其實這個DBitmap起到的是一個限制的做用了,給了Paint的一個做用範圍,那麼實現的就更加舒服了,固然若是你限制了View在整合佈局中的大小,正好佔滿也是能夠解決這個問題的,可是並不推薦。

最後就是一個簡單的也比較重要的點了,這是Android的事件分發機制都會問到的一個點,咱們應該重寫什麼方法,來完成繪製路徑的添加。建議仍是在OnTouch中完成,由於OnTouch的響應實際上是先於OnTouchEvent的。

以上就是個人學習成果,若是有什麼我沒有思考到的地方或是文章內存在錯誤,歡迎與我分享。


相關文章推薦:

【從零衝擊音視頻開發】FFmpeg的介紹和基本使用

【從零衝擊音視頻開發】移動環境搭建

面試官問我:「泛型擦除是什麼,會帶來什麼問題?」

相關文章
相關標籤/搜索