最近一段時間一直在做智能後視鏡産品(Android平台)裡的相機子產品,目前已經告一段落了。現在空閑的時候可以把一些技術點記錄下來。
其中有一個需求就是顯示前、後兩個攝像頭的預覽畫面,并且可以互相切換顯示。
類似下面這個樣子:

在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);
}