天天看點

Android兩個SurfaceView疊加顯示

最近一段時間一直在做智能後視鏡産品(Android平台)裡的相機子產品,目前已經告一段落了。現在空閑的時候可以把一些技術點記錄下來。

其中有一個需求就是顯示前、後兩個攝像頭的預覽畫面,并且可以互相切換顯示。

類似下面這個樣子:

Android兩個SurfaceView疊加顯示

在Android裡常用來顯示相機預覽的就是SurfaceView,因為不需要在UI線程重新整理。那這裡我們考慮的就是将兩個SurfaceView疊加在一起顯示。

布局xml(部分):

<FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"  android:layout_gravity="center_vertical|center_horizontal" >      
        < SurfaceView
            android:id="@+id/usb_preview_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
        <SurfaceView
            android:id="@+id/mipi_preview_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </FrameLayout>
           

這樣子就可以把兩個SurfaceView以全屏的形式疊加在一起了。

那問題是怎樣去切換這兩個界面呢?

像普通的view一樣設定其可見性setVisibility,将上層的SurfaceView設定不可見就可以顯示下層的SurfaceView了。

但是實際是不可行的,SurfaceView和普通的View不一樣,它擁有一個獨立于Activity之外的Window,當SurfaceView被設定為不可見的時候,其所對應的Window就會銷毀,再想去顯示這個SurfaceView的時候就不得不重新建立;另外,SurfaceView建立的先後順序也是不受FrameLayout的規則影響的,一般來說,布局xml檔案中FrameLayout布局靠後的view會覆寫考前的view,但SurfaceView是根據其對應的Surface建立的先後順序決定的。

那另一種思路就是,兩個SurfaceView疊在一起後,将上層的SurfaceView所在Window設定成透明的,如果需要顯示下面SurfaceView的内容,就将上層SurfaceView的繪制區域縮小到1、2像素的範圍,肉眼看不見,顯示的就是下層SurfaceView的内容了;顯示上層SurfaceView的時候,就全屏繪制了。這裡SurfaceView對應Window的大小是不會動态改變的,改變的隻是圖像繪制區域的大小。

設定透明的方法,是通過SurfaceHolder來設定的,注意調用的位置,否則無效。

mSurfaceView.getHolder().setFormat(PixelFormat.TRANSPARENT);

API文檔裡的:

public abstract void setFormat (int format)

****Added in API level 1

Set the desired PixelFormat of the surface. The default is OPAQUE. When working with a SurfaceView, this must be called from the same thread running the SurfaceView’s window.**

這種方式實際是可行的,但是也有局限性。Android架構上,我們隻需初始化SurfaceHolder,再通過Camera中setPreviewDisplay(holder)後啟動預覽,就可以自動display the surface了。也就是說,如果想改變SurfaceView繪制的區域,就得重新設定大小和位置,并重新啟動相機預覽,比較耗時,而且在背景錄像的情況下是不可行的。

還好我們這裡在上層的SurfaceView是用來顯示USB攝像頭的預覽,就是在Canvas上将圖像一幀幀繪制出來的。

Canvas canvas = getHolder().lockCanvas();
if (canvas != null) {
    if (mCurrentPreviewState == USB_PREVIEW) {
        rect.set(IMG_WIDTH / , IMG_HEIGHT / , IMG_WIDTH, IMG_HEIGHT);
        canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
        canvas.drawBitmap(mPreviewBitmap, null, rect, null);
    } else {
        canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
        canvas.drawBitmap(mPreviewBitmap, null, rect, null);
    }                   
    getHolder().unlockCanvasAndPost(canvas);
}