高仿QQ 發(fā)送圖片高亮HaloProgressView

之前看到qq 的圖片發(fā)送效果很酷炫,很吸引人,不過現(xiàn)在這個效果好像沒有了。試了幾次,決定試試實現(xiàn)。大致想了下,實現(xiàn)效果還不錯

需要實現(xiàn)的效果

一圖勝千言,看圖如下:


20181229_112329.gif
怎樣實現(xiàn)呢?

首先從圖中看分兩部分,一部分是進度條帶光暈得效果。第二部分是圓圈擴散到整個圖片,到顯示完整圖片的過程。接下來一步一步跟著代碼分析實現(xiàn)。

1.繪制的范圍包括圖片顯示都在圓角矩形內,所以首先要裁剪canvas到圓角矩形。

        val path = Path()
        canvas.save()
        path.addRoundRect(RectF(0f, 0f, width.toFloat(), height.toFloat())
                , round, round, Path.Direction.CW)
        canvas.clipPath(path)

先保存畫布,save()到最后要canvas.restore().因為顯示圖片,可以有兩種選擇,第一種:自己繪制圖片,通過drawable得方式。第二種:繼承ImageView 同時還可以獲得ImageView提供的各種屬性,scaleType之類。本質上ImageView也是通Drawable實現(xiàn)。IamgeView還幫我們處理了測量的狂傲,所以有什么理由不選擇繼承呢。然后繪制圖片只有簡單一行代碼,再裁剪畫布之后:

        super.onDraw(canvas)

2.繪制背景
可以看到效果圖,圖片在黑色半透明的下方。并且在最后顯示出來。著一點都是跟canvas繪制背景相關的。不多說,先設置畫筆。

    private var paint: Paint = Paint()
    paint.isAntiAlias = true
    paint.color = getColor(R.color.bantouming)

背景怎么繪制,直接通過canvas.drawPaint方法即可實現(xiàn)。把paint的顏色繪制到整個畫布。并且再圖片后邊繪制,所以在上方。

                canvas.drawPaint(paint)

3.繪制進度
這里根據(jù)每一階段狀態(tài)的不同,通過三個狀態(tài)值區(qū)分:

    companion object {
        private const val READY = 1
        private const val PROGRESS = 2
        private const val FINISH = 3
    }

為了方便的繪制,并且整個view是對稱的。所以坐標點移動到view中心,非常有利于實現(xiàn)。

     canvas.save()
        canvas.translate(width / 2f, height / 2f)

當然最后別忘了canvas.restore().
在中間都好說了。先看百分比的實現(xiàn)。主要是drawText()的x,y比較不好掌握,不過搞明白基線之類的,就沒問題了。

先看百分比的paint

    private val textPaint by lazy {
        Paint().apply {
            isAntiAlias = true
            style = Paint.Style.STROKE
            textSize = dp2px(16f).toFloat()
            color = getColor(R.color.main_gray)
        }
    }

接下來繪制,這行代碼可能比較長。需要優(yōu)化,到這里就先別吐槽。有點偷懶。
可以看到根據(jù)文字的寬,高。來繪制的。高這里需要額外注意

textPaint.textHeight().div(2) - textPaint.descent()

需要減去textPiat.descent(),如果不減繪制會偏下。

  val text = "${progress}%"
                canvas.drawText(text, 0 - textPaint.measureText(text).div(2), textPaint.textHeight().div(2) - textPaint.descent(), textPaint)

4.繪制光暈
這算是實現(xiàn)比較疑難的地方。要注意3個地方。1.光暈的實現(xiàn) 2.呼吸效果 3.PorterDuffXmode 使用。

先看呼吸效果如何實現(xiàn)。可能簡單的想到的是通過圓環(huán)實現(xiàn)。但這樣挺麻煩的,如果通過兩個圓疊加,并設置paint.xfermode(PorterDuff.Mode.DST_OUT),可實現(xiàn)把內部圓裁剪掉。關于怎么使用,請看之前的關于xfermode的文章。光暈的實現(xiàn)需要依賴shader,這里通過RadilGradient 實現(xiàn)。具體用法也可看之前文章。
設置shader

     paint.setShader(RadialGradient(0f, 0f, outRadius
                        , intArrayOf(Color.TRANSPARENT, Color.WHITE, Color.WHITE, Color.TRANSPARENT)
                        , floatArrayOf(0.1f, 0.4f, 0.8f, 1f), Shader.TileMode.CLAMP))

接下來的呼吸效果通過動畫設置大圓半徑的變化來實現(xiàn)。

                canvas.drawCircle(0f, 0f, innRaduus + (outRadius - innRaduus) * animatorValue, paint)

完整代碼如下

             canvas.drawCircle(0f, 0f, innRaduus + (outRadius - innRaduus) * animatorValue, paint)
                paint.setXfermode(PorterDuffXfermode(PorterDuff.Mode.DST_OUT))
                paint.setShader(null)
                paint.color = Color.WHITE
                canvas.drawCircle(0f, 0f, innRaduus, paint)
                paint.setXfermode(null)

如果僅僅是這樣那么繪制出來中間喝一個黑洞。因為背景是透明的。所以這時候在繪制之前需要canvas.savlayer。如下

                val sc = canvas.saveLayer(-outRadius, -outRadius, outRadius, outRadius, paint, Canvas.ALL_SAVE_FLAG)

保存的范圍包括大圓小圓
最后要restore

                canvas.restoreToCount(sc)

再加上animatorValue 從0到1的動畫就完成PROGRES階段的動畫了。

5.繪制FINISH動畫,揭露圖片效果
同樣這里也需要使用PorterDuff.Mode.DST_OUT,不過這里需要的是對整個圓角畫布范圍進行操作。DST 是canvas.drawPaint繪制的背景。SRC 是一整個圓角矩形對角線的一半為最大半徑,從PROGRES 狀態(tài)大圓的半徑的范圍,到最大范圍的動畫變化。如下:

   val sc = canvas.saveLayer(-width.div(2f), -height.div(2f), width.div(2f), height.div(2f), paint, Canvas.ALL_SAVE_FLAG)
                canvas.drawPaint(paint)
                val maxRadius = Math.sqrt(Math.pow(width.toDouble(), 2.0) + Math.pow(height.toDouble(), 2.0)).div(2)
                paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_OUT)
                paint.color = Color.WHITE
                canvas.drawCircle(0f, 0f, (outRadius + (maxRadius - outRadius) * finishAnimValue).toFloat(), paint)
                paint.xfermode = null
                canvas.restoreToCount(sc)

6.動畫的使用,與交替。這一點比較簡單的ValueAnimator的使用,設置屬性,Listener即可。有興趣可參考源碼
我是源碼

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容