SurfaceView的一些事

SurfaceView的一些事

1.為什么Android設(shè)計了SurfaceView

View可以滿足大部分需求,但是在某些特殊時候也力不從心。
因為View是通過刷新來重繪視圖,Android系統(tǒng)通過發(fā)出VSYNC信號來進(jìn)行屏幕繪制,刷新間隔時間為16ms。
如果16ms內(nèi)view完成了繪制過程,那么用戶在視覺上不會感到卡頓。
但是在繪制過程中有太多的邏輯需要處理,16ms內(nèi)無法完成繪制的時候,就需要SurfaceView來解決這一問題。

2.View與SurfaceView的區(qū)別
  • View主要適用于主動更新的情況下,SurfaceView適用于被動更新的情況,例如頻繁的刷新
  • View在主線程中對畫面進(jìn)行刷新,SurfaceView通常會通過一個子線程來進(jìn)行頁面刷新
  • View在繪制時沒有使用雙緩沖機(jī)制,SurfaceView在底層實現(xiàn)機(jī)制中實現(xiàn)了雙緩沖機(jī)制
    • 雙緩沖機(jī)制解釋:在一個陽光明媚的日子,你想將水池里的水換掉,而又找不到水管的時候,你就只能用木桶來灌滿水池。當(dāng)木桶被水龍頭注滿的,關(guān)掉水龍頭,走到水池旁邊,將水到進(jìn)去,然后走回到水龍頭旁邊繼續(xù)重復(fù)上述工作,如此往復(fù)直到將水池灌滿。這就類似單緩沖工作過程。當(dāng)你想將木桶里的水倒出的時候,你必須關(guān)掉水龍頭。
    • 現(xiàn)在假設(shè)你用兩個木桶來做上面的工作。你會注滿第一個木桶然后將第二個木桶換到水龍頭下面,這樣,在第二個水桶注滿的時間內(nèi),你就可以將第一個木桶里面的水倒進(jìn)水池里面,當(dāng)你回來的時候,你只需要再將第一個木桶換下第二個注滿水木桶,當(dāng)?shù)谝粋€木桶開始注水的時候你就將第二個木桶里面的水倒進(jìn)水池里面。重復(fù)這個過程直到水池被注滿。很容易看得到用這種技術(shù)注滿水池將會更快,同時也節(jié)省了很多等待木桶被注滿的時間,而這段時間里你什么也做不了,而水龍頭也就不用等待從木桶被注滿到你回來的這段時間了。
    • 當(dāng)你雇傭另外一個人來搬運(yùn)一個被注滿的木桶時,這就有點(diǎn)類似于三個緩沖區(qū)的工作原理。如果將搬運(yùn)木桶的的時間很長,你可以用更多的木桶,雇傭更多的人,這樣水龍頭就會一直開著注滿木桶了。
    • 在計算機(jī)圖形學(xué)中,雙緩沖是一種畫圖技術(shù),使用這種技術(shù)可以使得畫圖沒有(至少是減少)閃爍、撕裂等不良效果,并減少等待時間。
    • 雙緩沖機(jī)制的原理大概是:所有畫圖操作將它們畫圖的結(jié)果保存在一塊系統(tǒng)內(nèi)存區(qū)域中,這塊區(qū)域通常被稱作“后緩沖區(qū)(back buffer)”,當(dāng)所有的繪圖操作結(jié)束之后,將整塊區(qū)域復(fù)制到顯示內(nèi)存中,這個復(fù)制操作通常要跟顯示器的光棧束同步,以避免撕裂。雙緩沖機(jī)制必須要求有比單緩沖更多的顯示內(nèi)存和CPU消耗時間,因為“后緩沖區(qū)”需要顯示內(nèi)存,而復(fù)制操作和等待同步需要CPU時間。
3.SurfaceView的使用

模板代碼

//繼承SurfaceView 類,實現(xiàn)SurfaceHolder.Callback 和 Runnable 接口
public abstract class SurfaceViewTemplate extends SurfaceView implements SurfaceHolder.Callback, Runnable {

    //定義以下三個成員變量
    private SurfaceHolder mHolder;  //SurfaceHolder
    private Canvas mCanvas;         //用于繪制的Canvas
    private boolean mIsDrawing;     //子線程繪制標(biāo)志位

    public SurfaceViewTemplate(Context context) {
        super(context);
        initView();
    }

    public SurfaceViewTemplate(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public SurfaceViewTemplate(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    //初始化
    private void initView() {
        mHolder = getHolder();
        mHolder.addCallback(this);
        setFocusable(true);
        setFocusableInTouchMode(true);
        setKeepScreenOn(true);
        onInit(mHolder);
    }

    //初始化時候回調(diào)
    protected abstract void onInit(SurfaceHolder holder);

    //當(dāng)SurfaceView創(chuàng)建的時候,開啟子線程進(jìn)行繪制操作
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        mIsDrawing = true;
        new Thread(this).start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    //當(dāng)SurfaceViewDestroy時候停止繪制操作
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        mIsDrawing = false;
    }

    private void draw() {
        try {
            mCanvas = mHolder.lockCanvas();
            realDraw(mCanvas);
        } catch (Exception e) {
            onError(e);
        } finally {
            if (mCanvas != null) {
                mHolder.unlockCanvasAndPost(mCanvas);
            }
        }
    }

    //循環(huán)進(jìn)行繪制流程
    @Override
    public void run() {
        
        while (mIsDrawing) {
            beforeDraw();
            draw();
            afterDraw();
        }
        
    }

    //繪制之前回調(diào)
    protected abstract void beforeDraw();

    //繪制完畢回調(diào)
    protected abstract void afterDraw();

    //真正的繪制一些東西
    protected abstract void realDraw(Canvas canvas);

    //繪制時候發(fā)生異?;卣{(diào)
    protected abstract void onError(Exception e);
}

繪畫板Demo

public class DrawingBoardView extends SurfaceViewTemplate {

    public DrawingBoardView(Context context) {
        super(context);
    }

    public DrawingBoardView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public DrawingBoardView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private Paint mPaint;

    private Path mPath;

    @Override
    protected void onInit(SurfaceHolder holder) {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.RED);
        mPaint.setStyle(Paint.Style.STROKE);
        mPath = new Path();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mPath.moveTo(x, y);
                break;
            case MotionEvent.ACTION_MOVE:
                mPath.lineTo(x, y);
                break;
        }
        return true;
    }

    @Override
    protected void beforeDraw() {

    }

    @Override
    protected void afterDraw() {

    }

    @Override
    protected void realDraw(Canvas canvas) {
        //canvas默認(rèn)保存了上一次繪制的內(nèi)容
        //所以這里清空一下,重新開始繪制
        canvas.drawColor(Color.WHITE);
        //繪制路徑
        canvas.drawPath(mPath, mPaint);
    }

    @Override
    protected void onError(Exception e) {

    }
}

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

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容