TextureView 初探:结合 Camera 实现相机预览
1、TextureView 基本概念
1.1、什么是 TextureView
TextureView 是 Android 4.0(API 14)引入的一个视图类,它可以将内容流(如相机预览、视频播放)渲染到一个硬件加速的纹理上。与 SurfaceView 不同,TextureView 是一个普通的 View,可以像其他 View 一样进行变换(如旋转、缩放)和动画。
1.2、TextureView 的特点
- 支持硬件加速渲染
- 可以像普通 View 一样进行变换和动画
- 可以与其他 View 叠加
- 支持截图功能
- 比 SurfaceView 消耗更多的内存和 GPU 资源
2、TextureView 基本用法
2.1、在布局文件中添加 TextureView
<TextureView
android:id="@+id/texture_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />2.2、获取 TextureView 实例
TextureView textureView = findViewById(R.id.texture_view);2.3、设置 SurfaceTextureListener
textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
// SurfaceTexture 可用时回调
// 在这里初始化相机
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
// SurfaceTexture 尺寸变化时回调
// 在这里调整相机预览尺寸
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
// SurfaceTexture 销毁时回调
// 在这里释放相机资源
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
// SurfaceTexture 更新时回调
// 可以在这里处理每一帧的图像
}
});2.4、TextureView 的常用方法
- getSurfaceTexture():获取 SurfaceTexture 实例
- getBitmap():获取当前帧的 Bitmap
- setTransform():设置变换矩阵
- setAlpha():设置透明度
- setRotation():设置旋转角度
- setScaleX()/setScaleY():设置缩放比例
3、TextureView 结合 Camera 实现
3.1、相机权限声明
在 AndroidManifest.xml 中添加相机权限:
<uses-permission android:name="android.permission.CAMERA" />对于 Android 6.0 及以上版本,需要动态申请权限:
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION);
}3.2、初始化相机
private Camera mCamera;
private TextureView mTextureView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextureView = findViewById(R.id.texture_view);
mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
}
private TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
// 打开相机
mCamera = Camera.open();
try {
// 设置相机预览
mCamera.setPreviewTexture(surface);
// 开始预览
mCamera.startPreview();
// 调整 TextureView 尺寸
adjustTextureViewSize();
} catch (IOException e) {
e.printStackTrace();
}
}
// 其他方法实现...
};3.3、调整 TextureView 尺寸
private void adjustTextureViewSize() {
if (mCamera == null || mTextureView == null) {
return;
}
// 获取相机参数
Camera.Parameters parameters = mCamera.getParameters();
// 获取支持的预览尺寸
List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
if (previewSizes == null || previewSizes.isEmpty()) {
return;
}
// 选择合适的预览尺寸
Camera.Size optimalSize = getOptimalPreviewSize(previewSizes, mTextureView.getWidth(), mTextureView.getHeight());
// 设置相机预览尺寸
parameters.setPreviewSize(optimalSize.width, optimalSize.height);
mCamera.setParameters(parameters);
// 调整 TextureView 布局参数
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mTextureView.getLayoutParams();
float ratio = (float) optimalSize.width / optimalSize.height;
int viewWidth = mTextureView.getWidth();
int viewHeight = (int) (viewWidth / ratio);
params.height = viewHeight;
mTextureView.setLayoutParams(params);
}
private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int width, int height) {
final double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double) width / height;
if (sizes == null) {
return null;
}
Camera.Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = height;
// 找出最接近目标比例的尺寸
for (Camera.Size size : sizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) {
continue;
}
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
// 如果没有找到合适的比例,就找最小高度的
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Camera.Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}3.4、处理相机生命周期
@Override
protected void onPause() {
super.onPause();
if (mCamera != null) {
mCamera.stopPreview();
}
}
@Override
protected void onResume() {
super.onResume();
if (mTextureView.isAvailable()) {
if (mCamera != null) {
try {
mCamera.setPreviewTexture(mTextureView.getSurfaceTexture());
mCamera.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mCamera != null) {
mCamera.release();
mCamera = null;
}
}3.5、实现相机切换
private int mCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
private void switchCamera() {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
// 切换相机
mCameraId = (mCameraId == Camera.CameraInfo.CAMERA_FACING_BACK) ?
Camera.CameraInfo.CAMERA_FACING_FRONT : Camera.CameraInfo.CAMERA_FACING_BACK;
try {
mCamera = Camera.open(mCameraId);
mCamera.setPreviewTexture(mTextureView.getSurfaceTexture());
mCamera.startPreview();
adjustTextureViewSize();
} catch (Exception e) {
e.printStackTrace();
}
}4、TextureView 高级应用
4.1、实现相机预览的缩放和旋转
// 缩放
mTextureView.setScaleX(1.5f);
mTextureView.setScaleY(1.5f);
// 旋转
mTextureView.setRotation(90f);
// 组合变换
Matrix matrix = new Matrix();
matrix.setScale(1.5f, 1.5f);
matrix.setRotate(90f);
mTextureView.setTransform(matrix);4.2、实现截图功能
private void captureScreenshot() {
if (mTextureView.isAvailable()) {
Bitmap bitmap = mTextureView.getBitmap();
// 保存截图
saveBitmap(bitmap);
}
}
private void saveBitmap(Bitmap bitmap) {
File file = new File(Environment.getExternalStorageDirectory(), "screenshot.jpg");
try {
FileOutputStream fos = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
fos.flush();
fos.close();
Toast.makeText(this, "截图已保存", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
}
}4.3、实现滤镜效果
private void applyFilter() {
if (mTextureView.isAvailable()) {
Bitmap bitmap = mTextureView.getBitmap();
// 应用滤镜
Bitmap filteredBitmap = applyGrayscaleFilter(bitmap);
// 显示滤镜效果
imageView.setImageBitmap(filteredBitmap);
}
}
private Bitmap applyGrayscaleFilter(Bitmap bitmap) {
Bitmap grayscaleBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(grayscaleBitmap);
Paint paint = new Paint();
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setSaturation(0); // 灰度
ColorMatrixColorFilter colorFilter = new ColorMatrixColorFilter(colorMatrix);
paint.setColorFilter(colorFilter);
canvas.drawBitmap(bitmap, 0, 0, paint);
return grayscaleBitmap;
}4.4、与 MediaRecorder 结合录制视频
private MediaRecorder mMediaRecorder;
private void startRecording() {
if (mCamera == null) {
return;
}
try {
// 解锁相机
mCamera.unlock();
// 初始化 MediaRecorder
mMediaRecorder = new MediaRecorder();
mMediaRecorder.setCamera(mCamera);
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mMediaRecorder.setOutputFile(getOutputMediaFile().toString());
mMediaRecorder.setPreviewDisplay(mTextureView.getSurfaceTexture());
// 准备并开始录制
mMediaRecorder.prepare();
mMediaRecorder.start();
} catch (Exception e) {
e.printStackTrace();
// 重新锁定相机
mCamera.lock();
}
}
private void stopRecording() {
if (mMediaRecorder != null) {
try {
mMediaRecorder.stop();
mMediaRecorder.release();
mMediaRecorder = null;
mCamera.lock();
} catch (Exception e) {
e.printStackTrace();
}
}
}
private File getOutputMediaFile() {
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_MOVIES), "CameraDemo");
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
return null;
}
}
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
return new File(mediaStorageDir.getPath() + File.separator +
"VID_" + timeStamp + ".mp4");
}5、最佳实践与注意事项
5.1、性能优化
- 避免在主线程进行耗时操作
- 合理设置相机预览尺寸
- 及时释放相机资源
- 避免频繁的视图变换
5.2、内存管理
- 及时释放 Bitmap 资源
- 避免内存泄漏
- 监控应用内存使用情况
5.3、兼容性问题
- 在 Android 4.0 以下版本不支持 TextureView
- 部分设备可能存在硬件加速问题
- 不同设备的相机参数可能有所不同
5.4、权限处理
- 正确申请相机权限
- 处理权限拒绝的情况
- 提供友好的权限提示
5.5、用户体验
- 提供相机切换功能
- 实现平滑的预览效果
- 提供截图和录制功能
- 处理各种异常情况
TextureView 是 Android 中一个强大的视图组件,它可以将内容流渲染到硬件加速的纹理上,支持各种视图变换和动画。结合 Camera 使用时,可以实现灵活的相机预览效果,如缩放、旋转、截图等。
在实际开发中,需要注意以下几点:
- 正确处理相机权限
- 合理管理相机生命周期
- 优化性能和内存使用
- 处理兼容性问题
- 提供良好的用户体验
通过掌握 TextureView 的使用方法和最佳实践,可以开发出功能丰富、性能优秀的相机应用。

