目录
对于手机拍照的APP而言,有两种实现方式,一种是借助系统相机来拍摄照片,另一种是通过Camera工具联合表面视图SurfaceView自行规划编码细节。两者有各自的优缺点,可以按需选择。
使用系统相机拍摄照片
利用系统相机拍摄照片有其优势以及缺点,具体说明如下:
优点:
开发简单:只需要几行代码就可以启动系统相机应用,无需处理复杂的相机操作。
稳定性高:系统相机经过长期测试和优化,通常比自定义相机更稳定。
功能丰富:系统相机通常提供多种拍摄模式、滤镜和设置,用户熟悉且可以直接使用。
兼容性好:不同设备上的系统相机会处理硬件和驱动的差异,避免了兼容性问题。
缺点:
定制性差:无法自定义相机界面和功能,只能使用系统提供的界面。
依赖系统:如果设备上没有相机应用,或者相机应用出现故障,则无法使用。
返回数据可能受限:返回的图片可能是缩略图或者经过压缩的图片(如果没有指定输出文件)。
获取系统相机拍摄的照片有两种方式,一个是获取缩略图,另一个是保存拍摄的照片后读取该照片,此时获取的照片为原始图片。示例代码如下,在布局文件中添加两个按钮以及一个文本视图、一个图片视图。
部分Java代码如下,主要看两个活动结果启动器的内容以及方法takeOriginalPhoto的内容,第一个启动器返回的是一个Bitmap类型的数据,可以直接将Bitmap赋给ImageView;第二个启动器返回的是一个布尔类型,表示是否拍照成功,若成功则可以通过在takeOriginalPhoto方法中获取的Uri来打开已保存的图片并将其转化为Bitmap类型赋给ImageView,示例代码中的图片保存在系统相册中,可以在系统相册中找到拍摄的照片。如果只是需要简单的图片那么第一种方式已经足够了,代码量少且简单实现。
public class SnapBySystemCameraActivity extends AppCompatActivity implements View.OnClickListener {
private final String TAG = "SnapBySystemCamera";
private Button button_Snap, button_Snap_Origin;
private TextView textView;
private ImageView imageView;
private ActivityResultLauncher activityResultLauncher_Original; //活动结果启动器(原始图)
private ActivityResultLauncher activityResultLauncher_Thumbnail; //活动结果启动器(缩略图)
private Uri mImageUri;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_snap_by_system_camera);
initView();
}
//初始化页面视图
private void initView() {
button_Snap = findViewById(R.id.button_Snap);
button_Snap_Origin = findViewById(R.id.button_Snap_Origin);
textView = findViewById(R.id.textView);
imageView = findViewById(R.id.imageView);
// 注册一个善后工作的活动结果启动器,准备打开拍照界面(返回缩略图)
activityResultLauncher_Thumbnail = registerForActivityResult(new ActivityResultContracts.TakePicturePreview(),
bitmap_Thumbnail -> imageView.setImageBitmap(bitmap_Thumbnail));
// 注册一个善后工作的活动结果启动器,准备打开拍照界面(返回原始图)
activityResultLauncher_Original = registerForActivityResult(new ActivityResultContracts.TakePicture(),
if_success -> {
if (if_success) {
try (InputStream inputStream = getContentResolver().openInputStream(mImageUri)) {
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
textView.setText("原始图拍照结果如下:");
imageView.setImageBitmap(bitmap); // 设置图像视图的位图对象
} catch (Exception e) {
Log.d(TAG, "onActivityResult: " + e.getMessage());
}
}
});
button_Snap.setOnClickListener(this);
button_Snap_Origin.setOnClickListener(this);
}
@Override
public void onClick(View view) {
if (view.getId() == R.id.button_Snap) {
Log.d(TAG, "onClick: button_Snap");
activityResultLauncher_Thumbnail.launch(null);
} else if (view.getId() == R.id.button_Snap_Origin) {
Log.d(TAG, "onClick: button_Snap_Origin");
takeOriginalPhoto();
}
}
// 拍照时获取原始图片
private void takeOriginalPhoto() {
// Android10开始必须由系统自动分配路径,同时该方式也能自动刷新相册
ContentValues values = new ContentValues();
Calendar calendar = Calendar.getInstance();
String time = String.format("%s%s%s%s", calendar.get(Calendar.HOUR_OF_DAY),
calendar.get(Calendar.MINUTE), calendar.get(Calendar.SECOND), calendar.get(Calendar.MILLISECOND));
// 指定图片文件的名称
values.put(MediaStore.Images.Media.DISPLAY_NAME, "photo_" + time);
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); // 类型为图像
// 通过内容解析器插入一条外部内容的路径信息
mImageUri = getContentResolver().insert(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
activityResultLauncher_Original.launch(mImageUri);
}
}
效果图如下,第一张为刚进入页面,第二张为拍摄获取缩略图,第三张为拍摄获取原始图。可以看到两张照片之间大小是不一致的,第一张若放大到与第二张一样的宽高,它的效果会更加模糊。



使用Camera和SurfaceView自行实现
利用Camera以及SurfaceView实现拍照的优缺点如下说明:
优点:
高度定制:可以完全自定义相机界面和拍摄流程,满足特定业务需求。
控制精细:可以控制相机的各种参数(如对焦、曝光、闪光灯等)和拍摄过程。
无缝体验:可以将相机功能完全集成到应用中,提供一致的用户体验。
缺点:
开发复杂:需要处理相机的打开、预览、参数设置、拍照、释放等整个生命周期,代码量大。
兼容性挑战:不同设备的相机硬件和驱动可能有差异,需要处理各种兼容性问题。
稳定性风险:自行管理的相机可能因为操作不当导致崩溃或性能问题。
功能实现成本高:如变焦、美颜、滤镜等功能都需要自行开发或集成第三方库。
下面的示例代码是比较简单的,若想要学习更加高深的可以另找其他厉害的博主,作者也才刚学不久。示例代码如下,在布局文件中添加一个按钮、一个SurfaceView以及一个ImageView。
部分Java代码如下,主要看方法initCamera以及按钮监听器中的逻辑处理代码。
public class CameraWithSurfaceViewActivity extends AppCompatActivity {
private SurfaceView surfaceView;
private Button button_Snap;
private ImageView imageView;
private SurfaceHolder surfaceHolder;
private Camera camera;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera_with_surface_view);
initView();
}
private void initView() {
button_Snap = findViewById(R.id.button_Snap);
imageView = findViewById(R.id.imageView);
surfaceView = findViewById(R.id.surfaceView);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
initCamera();
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int i1, int i2) {}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {}
});
button_Snap.setOnClickListener(view -> camera.takePicture(null, null, new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] bytes, Camera camera) {
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
imageView.setImageBitmap(bitmap);
}
}));
}
private void initCamera() {
// 初始化 Camera,0 通常代表后置摄像头
camera = Camera.open(0);
// 调整摄像头预览角度,例如设置为90度
camera.setDisplayOrientation(90);
// 设置SurfaceView不维护自己的缓冲区
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
// 将Camera的预览输出与SurfaceView关联
try {
camera.setPreviewDisplay(surfaceHolder);
} catch (IOException e) {
Log.d(TAG, "initializeCamera: " + e.getMessage());
}
camera.startPreview(); // 开始预览
}
}
效果图如下,图片中第一个图片为SurfaceView,第二个为ImageView。

浙公网安备 33010602011771号