近期。在改動Android4.4的原生相機Camera2,非常習慣的去尋找SurfaceView。結果任憑我使用grep還是ack。都無法搜索到SurfaceView,最后還是通過代碼CameraActivity-->CameraModule-->PhotoUI-->R.layout.photo_module找到。原來是使用了TextureView。不是非常了解此控件,百度之。在官方API文檔中找到此控件:
http://android.toolib.net/reference/android/view/TextureView.html
官方文檔大概的意思是:
TextureView能夠用來顯示內容流。這樣一個內容流比如能夠視頻或者OpenGL的場景。
內容流能夠來自本應用程序以及其它進程。
Textureview必須在硬件加速開啟的窗體中。
與SurfaceView相比,TextureView不會創建一個單獨的窗體。這使得它能夠像一般的View一樣運行一些變換操作,比方移動、動畫等等,比如,你能夠通過調用myView.setAlpha(0.5f)將TextureView設置成半透明。
使用TextureView非常easy:你須要使用的就是SurfaceTexture,SurfaceTexture能夠用於呈現內容。
以下是我寫一個小樣例來演示怎樣渲染相機預覽到TextureView,在官方文檔樣例的基礎上略微修改了一下:
main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" >
<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:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Capture" />
</LinearLayout>
CameraPreview.java:
import java.io.IOException;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.view.TextureView;
@SuppressLint("NewApi")
public class CameraPreview extends TextureView implements
TextureView.SurfaceTextureListener {
private Camera mCamera;
private TextureView mTextureView;
public CameraPreview(Context context , Camera camera) {
super(context);
mCamera = camera;
// TODO Auto-generated constructor stub
}
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width,
int height) {
// mCamera = Camera.open();
try {
mCamera.setPreviewTexture(surface);
mCamera.startPreview();
} catch (IOException ioe) {
// Something bad happened
}
}
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width,
int height) {
// Ignored, Camera does all the work for us
}
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
mCamera.stopPreview();
mCamera.release();
return true;
}
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
// Invoked every time there's a new Camera preview frame
}
}
CameraTest.java:
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.Toast;
import com.example.mycamera.R.id;
@SuppressLint("NewApi")
public class CameraTest extends Activity {
public static final int MEDIA_TYPE_IMAGE = 1;
public static final int MEDIA_TYPE_VIDEO = 2;
private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100;
private static final int CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE = 200;
private Camera mCamera;
private CameraPreview mPreview;
private static final String TAG = "ERROR";
private PictureCallback mPicture = new PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
if (pictureFile == null) {
Log.d(TAG,
"Error creating media file, check storage permissions: "
+ "e.getMessage()");
return;
}
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(data);
fos.close();
} catch (FileNotFoundException e) {
Log.d(TAG, "File not found: " + e.getMessage());
} catch (IOException e) {
Log.d(TAG, "Error accessing file: " + e.getMessage());
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 創建Camera實例
mCamera = getCameraInstance();
// 創建Preview view並將其設為activity中的內容
mPreview = new CameraPreview(this, mCamera);
mPreview.setSurfaceTextureListener(mPreview);
//設置渾濁
mPreview.setAlpha(0.5f);
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
// preview.setAlpha(0.0f);
preview.addView(mPreview);
// 在Capture按鈕中增加listener
Button captureButton = (Button) findViewById(id.button_capture);
captureButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 從攝像頭獲取圖片
mCamera.takePicture(null, null, mPicture);
}
});
}
/** 安全獲取Camera對象實例的方法 */
public static Camera getCameraInstance() {
Camera c = null;
try {
c = Camera.open(); // 試圖獲取Camera實例
}
catch (Exception e) {
// 攝像頭不可用(正被占用或不存在)
}
return c; // 不可用則返回null
}
/** 為保存圖片或視頻創建File */
private static File getOutputMediaFile(int type) {
// 安全起見,在使用前應該
// 用Environment.getExternalStorageState()檢查SD卡是否已裝入
File mediaStorageDir = new File(
Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
"MyCameraApp");
// 假設期望圖片在應用程序卸載后還存在、且能被其他應用程序共享,
// 則此保存位置最合適
// 假設不存在的話,則創建存儲文件夾
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
Log.d("MyCameraApp", "failed to create directory");
return null;
}
Log.d("MyCameraApp", "failed to create directory");
}
// 創建媒體文件名稱
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss")
.format(new Date());
File mediaFile;
if (type == MEDIA_TYPE_IMAGE) {
mediaFile = new File(mediaStorageDir.getPath() + File.separator
+ "IMG_" + timeStamp + ".jpg");
} else if (type == MEDIA_TYPE_VIDEO) {
mediaFile = new File(mediaStorageDir.getPath() + File.separator
+ "VID_" + timeStamp + ".mp4");
} else {
return null;
}
return mediaFile;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) {
if (resultCode == RESULT_OK) {
// 捕獲的圖像保存到Intent指定的fileUri
Toast.makeText(this, "Image saved to:\n" + data.getData(),
Toast.LENGTH_LONG).show();
} else if (resultCode == RESULT_CANCELED) {
// 用戶取消了圖像捕獲
} else {
// 圖像捕獲失敗,提示用戶
}
}
if (requestCode == CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE) {
if (resultCode == RESULT_OK) {
// 捕獲的視頻保存到Intent指定的fileUri
Toast.makeText(this, "Video saved to:\n" + data.getData(),
Toast.LENGTH_LONG).show();
} else if (resultCode == RESULT_CANCELED) {
// 用戶取消了視頻捕獲
} else {
// 視頻捕獲失敗。提示用戶
}
}
}
@Override
protected void onPause() {
super.onPause();
releaseCamera(); // 在暫停事件中馬上釋放攝像頭
}
private void releaseCamera() {
if (mCamera != null) {
mCamera.release(); // 為其他應用釋放攝像頭
mCamera = null;
}
}
}
