android SurfaceView與SurfaceHolder使用解析

寒到要死 7年前發布 | 15K 次閱讀 Android開發 移動開發 SurfaceView

surfaceView是視圖(View)的繼承類,這個視圖里內嵌了一個專門用于繪制的Surface,可以控制這個Surface的格式和尺寸。Surfaceview控制這個Surface的繪制位置。

SurfaceView分析

  1. surface是縱深排序(Z-ordered)的,這表明它總在自己所在窗口的后面。
  2. surfaceview提供了一個可見區域,只有在這個可見區域內 surface 部分內容才可見,可見區域外的部分不可見。
  3. surface 的排版顯示受到視圖層級關系的影響,它的兄弟視圖結點會在頂端顯示。這意味者 surface的內容會被它的兄弟視圖遮擋,這一特性可以用來放置遮蓋物(overlays)(例如,文本和按鈕等控件)。
  4. 注意,如果 surface 上面有透明控件,那么它的每次變化都會引起框架重新計算它和頂層控件之間的透明效果,這會影響性能。
  5. SurfaceView類的目的在于提供一個可以使用繪圖線程渲染到屏幕的surface。
  6. 所有的SurfaceView和surfaceHolder.Callback方法都應該在主線程調用,所以對于繪圖線程使用的變量應該做同步處理。

    從android N開始,SurfaceView窗口的位置與其他View同步更新,意味著對屏幕上的SurfaceView做轉換和縮放時,不會再出現重影。

  7. 一定要保證繪圖線程僅在surface可用時對其訪問,即在SurfaceHolder.Callback.surfaceCreated()與SurfaceHolder.Callback.surfaceDestroyed()之間。

SurfaceHolder簡介

SurfaceHolder是一個持有surface的抽象接口,可以控制surface的大小、格式、編輯、監聽surface改變,一般通過SurfaceView實現。

在SurfaceHolder的方法中,下面幾個經常用到:

  1. abstract void addCallback(SurfaceHolder.Callback callback);// 給SurfaceView當前的持有者一個回調對象。
  2. abstract Canvas lockCanvas();// 鎖定畫布,一般在鎖定后就可以通過其返回的畫布對象進行畫圖操作。
  3. abstract Canvas lockCanvas(Rect dirty);// 鎖定畫布的某個區域進行畫圖等。因為畫完圖后,會調用下面的unlockCanvasAndPost來改變顯示內容。相對部分內存要求比較高的游戲來說,可以不用重畫dirty外的其它區域的像素,可以提高速度。
  4. abstract void unlockCanvasAndPost(Canvas canvas);// 結束鎖定畫圖,并提交改變。

SurfaceView搭配SurfaceHolder進行繪圖

  1. 當SurfaceView的窗口可見時,創建surface;需要實現SurfaceCreated(SurfaceHolder)和surfaceDestoryed(SurfaceHolder)接口,監測窗口顯示、隱藏時surface的創建和銷毀。
  2. SurfaceView中沒有提供直接獲取surface的方法,需要通過surface的包裝類SurfaceHolder來操作surface,獲取SurfaceHolder可以使用SurfaceV的getHolder()方法;
  3. 在Surface上繪圖時,需要在繪圖線程中通過SurefaceHolder的lockCanvas()方法獲取surface區域,由于是在繪圖線程工作,所以繪制完畢后需要調用SurfaceHolder 的 unlockCanvasAndPost()方法解鎖 Canvas,并且讓 UI 線程把 Surface 上面的東西繪制到 View 的 Canvas 上面。
public classGameUIextendsSurfaceViewimplementsSurfaceHolder.Callback{

    private SurfaceHolder holder;
    private RenderThread renderThread;
    private boolean isDraw = false;// 控制繪制的開關

    publicGameUI(Context context){
        super(context);
        holder = this.getHolder();
        holder.addCallback(this);

        renderThread = new RenderThread();
    }

    @Override
    publicvoidsurfaceChanged(SurfaceHolder holder,intformat,intwidth,intheight){
    }

    @Override
    publicvoidsurfaceCreated(SurfaceHolder holder){
        isDraw = true;
        renderThread.start();

    }

    @Override
    publicvoidsurfaceDestroyed(SurfaceHolder holder){
        isDraw = false;

    }

    /**
    * 繪制界面的線程
    *
    * @author Administrator
    *
    */
    private classRenderThreadextendsThread{
        @Override
        publicvoidrun(){
            // 不停繪制界面
            while (isDraw) {
                drawUI();
            }
            super.run();
        }
    }

    /**
    * 界面繪制
    */
    publicvoiddrawUI(){
        Canvas canvas = holder.lockCanvas();
        try {
            drawCanvas(canvas);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            holder.unlockCanvasAndPost(canvas);
        }
    }

    privatevoiddrawCanvas(Canvas canvas){
        // 在 canvas 上繪制需要的圖形
    }
}

SuraceView搭配SurfaceHolder進行相機預覽

SurfaceView子類代碼

/** A basic Camera preview class */
public classCameraPreviewextendsSurfaceViewimplementsSurfaceHolder.Callback{
    private SurfaceHolder mHolder;
    private Camera mCamera;

    publicCameraPreview(Context context, Camera camera){
        super(context);
        mCamera = camera;

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        mHolder = getHolder();
        mHolder.addCallback(this);
        // deprecated setting, but required on Android versions prior to 3.0
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    publicvoidsurfaceCreated(SurfaceHolder holder){
        // The Surface has been created, now tell the camera where to draw the preview.
        try {
            mCamera.setPreviewDisplay(holder);
            mCamera.startPreview();
        } catch (IOException e) {
            Log.d(TAG, "Error setting camera preview: " + e.getMessage());
        }
    }

    publicvoidsurfaceDestroyed(SurfaceHolder holder){
        // empty. Take care of releasing the Camera preview in your activity.
    }

    publicvoidsurfaceChanged(SurfaceHolder holder,intformat,intw,inth){
        // If your preview can change or rotate, take care of those events here.
        // Make sure to stop the preview before resizing or reformatting it.

        if (mHolder.getSurface() == null){
          // preview surface does not exist
          return;
        }

        // stop preview before making changes
        try {
            mCamera.stopPreview();
        } catch (Exception e){
          // ignore: tried to stop a non-existent preview
        }

        // set preview size and make any resize, rotate or
        // reformatting changes here

        // start preview with new settings
        try {
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();

        } catch (Exception e){
            Log.d(TAG, "Error starting camera preview: " + e.getMessage());
        }
    }
}

布局代碼

<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
  <FrameLayout
    android:id="@+id/camera_preview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_weight="1"
    />

  <Button
    android:id="@+id/button_capture"
    android:text="Capture"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    />
</LinearLayout>

activity中調用

public classCameraActivityextendsActivity{

    private Camera mCamera;
    private CameraPreview mPreview;

    @Override
    publicvoidonCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // Create an instance of Camera
        mCamera = getCameraInstance();

        // Create our Preview view and set it as the content of our activity.
        mPreview = new CameraPreview(this, mCamera);
        FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
        preview.addView(mPreview);
    }
}

 

來自:https://wangyantao.github.io/surfaceview/

 

 本文由用戶 寒到要死 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!