package com.example.test2;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.ImageCaptureException;
import androidx.camera.core.Preview;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.camera.video.FileOutputOptions;
import androidx.camera.video.Quality;
import androidx.camera.video.QualitySelector;
import androidx.camera.video.Recorder;
import androidx.camera.video.Recording;
import androidx.camera.video.VideoCapture;
import androidx.camera.video.VideoRecordEvent;
import androidx.camera.view.PreviewView;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.media.MediaPlayer;
import android.net.Uri;
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.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;
import android.widget.VideoView;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.ui.StyledPlayerView;
import com.google.common.util.concurrent.ListenableFuture;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "CameraXDemo";
private static final int REQUEST_CAMERA_PERMISSION = 1001;
private PreviewView previewView;
private FrameLayout previewContainer;
private Button toggleCameraButton;
private Button openCameraButton;
private Button openViewButton;
private ImageView imageView;
private StyledPlayerView playerView;
private ExoPlayer player;
private LinearLayout mediaDisplayLayout; // 新增的媒体显示区域布局
private ListenableFuture<ProcessCameraProvider> cameraProviderFuture;
private ProcessCameraProvider cameraProvider;
private boolean isCameraRunning = false;
private boolean isRecording = false;
private Camera camera;
private ImageCapture imageCapture;
private VideoCapture<Recorder> videoCapture;
private Recording recording;
private ExecutorService cameraExecutor;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
previewView = findViewById(R.id.previewView);
previewContainer = findViewById(R.id.previewContainer);
toggleCameraButton = findViewById(R.id.toggleCameraButton);
openCameraButton = findViewById(R.id.openCameraButton);
openViewButton = findViewById(R.id.openViewButton);
imageView = findViewById(R.id.image);
mediaDisplayLayout = findViewById(R.id.mediaDisplayLayout); // 获取媒体显示区域布局
// 初始化视频播放器视图
playerView = new StyledPlayerView(this);
playerView.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
mediaDisplayLayout.addView(playerView); // 添加到媒体显示区域
playerView.setVisibility(View.GONE);
// 初始化ExoPlayer
player = new ExoPlayer.Builder(this).build();
playerView.setPlayer(player);
// 初始化相机执行器
cameraExecutor = Executors.newSingleThreadExecutor();
// 初始化 CameraProvider
cameraProviderFuture = ProcessCameraProvider.getInstance(this);
// 打开/关闭摄像头按钮
toggleCameraButton.setOnClickListener(v -> {
if (isCameraRunning) {
stopCamera();
} else {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO},
REQUEST_CAMERA_PERMISSION);
} else {
startCamera();
}
}
});
// 拍照按钮
openCameraButton.setOnClickListener(v -> {
if (isCameraRunning && !isRecording) {
takePhoto();
} else if (isRecording) {
Toast.makeText(this, "请先停止录像", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "请先打开摄像头", Toast.LENGTH_SHORT).show();
}
});
// 录像按钮
openViewButton.setOnClickListener(v -> {
if (isCameraRunning) {
if (isRecording) {
stopRecording();
} else {
startRecording();
}
} else {
Toast.makeText(this, "请先打开摄像头", Toast.LENGTH_SHORT).show();
}
});
cameraProviderFuture.addListener(() -> {
try {
cameraProvider = cameraProviderFuture.get();
Log.d(TAG, "CameraProvider 初始化成功");
} catch (ExecutionException | InterruptedException e) {
Log.e(TAG, "CameraProvider 初始化失败", e);
}
}, ContextCompat.getMainExecutor(this));
}
private void startCamera() {
if (cameraProvider == null) {
Log.e(TAG, "CameraProvider 未初始化");
return;
}
// 确保预览视图可见
previewView.setVisibility(View.VISIBLE);
previewContainer.setBackgroundColor(Color.WHITE);
// 创建Preview用例
Preview preview = new Preview.Builder().build();
preview.setSurfaceProvider(previewView.getSurfaceProvider());
// 创建ImageCapture用例
imageCapture = new ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.build();
// 创建VideoCapture用例
Recorder recorder = new Recorder.Builder()
.setQualitySelector(QualitySelector.from(Quality.HIGHEST))
.build();
videoCapture = VideoCapture.withOutput(recorder);
CameraSelector cameraSelector = new CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build();
try {
// 解绑所有用例
cameraProvider.unbindAll();
// 绑定生命周期和用例
camera = cameraProvider.bindToLifecycle(
this, cameraSelector, preview, imageCapture, videoCapture);
Log.d(TAG, "相机绑定成功");
// 显示PreviewView
previewContainer.setVisibility(View.VISIBLE);
imageView.setVisibility(View.GONE);
toggleCameraButton.setText("关闭摄像头");
isCameraRunning = true;
} catch (Exception e) {
Log.e(TAG, "绑定失败", e);
Toast.makeText(this, "相机绑定失败", Toast.LENGTH_SHORT).show();
}
}
private void stopCamera() {
if (isRecording) {
stopRecording();
}
if (cameraProvider != null) {
cameraProvider.unbindAll();
camera = null;
imageCapture = null;
videoCapture = null;
// 预览区域变为黑色
previewContainer.setVisibility(View.VISIBLE);
previewView.setVisibility(View.INVISIBLE);
previewContainer.setBackgroundColor(Color.BLACK);
toggleCameraButton.setText("打开摄像头");
isCameraRunning = false;
Log.d(TAG, "相机已关闭");
}
}
private void takePhoto() {
if (imageCapture == null) {
return;
}
File photoFile = createImageFile();
if (photoFile == null) {
Toast.makeText(this, "创建照片文件失败", Toast.LENGTH_SHORT).show();
return;
}
ImageCapture.OutputFileOptions outputFileOptions =
new ImageCapture.OutputFileOptions.Builder(photoFile).build();
imageCapture.takePicture(
outputFileOptions,
ContextCompat.getMainExecutor(this),
new ImageCapture.OnImageSavedCallback() {
@Override
public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
String savedPath = photoFile.getAbsolutePath();
Log.d(TAG, "照片保存成功: " + savedPath);
runOnUiThread(() -> {
playerView.setVisibility(View.GONE);
imageView.setVisibility(View.VISIBLE);
Bitmap bitmap = BitmapFactory.decodeFile(savedPath);
imageView.setImageBitmap(bitmap);
Toast.makeText(MainActivity.this, "拍照成功", Toast.LENGTH_SHORT).show();
});
}
@Override
public void onError(@NonNull ImageCaptureException exception) {
Log.e(TAG, "拍照失败: " + exception.getMessage(), exception);
runOnUiThread(() -> {
Toast.makeText(MainActivity.this, "拍照失败: " + exception.getMessage(),
Toast.LENGTH_SHORT).show();
});
}
});
}
private void startRecording() {
if (videoCapture == null || isRecording) {
return;
}
File videoFile = createVideoFile();
if (videoFile == null) {
Toast.makeText(this, "创建视频文件失败", Toast.LENGTH_SHORT).show();
return;
}
FileOutputOptions fileOutputOptions = new FileOutputOptions.Builder(videoFile).build();
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.RECORD_AUDIO},
REQUEST_CAMERA_PERMISSION);
return;
}
recording = videoCapture.getOutput()
.prepareRecording(this, fileOutputOptions)
.withAudioEnabled()
.start(ContextCompat.getMainExecutor(this), event -> {
if (event instanceof VideoRecordEvent.Start) {
runOnUiThread(() -> {
isRecording = true;
openViewButton.setText("停止录制");
Toast.makeText(this, "开始录制", Toast.LENGTH_SHORT).show();
});
} else if (event instanceof VideoRecordEvent.Finalize) {
if (!((VideoRecordEvent.Finalize) event).hasError()) {
String savedPath = videoFile.getAbsolutePath();
Log.d(TAG, "视频保存成功: " + savedPath);
runOnUiThread(() -> {
setupVideoPlayer(Uri.fromFile(videoFile));
});
} else {
Log.e(TAG, "视频录制出错: " +
((VideoRecordEvent.Finalize) event).getError());
}
runOnUiThread(() -> {
isRecording = false;
openViewButton.setText("开始录制");
});
}
});
}
private void stopRecording() {
if (recording != null) {
recording.stop();
recording = null;
}
}
private void setupVideoPlayer(Uri videoUri) {
imageView.setVisibility(View.GONE);
playerView.setVisibility(View.VISIBLE);
player.setMediaItem(MediaItem.fromUri(videoUri));
player.prepare();
player.setPlayWhenReady(false);
playerView.setOnClickListener(v -> {
if (player.isPlaying()) {
player.pause();
} else {
player.setPlayWhenReady(true);
player.seekTo(0);
}
});
}
private File createImageFile() {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES);
try {
return File.createTempFile(
imageFileName, ".jpg", storageDir);
} catch (IOException e) {
Log.e(TAG, "创建照片文件失败", e);
return null;
}
}
private File createVideoFile() {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
String videoFileName = "VIDEO_" + timeStamp + "_";
File storageDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES);
try {
return File.createTempFile(
videoFileName, ".mp4", storageDir);
} catch (IOException e) {
Log.e(TAG, "创建视频文件失败", e);
return null;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
cameraExecutor.shutdown();
if (cameraProvider != null) {
cameraProvider.unbindAll();
cameraProvider.shutdown();
}
if (player != null) {
player.release();
}
}
}在此基础上增加一个删除按钮,用于删除照片和视频