• 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. 代码示例

    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
                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();
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    2. ImageUtil部分函数

    public static Bitmap rotateBitmap(Bitmap bitmap, int rotation) {
        if (rotation == 0) {
            return bitmap;
        }
        Matrix matrix = new Matrix();
        matrix.postRotate(rotation);
        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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    三、打开相机获取图像

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

    1. 代码示例

    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 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");
                        // 后续处理逻辑
                    }
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    注意:

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

    2. 修改openCamera()

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

    private String mPhotoPath; // 用于存储文件保存的路径
    
    private void openCamera() {
        if (mCameraLauncher != null) {
            // launch的输入参数是泛型,对应ActivityResultLauncher
            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);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    3. ImageUtil.createImageFile() 代码

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

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    4. 自定义的FileProvider

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

    a. AndroidManifest.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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

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

    b. xml/file_paths 文件

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

    
    <paths xmlns:android="http://schemas.android.com/apk/res/android">
        <external-path
            name="my_images"
            path="." />
    paths>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

    5. 修改 handleCameraResult(ActivityResult result)

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

    private void handleCameraResult(ActivityResult result) {
        if (result.getResultCode() == RESULT_OK) {
            Bitmap bitmap = BitmapFactory.decodeFile(mPhotoPath);
    	    int rotation = ImageUtil.getRotation(mPhotoPath);
    	    Bitmap rotatedBitmap = ImageUtil.rotateBitmap(bitmap, rotation);
    	    // 后续处理逻辑
       	}
    }
    
    
    /** ImageUtil */
    public static int getRotation(String photoPath) {
        int rotationAngle = 0;
        try {
            ExifInterface exif = new ExifInterface(photoPath);
            int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
            switch (orientation) {
                case ExifInterface.ORIENTATION_ROTATE_90:
                    rotationAngle = 90;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    rotationAngle = 180;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_270:
                    rotationAngle = 270;
                    break;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return rotationAngle;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
  • 相关阅读:
    Linux系统下如何使用NiMotionUSBCAN转换器?
    全球无人驾驶大洗牌,百度Apollo Day宣告Robotaxi进入2.0时代
    idea pom导入net.sf.json的jar包失败
    Java集合之LinedList
    ChatGPT 和文心一言哪个更好用?
    webpack之处理js资源之eslint
    如何动态修改 spring aop 切面信息?让自动日志输出框架更好用
    Icon闪烁/设定蜂鸣器响的次数
    Mysql 45讲学习笔记(三十五)优化join语句
    设计模式——工厂模式
  • 原文地址:https://blog.csdn.net/afei__/article/details/133690534