Android 使用 registerForActivityResult() 打开系统相册或相机获取图像

一、简介

当使用了 AndroidX 后,发现 `startActivityForResult()` 标记为过时了,而是推荐我们使用 `registerForActivityResult()` 函数。

`registerForActivityResult()` 函数是 Android 中用于启动 Activity 结果回调的新方式。这个函数的目的是简化在 Activity 和 Fragment 之间进行启动其他 Activity 并接收结果的过程,取代了传统的 `startActivityForResult()``onActivityResult()` 方法。

使用 `registerForActivityResult()` 函数,您可以更容易地管理 Activity 结果的处理,使代码更清晰和模块化。

AndroidX 介绍:

AndroidX 是 Android 中的一个开发库,旨在简化 Android 应用程序的开发和维护。AndroidX 提供了一组替代 Android Support Library 的库,它们包括了新的功能、改进的性能和更好的兼容性,以便开发人员更轻松地构建现代化、高质量的 Android 应用程序。

主要包括:

  1. 新功能和改进: AndroidX 引入了许多新的功能和改进,包括更好的 Jetpack 组件、性能优化、Kotlin 支持、Android 架构组件等,以提高开发效率和用户体验。
  2. 向后兼容性: AndroidX 专注于提供向后兼容性,以确保应用程序在不同 Android 版本上能够稳定运行。这意味着即使您的应用使用了 AndroidX 库,也可以在较旧的 Android 版本上运行。
  3. Jetpack 组件: AndroidX 包括了 Android Jetpack 组件,这是一组库和工具,旨在简化 Android 应用程序的常见开发任务,例如导航、数据存储、UI 构建、生命周期管理等。Jetpack 组件的目标是提高应用程序的可维护性、稳定性和性能。

二、打开相册获取图像

1. 代码示例

java 复制代码
public class MainActivity extends AppCompatActivity {
    
    private ActivityResultLauncher<String> mGalleryLauncher;
    
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 需要事先注册
        mGalleryLauncher = registerForActivityResult(
                // 获取内容,回调函数的参数将是一个URI
                new ActivityResultContracts.GetContent(),
                // 回调函数
                this::handleGalleryResult);
    }
    
    private void openGallery() {
        if (mGalleryLauncher != null) {
            // launch的输入参数是泛型,对应ActivityResultLauncher<String>
            mGalleryLauncher.launch("image/*");
        }
    }
    
    private void handleGalleryResult(Uri imageUri) {
        if (imageUri != null) {
            try {
                Bitmap selectedBitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), imageUri);
                int rotation = ImageUtil.getRotation(this, imageUri);
                Bitmap rotatedBitmap = ImageUtil.rotateBitmap(selectedBitmap, rotation);
                // 后续处理逻辑
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

2. ImageUtil部分函数

java 复制代码
public static Bitmap rotateBitmap(Bitmap bitmap, int rotation) {
    if (rotation == 0) {
        return bitmap;
    }
    return rotateResizeBitmap(bitmap, rotation, 1f);
}

public static Bitmap rotateResizeBitmap(Bitmap source, float angle, float scale) {
    Matrix matrix = new Matrix();
    matrix.postRotate(angle);
    if (scale != 1f) {
        matrix.setScale(scale, scale);
    }
    return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix, true);
}

public static int getRotation(Context context, Uri photoUri) {
    if (photoUri == null) {
        return 0;
    }
    ExifInterface ei;
    try {
        InputStream inputStream = context.getContentResolver().openInputStream(photoUri);
        ei = new ExifInterface(inputStream);
    } catch (IOException e) {
        return 0;
    }
    int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
    switch (orientation) {
        case ExifInterface.ORIENTATION_ROTATE_90:
            return 90;
        case ExifInterface.ORIENTATION_ROTATE_180:
            return 180;
        case ExifInterface.ORIENTATION_ROTATE_270:
            return 270;
        default:
            return 0;
    }
}

三、打开相机获取图像

因为使用的是系统相机应用,所以也不需要额外申请相机权限了。

1. 代码示例

java 复制代码
public class MainActivity extends AppCompatActivity {
    
    private ActivityResultLauncher<Intent> mCameraLauncher;
    
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 需要事先注册
        mCameraLauncher = registerForActivityResult(
                // 回调函数的参数将是一个ActivityResult
                new ActivityResultContracts.StartActivityForResult(),
                // 回调函数
                this::handleCameraResult);
    }
    
    private void openCamera() {
        if (mCameraLauncher != null) {
            // launch的输入参数是泛型,对应ActivityResultLauncher<Intent>
            Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            // 确保有相机应用可用
            if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
                mCameraLauncher.launch(takePictureIntent);
            }
        }
    }
    
    private void handleCameraResult(ActivityResult result) {
        if (result.getResultCode() == RESULT_OK) {
            Intent data = result.getData();
            if (data != null) {
                Bundle extras = data.getExtras();
                if (extras != null) {
                    Uri uri = data.getData();
                    Bitmap imageBitmap = (Bitmap) extras.get("data");
                    // 后续处理逻辑
                }
            }
        }
    }
}

