Google自Android4.0出了TextureView,為什么推出呢?就是為了彌補Surfaceview的不足,另外一方面也是為了平衡GlSurfaceView,當然這是本人揣度的。關於TextureView、Surfaceview、SurfaceTexture、GLSurfaceView的關系,待咱家推出GLSurfaceview預覽Camera后再專門分析。本文主要介紹使用TextureView預覽Camera。
其實關於如何用TextureView預覽Camera,官網已經給出了demo,參見這里。另外,鏈接1 鏈接2也給出了完整的預覽Camera的demo,但都是一堆東西染在一塊。本文就利用前文 搭建的一個輕量級的Camera框架來快速替換掉Surfaceview。因為用Surfaceview預覽的話傳一個SurfaceHolder進去,用Textureview預覽的話需要傳進去一個SurfaceTexture。其他的Camera流程不變。
一、新建CameraTextureView類繼承TextureView,並實現TextureView.SurfaceTextureListener接口。實現這個接口就像實現SurfaceHolder.Callback,最主要的目的是在SurfaceTexture准備好后能夠知道,也即onSurfaceTextureAvailable這個函數。
CameraTextureView.java
1 <span style="font-family:Comic Sans MS;font-size:18px;">package org.yanzi.camera.preview; 2 3 import org.yanzi.camera.CameraInterface; 4 5 import android.content.Context; 6 import android.graphics.PixelFormat; 7 import android.graphics.SurfaceTexture; 8 import android.util.AttributeSet; 9 import android.util.Log; 10 import android.view.SurfaceHolder; 11 import android.view.SurfaceView; 12 import android.view.TextureView; 13 14 public class CameraTextureView extends TextureView implements TextureView.SurfaceTextureListener { 15 private static final String TAG = "yanzi"; 16 Context mContext; 17 SurfaceTexture mSurface; 18 public CameraTextureView(Context context, AttributeSet attrs) { 19 super(context, attrs); 20 // TODO Auto-generated constructor stub 21 mContext = context; 22 this.setSurfaceTextureListener(this); 23 } 24 @Override 25 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, 26 int height) { 27 // TODO Auto-generated method stub 28 Log.i(TAG, "onSurfaceTextureAvailable..."); 29 mSurface = surface; 30 // CameraInterface.getInstance().doStartPreview(surface, 1.33f); 31 } 32 @Override 33 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 34 // TODO Auto-generated method stub 35 Log.i(TAG, "onSurfaceTextureDestroyed..."); 36 CameraInterface.getInstance().doStopCamera(); 37 return true; 38 } 39 @Override 40 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, 41 int height) { 42 // TODO Auto-generated method stub 43 Log.i(TAG, "onSurfaceTextureSizeChanged..."); 44 } 45 @Override 46 public void onSurfaceTextureUpdated(SurfaceTexture surface) { 47 // TODO Auto-generated method stub 48 Log.i(TAG, "onSurfaceTextureUpdated..."); 49 50 } 51 52 /* 讓Activity能得到TextureView的SurfaceTexture 53 * @see android.view.TextureView#getSurfaceTexture() 54 */ 55 public SurfaceTexture _getSurfaceTexture(){ 56 return mSurface; 57 } 58 } 59 </span>
二、在布局文件里把它加上就行了,因為他的父類就是View,當成一般的View就行
1 <span style="font-family:Comic Sans MS;font-size:18px;"><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 tools:context=".CameraActivity" > 6 <FrameLayout 7 android:layout_width="wrap_content" 8 android:layout_height="wrap_content" > 9 <org.yanzi.camera.preview.CameraTextureView 10 android:id="@+id/camera_textureview" 11 android:layout_width="0dip" 12 android:layout_height="0dip" /> 13 </FrameLayout> 14 <ImageButton 15 android:id="@+id/btn_shutter" 16 android:layout_width="wrap_content" 17 android:layout_height="wrap_content" 18 android:background="@drawable/btn_shutter_background" 19 android:layout_alignParentBottom="true" 20 android:layout_centerHorizontal="true" 21 android:layout_marginBottom="10dip"/> 22 </RelativeLayout> 23 </span>
三、在CameraInterface里,我封裝了兩個函數:
1 <span style="font-family:Comic Sans MS;font-size:18px;">/**使用Surfaceview開啟預覽 2 * @param holder 3 * @param previewRate 4 */ 5 public void doStartPreview(SurfaceHolder holder, float previewRate){ 6 Log.i(TAG, "doStartPreview..."); 7 if(isPreviewing){ 8 mCamera.stopPreview(); 9 return; 10 } 11 if(mCamera != null){ 12 try { 13 mCamera.setPreviewDisplay(holder); 14 } catch (IOException e) { 15 // TODO Auto-generated catch block 16 e.printStackTrace(); 17 } 18 initCamera(previewRate); 19 } 20 21 22 } 23 /**使用TextureView預覽Camera 24 * @param surface 25 * @param previewRate 26 */ 27 public void doStartPreview(SurfaceTexture surface, float previewRate){ 28 Log.i(TAG, "doStartPreview..."); 29 if(isPreviewing){ 30 mCamera.stopPreview(); 31 return; 32 } 33 if(mCamera != null){ 34 try { 35 mCamera.setPreviewTexture(surface); 36 } catch (IOException e) { 37 // TODO Auto-generated catch block 38 e.printStackTrace(); 39 } 40 initCamera(previewRate); 41 } 42 43 }</span>
分別對應Surfaceview和TextureView預覽。可以看到就是傳進來的參數不一樣,initCamera()的東西都一樣。
1 <span style="font-family:Comic Sans MS;font-size:18px;"> private void initCamera(float previewRate){ 2 if(mCamera != null){ 3 4 mParams = mCamera.getParameters(); 5 mParams.setPictureFormat(PixelFormat.JPEG);//設置拍照后存儲的圖片格式 6 // CamParaUtil.getInstance().printSupportPictureSize(mParams); 7 // CamParaUtil.getInstance().printSupportPreviewSize(mParams); 8 //設置PreviewSize和PictureSize 9 Size pictureSize = CamParaUtil.getInstance().getPropPictureSize( 10 mParams.getSupportedPictureSizes(),previewRate, 800); 11 mParams.setPictureSize(pictureSize.width, pictureSize.height); 12 Size previewSize = CamParaUtil.getInstance().getPropPreviewSize( 13 mParams.getSupportedPreviewSizes(), previewRate, 800); 14 mParams.setPreviewSize(previewSize.width, previewSize.height); 15 16 mCamera.setDisplayOrientation(90); 17 18 // CamParaUtil.getInstance().printSupportFocusMode(mParams); 19 List<String> focusModes = mParams.getSupportedFocusModes(); 20 if(focusModes.contains("continuous-video")){ 21 mParams.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); 22 } 23 mCamera.setParameters(mParams); 24 mCamera.startPreview();//開啟預覽 25 26 27 28 isPreviewing = true; 29 mPreviwRate = previewRate; 30 31 mParams = mCamera.getParameters(); //重新get一次 32 Log.i(TAG, "最終設置:PreviewSize--With = " + mParams.getPreviewSize().width 33 + "Height = " + mParams.getPreviewSize().height); 34 Log.i(TAG, "最終設置:PictureSize--With = " + mParams.getPictureSize().width 35 + "Height = " + mParams.getPictureSize().height); 36 } 37 }</span>
四、在Activity里,依舊開一個線程去open Camera:
1 <span style="font-family:Comic Sans MS;font-size:18px;"> Thread openThread = new Thread(){ 2 @Override 3 public void run() { 4 // TODO Auto-generated method stub 5 CameraInterface.getInstance().doOpenCamera(CameraActivity.this); 6 } 7 }; 8 openThread.start();</span>
在Camera Open完的回調里開預覽:
1 <span style="font-family:Comic Sans MS;font-size:18px;"> @Override 2 public void cameraHasOpened() { 3 // TODO Auto-generated method stub 4 SurfaceTexture surface = textureView._getSurfaceTexture(); 5 CameraInterface.getInstance().doStartPreview(surface, previewRate); 6 }</span>
之后就能正常運行了,可以看到與前文Surfaceview預覽Camera 改動非常之小。
幾個注意事項:
1、TextureView是Android 4.0之后加入的,低版本么這個類。TextureView必須工作在開啟硬件加速的環境中,也即配置文件里Activity的設置項里:android:hardwareAccelerated="true" 默認的這個屬性就是true,因此不用再寫了。但如果寫成false,可以看到onSurfaceTextureAvailable()這個回調就進不來了,TextureView沒有了SurfaceTexture還玩個屁啊。
2、本文demo打開camera並預覽的正常log是:
1 <span style="font-family:Comic Sans MS;font-size:18px;"> Line 417: 06-22 12:37:43.682 I/yanzi ( 4917): Camera open.... 2 Line 489: 06-22 12:37:43.758 I/yanzi ( 4917): onSurfaceTextureAvailable... 3 Line 533: 06-22 12:37:43.819 I/yanzi ( 4917): Camera open over.... 4 Line 535: 06-22 12:37:43.819 I/yanzi ( 4917): doStartPreview... 5 Line 537: 06-22 12:37:43.825 I/yanzi ( 4917): PictureSize : w = 1280h = 720 6 Line 539: 06-22 12:37:43.825 I/yanzi ( 4917): PreviewSize:w = 800h = 448 7 Line 555: 06-22 12:37:43.874 I/yanzi ( 4917): 最終設置:PreviewSize--With = 800Height = 448 8 Line 557: 06-22 12:37:43.874 I/yanzi ( 4917): 最終設置:PictureSize--With = 1280Height = 720 9 Line 577: 06-22 12:37:44.106 I/yanzi ( 4917): onSurfaceTextureUpdated... 10 Line 579: 06-22 12:37:44.138 I/yanzi ( 4917): onSurfaceTextureUpdated... 11 Line 583: 06-22 12:37:44.169 I/yanzi ( 4917): onSurfaceTextureUpdated... 12 Line 585: 06-22 12:37:44.220 I/yanzi ( 4917): onSurfaceTextureUpdated... 13 Line 587: 06-22 12:37:44.253 I/yanzi ( 4917): onSurfaceTextureUpdated...</span>
測試手機為中興Geek,這個手機Camera還是很牛逼的,比手里的華為G700強,就是偶爾會連不上Camera Service,汗。從log可以看到,onSurfaceTextureAvailable這個回調需要一定時間。Camera.open()這句話用了130多ms。但有兩點跟Surfaceview不同。第一,TextureView創建過程中沒有進到onSurfaceTextureSizeChanged()這個函數里。而SurfaceView在創建過程中,從無到有的時候會進到大小發生變化回調里。第二,onSurfaceTextureUpdated()這個函數每上來一幀數據,這塊就進來一次。這是跟Surfaceview相比,最偉大的一個地方。通過這個接口,可以將上來的SurfaceTexture送給OpenGL再去處理。這個回調是實時的,而非用Camera的PreviewCallback這種2次回調的方式。從時間看,基本上每32ms左右上來一幀數據,即每秒30幀,跟本手機的Camera的性能吻合。
3、Camera再執行startPreview時必須保證TextureView的SurfaceTexture上來了,如果因為一些性能原因onSurfaceTextureAvailable()這個回調上不來就開預覽,就開不了的。如果發生這種情況,就在onSurfaceTextureAvailable()回調里執行open和startPreview操作,保證萬無一失。
4、TextureView本身就有getSurfaceTexture()這個函數,我又封裝了個:
1 <span style="font-family:Comic Sans MS;font-size:18px;"> /* 讓Activity能得到TextureView的SurfaceTexture 2 * @see android.view.TextureView#getSurfaceTexture() 3 */ 4 public SurfaceTexture _getSurfaceTexture(){ 5 return mSurface; 6 }</span>
這里的mSurface就是onSurfaceTextureAvailable()回調里傳上來的SurfaceTexture。測試證明,開預覽時直接調
textureView.getSurfaceTexture(),把它傳給Camera: mCamera.setPreviewTexture(surface);也是能正常預覽的。但是推薦使用前者,原因見官方上的這段話:
A TextureView's SurfaceTexture can be obtained either by invoking getSurfaceTexture()
or by using a TextureView.SurfaceTextureListener
. It is important to know that a SurfaceTexture is available only after the TextureView is attached to a window (and onAttachedToWindow()
has been invoked.) It is therefore highly recommended you use a listener to be notified when the SurfaceTexture becomes available.
兩種方式獲得SurfaceTexture,推薦使用監聽。因為只有在TextureView執行完onAttachedToWindow時,它的tSurfaceTexture才上來。
5、SurfaceTexture和TextureView的關系:
Using a TextureView is simple: all you need to do is get its SurfaceTexture
. The SurfaceTexture
can then be used to render content
如果說TextureView是一幅畫的話,那SurfaceTexture就是畫布,真正渲染的載體是SurfaceTexture。
6、TextureView可以像一般View執行各種變化,其中有個textureView.setAlpha(1.0f);默認不寫這句話,它的alpha也是1.0f,即不透明。如果設成透明0.0f,可以看到啥都看不到了,這一點跟Surfaceview剛好相反。Surfaceview的SurfaceHolder一般要設一下Transparent即透明。但TextureView因為是個view,任何一個png的照片透明度設成0肯定啥都看不到。
7、如果認為預覽個Camera這就是TextureView和SurfaceTexture的使命的話,就大錯特錯了,真正用意是和OpenGL無縫連接。
--------------------本文系原創,轉載請注明作者yanzi1225627
版本號:PlayCamera_V2.0.0[2014-6-22].zip
CSDN下載鏈接:http://download.csdn.net/detail/yanzi1225627/7540903
百度雲盤: