主頁 > 移動端開發 > 講講Android為自定義view提供的SurfaceView

講講Android為自定義view提供的SurfaceView

2021-09-24 11:27:13 移動端開發

系列文章目錄

講講Android為自定義view提供的SurfaceView


文章目錄

  • 系列文章目錄
  • 前言
  • 一、Android為什么會提供SurfaceView
  • 二、先看看Android Demo的實作
    • 1.實作介面以及介面定義的方法
    • 2.與Activity生命周期進行系結
    • 3.完成初始化操作
    • 4.實作
    • 5.運行
  • 三、繼承SurfaceView實作
    • 1.自定義類繼承自SurfaceView,并且實作兩個介面以及介面定義的方法,
    • 2.初始化
    • 3.步驟與Android Demo的實作-4.實作類似
  • 四、放一個使用案例原始碼
  • 五、拓展一下(以下內容來源于網路)

前言

前幾天發表了幾篇在自定義view中通過修改值實作動態效果的文章,起到主要作用的是呼叫重繪界面的方法,但是假設繪制的程序邏輯比較復雜,并且界面更新頻繁,這時候就會造成界面的卡頓,十分影響用戶體驗感,

靈感來源于,Android官方demo(效果圖如下)

Video_20210830_065918_904.gif


一、Android為什么會提供SurfaceView

View是通過重繪來重繪視圖,并且有一個重繪的間隔,當繪制程序邏輯很復雜加上界面更新還非常頻繁時,就可能無法在間隔內完成繪制,就會造成界面效果的卡頓,影響用戶體驗,為此Android提供了SurfaceView來解決這一問題,

二、先看看Android Demo的實作

1.實作介面以及介面定義的方法

....implements SurfaceHolder.Callback2
public void surfaceCreated(SurfaceHolder holder) {
    synchronized (mDrawingThread) {
        mDrawingThread.mSurface = holder;
        mDrawingThread.notify();
    }
}

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    //這里不需要做任何事情;繪制執行緒將從畫布中獲取
}

public void surfaceRedrawNeeded(SurfaceHolder holder) {
}

