You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android Studio开发文字拍摄App:如何实现相机自动对焦?

解决Android相机自动对焦问题(拍摄文字清晰化)

嘿,我明白你现在的困扰——用系统相机Intent拍出来的照片不对焦,文字根本看不清对吧?咱们一步步来解决这个问题:

为什么当前代码无法自动对焦?

你现在用的MediaStore.ACTION_IMAGE_CAPTURE是调用系统自带的相机App,对焦逻辑完全由那个App控制。有些系统相机默认不会自动对焦到文字,或者只在特定场景触发对焦,而且这个Intent返回的还是缩略图,本身分辨率就低,放大后自然模糊。

方案一:给系统相机Intent添加对焦参数(简单尝试)

虽然不是所有系统相机都支持,但可以先试试给Intent添加对焦模式参数,强制开启自动对焦:

修改你创建Intent的代码,加上这几行:

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// 添加连续自动对焦参数,适合静态物体(比如文字)
intent.putExtra(MediaStore.EXTRA_FOCUS_MODE, Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
// 额外:指定输出文件获取高清照片(默认返回的是缩略图)
File photoFile = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), System.currentTimeMillis() + ".jpg");
Uri photoUri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", photoFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
startActivityForResult(intent, 100);

然后修改onActivityResult,从指定文件加载高清照片:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == 100 && resultCode == RESULT_OK) {
        // 从指定文件加载Bitmap
        File photoDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
        // 这里要注意匹配刚才生成的文件名,或者你可以把文件名存在全局变量里
        File photoFile = new File(photoDir, System.currentTimeMillis() + ".jpg");
        Bitmap bitmap = BitmapFactory.decodeFile(photoFile.getAbsolutePath());
        camView.setImageBitmap(bitmap);
    }
}

注意:使用FileProvider需要在AndroidManifest.xml里配置,避免文件权限问题:

<application>
    ...
    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="${applicationId}.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>
</application>

然后在res/xml目录下创建file_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-files-path name="my_images" path="Pictures" />
</paths>

方案二:用CameraX实现可控的自动对焦(推荐)

如果系统相机的参数不生效,或者你需要更稳定的对焦效果,强烈推荐用Jetpack CameraX——它是Google官方推出的相机组件,兼容Android 5.0+,自动处理各种设备兼容性问题,对焦设置也非常简单。

步骤1:添加CameraX依赖

在你的app/build.gradle(Module级别)里添加依赖:

dependencies {
    def camerax_version = "1.3.0"
    implementation "androidx.camera:camera-core:$camerax_version"
    implementation "androidx.camera:camera-camera2:$camerax_version"
    implementation "androidx.camera:camera-view:$camerax_version"
}

步骤2:修改布局文件

替换原来的ImageView,用CameraX的PreviewView来显示相机预览:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.camera.view.PreviewView
        android:id="@+id/previewView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <Button
        android:id="@+id/camBTN"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="24dp"
        android:text="拍照"/>

    <ImageView
        android:id="@+id/camView"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_alignParentTop="true"
        android:layout_alignParentEnd="true"
        android:layout_margin="16dp"
        android:background="#80000000"/>
</RelativeLayout>

步骤3:修改CameraActivity代码

用CameraX实现自动对焦和拍照:

import android.Manifest;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Environment;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
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.view.PreviewView;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import com.google.common.util.concurrent.ListenableFuture;

import java.io.File;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;

public class CameraActivity extends AppCompatActivity {
    private PreviewView previewView;
    private Button camBtn;
    private ImageView camView;
    private ImageCapture imageCapture;
    private static final int CAMERA_PERMISSION_REQUEST_CODE = 101;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera);

        previewView = findViewById(R.id.previewView);
        camBtn = findViewById(R.id.camBTN);
        camView = findViewById(R.id.camView);

        // 先检查相机权限
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, CAMERA_PERMISSION_REQUEST_CODE);
            return;
        }

        // 启动相机
        startCamera();

        // 拍照按钮点击事件
        camBtn.setOnClickListener(v -> takePhoto());
    }

    private void startCamera() {
        // 获取CameraProvider实例
        ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this);
        Executor mainExecutor = ContextCompat.getMainExecutor(this);

        cameraProviderFuture.addListener(() -> {
            try {
                ProcessCameraProvider cameraProvider = cameraProviderFuture.get();

                // 配置预览用例
                Preview preview = new Preview.Builder().build();
                preview.setSurfaceProvider(previewView.getSurfaceProvider());

                // 配置拍照用例,开启连续自动对焦
                imageCapture = new ImageCapture.Builder()
                        .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
                        .setFocusMode(ImageCapture.FOCUS_MODE_CONTINUOUS_AUTO) // 关键:连续自动对焦
                        .build();

                // 选择后置相机
                CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;

                // 绑定用例到Activity生命周期
                cameraProvider.unbindAll();
                cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture);

            } catch (ExecutionException | InterruptedException e) {
                e.printStackTrace();
                Toast.makeText(this, "相机初始化失败", Toast.LENGTH_SHORT).show();
            }
        }, mainExecutor);
    }

    private void takePhoto() {
        if (imageCapture == null) return;

        // 创建保存照片的文件
        File photoDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
        File photoFile = new File(photoDir, System.currentTimeMillis() + ".jpg");

        // 配置输出选项
        ImageCapture.OutputFileOptions outputOptions = new ImageCapture.OutputFileOptions.Builder(photoFile).build();

        // 执行拍照
        imageCapture.takePicture(outputOptions, ContextCompat.getMainExecutor(this), new ImageCapture.OnImageSavedCallback() {
            @Override
            public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
                // 照片保存成功,加载到小ImageView预览
                Bitmap bitmap = BitmapFactory.decodeFile(photoFile.getAbsolutePath());
                camView.setImageBitmap(bitmap);
                Toast.makeText(CameraActivity.this, "照片已保存到:" + photoFile.getAbsolutePath(), Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onError(@NonNull ImageCaptureException exception) {
                exception.printStackTrace();
                Toast.makeText(CameraActivity.this, "拍照失败:" + exception.getMessage(), Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == CAMERA_PERMISSION_REQUEST_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                startCamera();
            } else {
                Toast.makeText(this, "需要相机权限才能使用拍照功能", Toast.LENGTH_SHORT).show();
                finish();
            }
        }
    }
}

这个方案的优势在于:

  • 稳定的自动对焦FOCUS_MODE_CONTINUOUS_AUTO会让相机持续对焦,确保文字清晰
  • 高清照片:直接保存全分辨率照片,不会像系统Intent那样返回缩略图
  • 兼容性强:CameraX自动适配不同Android版本和设备,不用自己处理复杂的相机API逻辑

最后总结

如果只是快速尝试,先给系统Intent添加对焦参数并指定输出文件;如果需要长期稳定的效果,果断切换到CameraX,这也是现在Android相机开发的主流方案。

内容的提问来源于stack exchange,提问作者S.Koren

火山引擎 最新活动