Android自定義View(13) 《子線程中更新UI》

概述

首先說明啊旭愧,不是標(biāo)題黨啊椭住,在子線程中更新UI的方式有很多中

  • 通過Looper在主線程中的Handler更新
  • 通過runUIThread
  • 通過view的post
    但是上述幾種方式最終都是通過主線程來繪制的,所以今天要說的是利用SurfaceView在子線程中來更新界面

自定義一個SurfaceView

首先我們創(chuàng)建一個自定義SurfaceView场勤,復(fù)寫其onDraw()方法们拙,繪制一個圓

package com.tx.txcustomview.menu

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.AttributeSet
import android.view.SurfaceView

/**
 * create by xu.tian
 * @date 2021/9/11
 */
class SubSurfaceView(context: Context?, attrs: AttributeSet?) : SurfaceView(context, attrs) {
    var paint = Paint()
    init {
        paint.color = Color.YELLOW
        paint.style = Paint.Style.FILL
    }
    var centerX = 0f
    var centerY = 0f
    var radius = 0f
    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        canvas?.drawCircle(centerX,centerY,radius,paint)
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        centerX = (w/2).toFloat()
        centerY = (h/2).toFloat()
        radius = centerX/10*9
    }
}

代碼很簡單锡移,我們看看運(yùn)行效果


device-2021-09-11-192148.png

中間的黑色矩形就是我們的自定義SurfaceView,很明顯我們復(fù)寫onDraw方法并沒有效果,那么是為啥呢崔赌?這個要從一個函數(shù)說起了

  public void setWillNotDraw(boolean willNotDraw) {
        setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);
    }

這個函數(shù)的功能簡單來說就是判斷要不要在這個View中調(diào)用onDraw()方法繪制內(nèi)容意蛀,因為一些ViewGroup是不需要繪制本身的,這樣就可以提高代碼的執(zhí)行效率峰鄙,而SurfaceView同樣被這樣設(shè)置了浸间,所以谷歌官方也是不建議通過復(fù)寫SurfaceView的onDraw()這樣的方式來繪制的

正確姿勢

    fun startDraw(){
        var thread = Thread(Runnable {
            var canvas = holder.lockCanvas()
            if (canvas!=null){
                canvas?.drawCircle(centerX,centerY,radius,paint)
                holder.unlockCanvasAndPost(canvas)
            }else{
                Log.d(tag,"canvas is null")
            }
        })
       thread.start()
    }

我們這里通過surfaceView的holder來獲取到Canvas對象,然后進(jìn)行繪制,吟榴,這里我們需要注意的是魁蒜,當(dāng)其他線程使用畫布或者畫布未被創(chuàng)建完成時,這里的Canvs對象是可能為空的吩翻,所以使用的時候需要非常小心
再看執(zhí)行效果


device-2021-09-11-201206.png

那么這種方式是否可以在主線程種使用呢兜看,那肯定是可以的了,主線程也是可以通過這種方式來更新SurfaceView的狭瞎。

更新SurfaceView局部內(nèi)容

    fun startDraw(){
        var thread = Thread(Runnable {
            var canvas = holder.lockCanvas()
            if (canvas!=null){
                canvas?.drawCircle(centerX,centerY,radius,paint)
                holder.unlockCanvasAndPost(canvas)
            }else{
                Log.d(tag,"canvas is null")
            }
            Thread.sleep(500)

            var rectCanvas = holder.lockCanvas(Rect(width/4,height/4,width/4*3,height/4*3))
            if (rectCanvas!=null){
                var rect = rectCanvas.clipBounds
                paint.color = Color.GREEN
                rectCanvas.drawCircle(rect.centerX().toFloat(),
                    rect.centerY().toFloat(), (rect.centerX()/2).toFloat(),paint)
                holder.unlockCanvasAndPost(rectCanvas)
            }else{
                Log.d(tag,"canvas is null")
            }


        })
       thread.start()
    }

運(yùn)行效果

subSurfaceView.gif

我們先用lockCanvas()獲取到一個完整Canvas對象然后進(jìn)行繪制细移,然后將線程休眠500ms再取Canvas其中的一部分來進(jìn)行繪制
這里能夠很明顯的看到我們局部更新是成功的

Surface,SurfaceView,SurfaceHolder的關(guān)系