public void surfaceDestroyed(SurfaceHolder holder) {
    //我們需要告訴繪圖執行緒停止
    synchronized (mDrawingThread) {
        mDrawingThread.mSurface = holder;
        mDrawingThread.notify();
        while (mDrawingThread.mActive) {
            try {
                mDrawingThread.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

2.與Activity生命周期進行系結

@Override
protected void onPause() {
    super.onPause();

    //當我們暫停時,確保繪制執行緒沒有運行,
    synchronized (mDrawingThread) {
        mDrawingThread.mRunning = false;
        mDrawingThread.notify();
    }
}

@Override
protected void onResume() {
    super.onResume();

    //讓繪圖執行緒繼續運行,
    synchronized (mDrawingThread) {
        mDrawingThread.mRunning = true;
        mDrawingThread.notify();
    }
}

@Override
protected void onDestroy() {
    super.onDestroy();

    //確保繪圖執行緒消失,
    synchronized (mDrawingThread) {
        mDrawingThread.mQuit = true;
        mDrawingThread.notify();
    }
}

3.完成初始化操作

4.實作

  • 通過lockCanvas()方法獲得Canvas物件
//鎖定畫布進行繪圖,
Canvas canvas = mSurface.lockCanvas();
if (canvas == null) {
    Log.i("WindowSurface", "Failure locking canvas");
    continue;
}
  • 在子執行緒中使用Canvas物件進行繪制
// 更新圖形
if (!mInitialized) {
    mInitialized = true;
    mPoint1.init(canvas.getWidth(), canvas.getHeight(), mMinStep);
    mPoint2.init(canvas.getWidth(), canvas.getHeight(), mMinStep);
    mColor.init(127, 127, 1);
} else {
    mPoint1.step(canvas.getWidth(), canvas.getHeight(),
            mMinStep, mMaxStep);
    mPoint2.step(canvas.getWidth(), canvas.getHeight(),
            mMinStep, mMaxStep);
    mColor.step(127, 127, 1, 3);
}
//顏色的效果
mBrightLine+=2;
if (mBrightLine > (NUM_OLD*2)) {
    mBrightLine = -2;
}

// 清理背景
canvas.drawColor(mBackground.getColor());

// 畫舊線
for (int i=mNumOld-1; i>=0; i--) {
    mForeground.setColor(mOldColor[i] | makeGreen(i));
    mForeground.setAlpha(((NUM_OLD-i) * 255) / NUM_OLD);
    int p = i*4;
    canvas.drawLine(mOld[p], mOld[p+1], mOld[p+2], mOld[p+3], mForeground);


}

// 畫新線
int red = (int)mColor.x + 128;
if (red > 255) red = 255;
int blue = (int)mColor.y + 128;
if (blue > 255) blue = 255;
int color = 0xff000000 | (red<<16) | blue;
mForeground.setColor(color | makeGreen(-2));
canvas.drawLine(mPoint1.x, mPoint1.y, mPoint2.x, mPoint2.y, mForeground);




// 添加新的線條
if (mNumOld > 1) {
    System.arraycopy(mOld, 0, mOld, 4, (mNumOld-1)*4);
    System.arraycopy(mOldColor, 0, mOldColor, 1, mNumOld-1);
}
if (mNumOld < NUM_OLD) mNumOld++;
mOld[0] = mPoint1.x;
mOld[1] = mPoint1.y;
mOld[2] = mPoint2.x;
mOld[3] = mPoint2.y;
mOldColor[0] = color;
  • 使用unlockCanvasAndPost()方法將畫布內容進行提交
//全部完成
mSurface.unlockCanvasAndPost(canvas);

5.運行

//告訴活動的視窗,我們想做我們自己的繪制
getWindow().takeSurface(this);
//這是將繪制到我們的表面的執行緒,
mDrawingThread = new DrawingThread();
mDrawingThread.start();

三、繼承SurfaceView實作

1.自定義類繼承自SurfaceView,并且實作兩個介面以及介面定義的方法,

public class MyView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
    public MyView(Context context) {
        super(context);
    }

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

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
    //創建
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
       
    }
    //改變
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }
    //銷毀
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

    }
    //子執行緒
    @Override
    public void run() {
    //子執行緒中執行的繪圖邏輯
    }
}

2.初始化

        mSurfaceHolder = getHolder();
        mSurfaceHolder.addCallback(this);
       ......

3.步驟與Android Demo的實作-4.實作類似

  • 通過lockCanvas()方法獲得Canvas物件
  • 在子執行緒中使用Canvas物件進行繪制(run())
  • 使用unlockCanvasAndPost()方法將畫布內容進行提交
        try {
            mCanvas = mSurfaceHolder.lockCanvas();
            mCanvas.drawColor(Color.WHITE);
            //繪制的邏輯
            .....
        }catch (Exception e){

        }finally {
            if (mCanvas != null){
                //釋放canvas物件并提交畫布
                mSurfaceHolder.unlockCanvasAndPost(mCanvas);
            }
        }

四、放一個使用案例原始碼

效果見:Android自定義view之線條等待影片(靈感來源:金鏟鏟之戰)

