玩轉Android Camera開發(二):使用TextureView和SurfaceTexture預覽Camera 基礎拍照demo
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
package org.yanzi.camera.preview; import org.yanzi.camera.CameraInterface; import android.content.Context; import android.graphics.PixelFormat; import android.graphics.SurfaceTexture; import android.util.AttributeSet; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.TextureView; public class CameraTextureView extends TextureView implements TextureView.SurfaceTextureListener { private static final String TAG = "yanzi"; Context mContext; SurfaceTexture mSurface; public CameraTextureView(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub mContext = context; this.setSurfaceTextureListener(this); } @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { // TODO Auto-generated method stub Log.i(TAG, "onSurfaceTextureAvailable..."); mSurface = surface; // CameraInterface.getInstance().doStartPreview(surface, 1.33f); } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { // TODO Auto-generated method stub Log.i(TAG, "onSurfaceTextureDestroyed..."); CameraInterface.getInstance().doStopCamera(); return true; } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { // TODO Auto-generated method stub Log.i(TAG, "onSurfaceTextureSizeChanged..."); } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { // TODO Auto-generated method stub Log.i(TAG, "onSurfaceTextureUpdated..."); } /* 讓Activity能得到TextureView的SurfaceTexture * @see android.view.TextureView#getSurfaceTexture() */ public SurfaceTexture _getSurfaceTexture(){ return mSurface; } }二、在布局文件中把它加上即可了,由於他的父類就是View。當成一般的View即可
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".CameraActivity" > <FrameLayout android:layout_width="wrap_content" android:layout_height="wrap_content" > <org.yanzi.camera.preview.CameraTextureView android:id="@+id/camera_textureview" android:layout_width="0dip" android:layout_height="0dip" /> </FrameLayout> <ImageButton android:id="@+id/btn_shutter" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/btn_shutter_background" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="10dip"/> </RelativeLayout>
三、在CameraInterface裏,我封裝了兩個函數:
/**使用Surfaceview開啟預覽
* @param holder
* @param previewRate
*/
public void doStartPreview(SurfaceHolder holder, float previewRate){
Log.i(TAG, "doStartPreview...");
if(isPreviewing){
mCamera.stopPreview();
return;
}
if(mCamera != null){
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
initCamera(previewRate);
}
}
/**使用TextureView預覽Camera
* @param surface
* @param previewRate
*/
public void doStartPreview(SurfaceTexture surface, float previewRate){
Log.i(TAG, "doStartPreview...");
if(isPreviewing){
mCamera.stopPreview();
return;
}
if(mCamera != null){
try {
mCamera.setPreviewTexture(surface);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
initCamera(previewRate);
}
}
分別相應Surfaceview和TextureView預覽。能夠看到就是傳進來的參數不一樣,initCamera()的東西都一樣。 private void initCamera(float previewRate){
if(mCamera != null){
mParams = mCamera.getParameters();
mParams.setPictureFormat(PixelFormat.JPEG);//設置拍照後存儲的圖片格式
// CamParaUtil.getInstance().printSupportPictureSize(mParams);
// CamParaUtil.getInstance().printSupportPreviewSize(mParams);
//設置PreviewSize和PictureSize
Size pictureSize = CamParaUtil.getInstance().getPropPictureSize(
mParams.getSupportedPictureSizes(),previewRate, 800);
mParams.setPictureSize(pictureSize.width, pictureSize.height);
Size previewSize = CamParaUtil.getInstance().getPropPreviewSize(
mParams.getSupportedPreviewSizes(), previewRate, 800);
mParams.setPreviewSize(previewSize.width, previewSize.height);
mCamera.setDisplayOrientation(90);
// CamParaUtil.getInstance().printSupportFocusMode(mParams);
List<String> focusModes = mParams.getSupportedFocusModes();
if(focusModes.contains("continuous-video")){
mParams.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
}
mCamera.setParameters(mParams);
mCamera.startPreview();//開啟預覽
isPreviewing = true;
mPreviwRate = previewRate;
mParams = mCamera.getParameters(); //又一次get一次
Log.i(TAG, "終於設置:PreviewSize--With = " + mParams.getPreviewSize().width
+ "Height = " + mParams.getPreviewSize().height);
Log.i(TAG, "終於設置:PictureSize--With = " + mParams.getPictureSize().width
+ "Height = " + mParams.getPictureSize().height);
}
}
四、在Activity裏,依然開一個線程去open Camera:
Thread openThread = new Thread(){
@Override
public void run() {
// TODO Auto-generated method stub
CameraInterface.getInstance().doOpenCamera(CameraActivity.this);
}
};
openThread.start();
在Camera Open完的回調裏開預覽:
@Override
public void cameraHasOpened() {
// TODO Auto-generated method stub
SurfaceTexture surface = textureView._getSurfaceTexture();
CameraInterface.getInstance().doStartPreview(surface, previewRate);
}
之後就能正常運行了。能夠看到與前文Surfaceview預覽Camera 修改非常之小。
幾個註意事項:
1、TextureView是Android 4.0之後增加的,低版本號麽這個類。TextureView必須工作在開啟硬件加速的環境中。也即配置文件中Activity的設置項裏:android:hardwareAccelerated="true" 默認的這個屬性就是true,因此不用再寫了。但假設寫成false,能夠看到onSurfaceTextureAvailable()這個回調就進不來了。TextureView沒有了SurfaceTexture還玩個屁啊。
2、本文demo打開camera並預覽的正常log是:
Line 417: 06-22 12:37:43.682 I/yanzi ( 4917): Camera open....
Line 489: 06-22 12:37:43.758 I/yanzi ( 4917): onSurfaceTextureAvailable...
Line 533: 06-22 12:37:43.819 I/yanzi ( 4917): Camera open over....
Line 535: 06-22 12:37:43.819 I/yanzi ( 4917): doStartPreview...
Line 537: 06-22 12:37:43.825 I/yanzi ( 4917): PictureSize : w = 1280h = 720
Line 539: 06-22 12:37:43.825 I/yanzi ( 4917): PreviewSize:w = 800h = 448
Line 555: 06-22 12:37:43.874 I/yanzi ( 4917): 終於設置:PreviewSize--With = 800Height = 448
Line 557: 06-22 12:37:43.874 I/yanzi ( 4917): 終於設置:PictureSize--With = 1280Height = 720
Line 577: 06-22 12:37:44.106 I/yanzi ( 4917): onSurfaceTextureUpdated...
Line 579: 06-22 12:37:44.138 I/yanzi ( 4917): onSurfaceTextureUpdated...
Line 583: 06-22 12:37:44.169 I/yanzi ( 4917): onSurfaceTextureUpdated...
Line 585: 06-22 12:37:44.220 I/yanzi ( 4917): onSurfaceTextureUpdated...
Line 587: 06-22 12:37:44.253 I/yanzi ( 4917): onSurfaceTextureUpdated...
測試手機為中興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()這個函數,我又封裝了個:
/* 讓Activity能得到TextureView的SurfaceTexture
* @see android.view.TextureView#getSurfaceTexture()
*/
public SurfaceTexture _getSurfaceTexture(){
return mSurface;
}
這裏的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
百度雲盤:
玩轉Android Camera開發(二):使用TextureView和SurfaceTexture預覽Camera 基礎拍照demo