這里的這三者和MVC模式中三者的對應(yīng)關(guān)系一樣

  • Surface-Model 保存繪制過程中的相關(guān)數(shù)據(jù)
  • SurfaceView - View 負(fù)責(zé)繪制和顯示
  • SurfaceHolder - Controller 負(fù)責(zé)具體的頁面實(shí)現(xiàn)

SurfaceView監(jiān)聽聲明周期

      holder.addCallback(object : SurfaceHolder.Callback{
            override fun surfaceCreated(holder: SurfaceHolder?) {
                TODO("Not yet implemented")
            }
            override fun surfaceChanged(
                holder: SurfaceHolder?,
                format: Int,
                width: Int,
                height: Int
            ) {
                TODO("Not yet implemented")
            }

            override fun surfaceDestroyed(holder: SurfaceHolder?) {
                TODO("Not yet implemented")
            }

        })

SurfaceView雙緩沖技術(shù)

Surface使用了一種叫做雙緩沖的技術(shù)來渲染程序,這套技術(shù)需要兩套圖形緩沖區(qū)熊锭,一個叫前端緩沖區(qū)弧轧,一個叫后端緩沖區(qū)雪侥,我們通過lockCanvas方式拿到的均為后端緩沖區(qū),當(dāng)我們調(diào)用unlockCanvas相關(guān)函數(shù)時就是將前緩沖區(qū)與后緩沖區(qū)交換精绎。
那么是不是前緩沖區(qū)和后緩沖區(qū)就是各自擁有一塊畫布呢速缨?
那當(dāng)然不是了,前緩沖區(qū)肯定是一塊代乃,因為最終顯示的是一個固定的內(nèi)容旬牲,但后緩沖區(qū)會根據(jù)實(shí)際情況來創(chuàng)建畫布數(shù)量,以應(yīng)對多個線程同時對SurfaceView進(jìn)行操作搁吓。

SurfaceView和普通View的使用場景

  • 當(dāng)需要與用戶發(fā)生頻繁交互原茅,或者需要實(shí)時給予用戶反饋時使用普通View菇民,比如按鈕雾家,滑動控件
  • 當(dāng)需要屏幕主動頻繁刷新且不需要與用戶交互時我們就可以使用SurfaceView,比如Camera預(yù)覽寞埠,Video播放贮预。

總結(jié)

SurfaceView是子線程更新UI的一個常用控件贝室,一般做視頻開發(fā)或者Camera相關(guān)的就比較容易接觸,記得怎么更新SurfaceView界面就可以

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末仿吞,一起剝皮案震驚了整個濱河市滑频,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌唤冈,老刑警劉巖峡迷,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異你虹,居然都是意外死亡绘搞,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進(jìn)店門傅物,熙熙樓的掌柜王于貴愁眉苦臉地迎上來夯辖,“玉大人,你說我怎么就攤上這事董饰≥锕樱” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵卒暂,是天一觀的道長啄栓。 經(jīng)常有香客問我,道長也祠,這世上最難降的妖魔是什么昙楚? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮诈嘿,結(jié)果婚禮上堪旧,老公的妹妹穿的比我還像新娘削葱。我一直安慰自己,他們只是感情好淳梦,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布佩耳。 她就那樣靜靜地躺著,像睡著了一般谭跨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上李滴,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天螃宙,我揣著相機(jī)與錄音,去河邊找鬼所坯。 笑死谆扎,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的芹助。 我是一名探鬼主播堂湖,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼状土!你這毒婦竟也來了无蜂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤蒙谓,失蹤者是張志新(化名)和其女友劉穎斥季,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體累驮,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡酣倾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了谤专。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片躁锡。...
    茶點(diǎn)故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖置侍,靈堂內(nèi)的尸體忽然破棺而出映之,到底是詐尸還是另有隱情,我是刑警寧澤墅垮,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布惕医,位于F島的核電站,受9級特大地震影響算色,放射性物質(zhì)發(fā)生泄漏抬伺。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一灾梦、第九天 我趴在偏房一處隱蔽的房頂上張望峡钓。 院中可真熱鬧妓笙,春花似錦、人聲如沸能岩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拉鹃。三九已至辈赋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間膏燕,已是汗流浹背钥屈。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留坝辫,地道東北人篷就。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像近忙,于是被迫代替她去往敵國和親竭业。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內(nèi)容