注意:

由于Bundle本身能够携带的数据大小限制,上面这种方式获取的相机图像分辨率通常很低。如果希望能够获取较大分辨率的图像,则可以先将相机图像保存到本地,再通过文件路径加载。具体方式如下:

2. 修改openCamera()

在打开相机的Intent中添加一个文件输出的Uri,告诉相机需要保存到指定位置。

java 复制代码
private String mPhotoPath; // 用于存储文件保存的路径

private void openCamera() {
    if (mCameraLauncher != null) {
        // launch的输入参数是泛型,对应ActivityResultLauncher<Intent>
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        // 确保有相机应用可用
        if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
            // 创建一个用于存储拍照结果的文件
            File photoFile = ImageUtil.createImageFile(this);
            mPhotoPath = photoFile.getAbsolutePath();
            Uri photoUri = FileProvider.getUriForFile(this,
                    "com.afei.demo.provider", // 对应 AndroidManifest.xml 中的声明
                    photoFile);
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
            mCameraLauncher.launch(takePictureIntent);
        }
    }
}

3. ImageUtil.createImageFile() 代码

创建一个可写的文件即可。

java 复制代码
public static File createImageFile(Context context) {
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date(System.currentTimeMillis()));
    String imageFileName = "JPEG_" + timeStamp + "_";
    File storageDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
    if (!storageDir.exists()) {
        storageDir.mkdirs();
    }
    File imageFile = null;
    try {
        imageFile = File.createTempFile(
                imageFileName,  /* 文件名 */
                ".jpg",         /* 文件扩展名 */
                storageDir      /* 存储目录 */
        );
        imageFile.setWritable(true);
    } catch (IOException e) {
        e.printStackTrace();
    }
    return imageFile;
}

4. 自定义的FileProvider

上述代码中还使用到了一个 `FileProvider.getUriForFile()` 方法,作为四大组件之一,也是需要在 AndroidManifest.xml 文件中声明和使用的。

a. AndroidManifest.xml 文件中
xml 复制代码
<application>
    ...
    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="com.afei.demo.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>
</application>

authorities 通常可以用 `包名+.provider`

b. xml/file_paths 文件

res 文件夹下面创建一个 xml 子文件夹,并新建 file_paths.xml 文件(文件名可以修改,和AndroidManifest.xml中对应上就行),内容如下:

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

关于FileProvider的使用这里不过多介绍了,不清楚的可以查阅其他资料。

5. 修改 handleCameraResult(ActivityResult result)

这里就不再需要通过 ActivityResult 获取数据了,而是从之前存储的 `mPhotoPath` 中获取。

java 复制代码
private void handleCameraResult(ActivityResult result) {
    if (result.getResultCode() == RESULT_OK) {
        Bitmap bitmap = BitmapFactory.decodeFile(mPhotoPath);
        // 后续处理逻辑
    }
}
相关推荐
松仔log5 小时前
Java中级——组合和继承
android·java·开发语言
我命由我123458 小时前
Jetpack Compose - MaterialExpressiveTheme 与 MaterialTheme、ColorScheme
android·java·开发语言·java-ee·kotlin·android jetpack·android runtime
lilian2339 小时前
Harmony os 技术实战|拼豆制图06:收藏 ID、生成记录与重启恢复怎么不打架
android·java·数据库·harmonyos
2601_9557596210 小时前
ClaudeAPI成本中心与业务标签设计指南
android·java·数据库
余衫马10 小时前
2026最新版!Android Studio 极速安装与配置指南
android·ide·android studio
AFinalStone10 小时前
Android 7系统异常问题排查(五)Framework层(下)—System Server崩溃
android·tombstone·系统异常
AFinalStone10 小时前
Android 7系统异常问题排查(八)系统追踪—Trace机制与性能诊断
android·系统异常
极客猴子14 小时前
Android录音转写频繁卡顿?多款APP长时间会议场景稳定性实测
android·人工智能·智能手机·飞书
Kapaseker14 小时前
Boolean 变量到底该怎么命名?
android·kotlin
AFinalStone14 小时前
Android 7系统异常问题排查(六)应用异常(上)—ANR机制全解
android·系统异常