当使用了 AndroidX 后,发现 `startActivityForResult()` 标记为过时了,而是推荐我们使用 `registerForActivityResult()` 函数。
`registerForActivityResult()` 函数是 Android 中用于启动 Activity 结果回调的新方式。这个函数的目的是简化在 Activity 和 Fragment 之间进行启动其他 Activity 并接收结果的过程,取代了传统的 `startActivityForResult()` 和 `onActivityResult()` 方法。
使用 `registerForActivityResult()` 函数,您可以更容易地管理 Activity 结果的处理,使代码更清晰和模块化。
AndroidX 介绍:
AndroidX 是 Android 中的一个开发库,旨在简化 Android 应用程序的开发和维护。AndroidX 提供了一组替代 Android Support Library 的库,它们包括了新的功能、改进的性能和更好的兼容性,以便开发人员更轻松地构建现代化、高质量的 Android 应用程序。
主要包括:
- 新功能和改进: AndroidX 引入了许多新的功能和改进,包括更好的 Jetpack 组件、性能优化、Kotlin 支持、Android 架构组件等,以提高开发效率和用户体验。
- 向后兼容性: AndroidX 专注于提供向后兼容性,以确保应用程序在不同 Android 版本上能够稳定运行。这意味着即使您的应用使用了 AndroidX 库,也可以在较旧的 Android 版本上运行。
- Jetpack 组件: AndroidX 包括了 Android Jetpack 组件,这是一组库和工具,旨在简化 Android 应用程序的常见开发任务,例如导航、数据存储、UI 构建、生命周期管理等。Jetpack 组件的目标是提高应用程序的可维护性、稳定性和性能。
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();
}
}
}
}
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;
}
}
因为使用的是系统相机应用,所以也不需要额外申请相机权限了。
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");
// 后续处理逻辑
}
}
}
}
}
注意:
由于Bundle本身能够携带的数据大小限制,上面这种方式获取的相机图像分辨率通常很低。如果希望能够获取较大分辨率的图像,则可以先将相机图像保存到本地,再通过文件路径加载。具体方式如下:
在打开相机的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);
}
}
}
创建一个可写的文件即可。
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;
}
上述代码中还使用到了一个 `FileProvider.getUriForFile()` 方法,作为四大组件之一,也是需要在 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>
authorities 通常可以用 `包名+.provider`。
在 res 文件夹下面创建一个 xml 子文件夹,并新建 file_paths.xml 文件(文件名可以修改,和AndroidManifest.xml中对应上就行),内容如下:
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="my_images"
path="." />
paths>
关于FileProvider的使用这里不过多介绍了,不清楚的可以查阅其他资料。
这里就不再需要通过 ActivityResult 获取数据了,而是从之前存储的 `mPhotoPath` 中获取。
private void handleCameraResult(ActivityResult result) {
if (result.getResultCode() == RESULT_OK) {
Bitmap bitmap = BitmapFactory.decodeFile(mPhotoPath);
// 后续处理逻辑
}
}