public class MyView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
    private SurfaceHolder mSurfaceHolder;
    private Canvas mCanvas;
    private int mWidth;
    private int mHeight;
    private int useWidth, minwidth;
    private boolean viewContinue=true,viewContinue1=true;
    private float mSweep,mSweep1;
    private boolean runDrawing;
    private Paint mPaint;
    public MyView(Context context) {
        super(context);
        initView();
    }

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

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
    //創建
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        runDrawing = true;
        new Thread(this).start();
    }
    //改變
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }
    //銷毀
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        runDrawing=false;
    }
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        mHeight = h;
        useWidth = mWidth;
        if (mWidth > mHeight) {
            useWidth = mHeight;
        }

    }
    //子執行緒
    @Override
    public void run() {
        while (runDrawing){
            draw();
        }
    }

    //繪制
    private void draw() {
        try {
            //獲得canvas物件
            mCanvas = mSurfaceHolder.lockCanvas();
            //繪制背景顏色
            mCanvas.drawColor(Color.WHITE);
            
            minwidth = useWidth / 10;

            mCanvas.drawLine(minwidth*5,minwidth*5,minwidth*5+mSweep,minwidth*5+mSweep,mPaint);
            mCanvas.drawLine(minwidth*5,minwidth*5,minwidth*5-mSweep,minwidth*5-mSweep,mPaint);
            mCanvas.drawLine(minwidth*5,minwidth*5,minwidth*5+mSweep,minwidth*5-mSweep,mPaint);
            mCanvas.drawLine(minwidth*5,minwidth*5,minwidth*5-mSweep,minwidth*5+mSweep,mPaint);

            mCanvas.drawLine(minwidth*5+mSweep,minwidth*5+mSweep,minwidth*5+mSweep,minwidth*5+mSweep-mSweep1,mPaint);
            mCanvas.drawLine(minwidth*5-mSweep,minwidth*5-mSweep,minwidth*5-mSweep,minwidth*5-mSweep+mSweep1,mPaint);
            mCanvas.drawLine(minwidth*5+mSweep,minwidth*5-mSweep,minwidth*5+mSweep-mSweep1,minwidth*5-mSweep,mPaint);
            mCanvas.drawLine(minwidth*5-mSweep,minwidth*5+mSweep,minwidth*5-mSweep+mSweep1,minwidth*5+mSweep,mPaint);


            if (viewContinue&&viewContinue1){
                mSweep += 2;
                if (mSweep > minwidth*2) {
                    viewContinue=false;

                }

            }
            if (!viewContinue&&viewContinue1){
                mSweep1 += 4;
                if (mSweep1 > 4*minwidth) {
                    viewContinue1=false;
                    viewContinue=true;

                }
            }
            if (viewContinue&&!viewContinue1){
                if (mSweep1 <=0) {
                    mSweep-=4;
                    if (mSweep<0){
                        viewContinue=true;
                        viewContinue1=true;
                    }

                }else{
                    mSweep1 -= 2;
                }
            }
            //重繪View
            invalidate();
        }catch (Exception e){

        }finally {
            if (mCanvas != null){
                //釋放canvas物件并提交畫布
                mSurfaceHolder.unlockCanvasAndPost(mCanvas);
            }
        }
    }
    private void initView(){
        mSurfaceHolder = getHolder();
        //注冊回呼方法
        mSurfaceHolder.addCallback(this);
        setFocusable(true);
        setKeepScreenOn(true);
        setFocusableInTouchMode(true);
        //初始化畫筆
        initPaint();
    }

    private void initPaint() {
        mPaint = new Paint();        //創建畫筆物件
        mPaint.setColor(Color.BLACK);    //設定畫筆顏色
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(4f);     //設定畫筆寬度為10px
        mPaint.setAntiAlias(true);     //設定抗鋸齒
        mPaint.setAlpha(255);        //設定畫筆透明度
    }
}

五、拓展一下(以下內容來源于網路)

View和SurfaceView的區別:

  • View適用于主動更新的情況,而SurfaceView則適用于被動更新的情況,比如頻繁重繪界面,
  • View在主執行緒中對頁面進行重繪,而SurfaceView則開啟一個子執行緒來對頁面進行重繪,
  • View在繪圖時沒有實作雙緩沖機制,SurfaceView在底層機制中就實作了雙緩沖機制,

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/302542.html

標籤:其他

上一篇:??【Android精進之路-04】Android核心組件Activity,必須掌握的知識點(Activity是什么,生命周期是怎樣的)??

下一篇:受眾同步管理功能上線,讓你的活動禮包發對人

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more