关于startActivityForResult() G了这件事
就很奇怪
register.setOnClickListener(view -> {
startActivityForResult(new Intent(MainActivity.this,SecondActivity.class),1);
});
就一个简单的带返回数据的请求启动,结果…
startActivityForResult(new Intent(MainActivity.this,SecondActivity.class),1);
被弃用了?
先来看看可爱(失智)的官方怎么说,好像没找到,那就去看看网上那群大佬Android博主咋说。
看了大佬们的一顿操作之后,全是kotlin,焯。整一篇java的供java人们食用
registerForActivityResult()是startActivityForResult()的替代,简化了数据回调的写法
个人理解,之前以startActivityForReslut(Intent intent,int requestCode)这种方式启动Activity,然后在启动的Activity那里重写一个onActivityResult(int requestCode, int resultCode, Intent data)进行回调,官方jio得太复杂了。而且代码冗余,还得通过requestCode单独去判断到底是谁启动的哪个Activity。我们来看看原来的写法:
//启动Activity
{
startActivityForResult(new Intent(One.this,Two.class),1);
}
//然后启动的Activity结束之后的回调
{
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 1){
if (resultCode == RESULT_OK){
if (data!=null){
//进行取数据操作
}
}
}
}
}
emmm,貌似不是很麻烦欸,别急看看官方用的
ActivityResultLauncher<Intent> launcher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
if (result.getResultCode() == RESULT_OK) {
Intent data = result.getData();
if (data != null) {
//进行数据操作
}
}
});
register.setOnClickListener(view -> {
launcher.launch(new Intent(MainActivity.this, SecondActivity.class));
});
怎么说,感觉其实也没省几行代码,registerForActivityResult的两个参数大致为启动类型,桥豆麻袋,我看见了好玩的东西,先上一段官方的代码
val getContent = registerForActivityResult(GetContent()) { uri: Uri? ->
// Handle the returned Uri
}
override fun onCreate(savedInstanceState: Bundle?) {
// ...
val selectButton = findViewById<Button>(R.id.select_button)
selectButton.setOnClickListener {
// Pass in the mime type you'd like to allow the user to select
// as the input
getContent.launch("image/*")
}
}
介玩意儿竟然能直接选图片!
我们来看一下示例
//在onCreate里面先注册一个拍照返回bitmap的启动成员变量
takePhotoBitmapResult = registerForActivityResult(new ActivityResultContracts.TakePicturePreview(), new ActivityResultCallback<Bitmap>() {
@Override
public void onActivityResult(Bitmap result) {
ImageView imageView = getImageView();
imageView.setImageBitmap(result);
//将ImageView放入布局中
addView(imageView);
}
//因为只需要获得预览图
});
/* */
//然后在一个监听事件中调用一下这个
takePhotoBitmapResult.launch(null);
然后就
图片就这么被转换成bitmap了?我的天哪!OH!OH!OH!OH!OH!OH!OH!OH!OH!OH!OH!OH!OH!OH!OH!OH!OH!OH!OH!OH!
妈妈再也不用担心new File() 出来个 permission denied了(上次Android 12 的小米 录音文件授权了也获取不到,Manifests.xml加了
android:requestLegacyExternalStorage="true"
也没用)
但不是说不用授权就可以访问图片和拍照了(想脾持),肯定还是要动态申请权限的,对于registerForActivityResult来说,权限申请啥的也是完全没问题
private ActivityResultLauncher<String> permissionResult = registerForActivityResult(new ActivityResultContracts.RequestPermission(), new ActivityResultCallback<Boolean>() {
@Override
public void onActivityResult(Boolean result) {
if (result) {
Toast.makeText(TakePhoto.this, "您同意了该权限", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(TakePhoto.this, "您拒绝了该权限", Toast.LENGTH_SHORT).show();
}
}
//在这里加入你要请求的权限
});
//获取单个权限
requestPermission.setOnClickListener(view -> {
//获取相机权限
permissionResult.launch(Manifest.permission.CAMERA);
});
//请求多个权限
private ActivityResultLauncher<String[]> permissionsResult = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback<Map<String, Boolean>>() {
@Override
public void onActivityResult(Map<String, Boolean> result) {
//请求完成回调后权限名称和是否同意授权被封装到了一个Map<String,Boolean>中
Set<Map.Entry<String, Boolean>> set = result.entrySet();
for (Map.Entry<String, Boolean> item : set) {
switch (item.getKey()) {
case Manifest.permission.CAMERA: {
showMsg(item, "相机");
break;
}
case Manifest.permission.WRITE_EXTERNAL_STORAGE: {
showMsg(item, "读写");
break;
}
case Manifest.permission.READ_CONTACTS: {
showMsg(item, "读取通讯录");
break;
}
}
}
}
});
演示:
不仅如此,官方还非常给了非常多人性化的接口调用:
Contract | 介绍 |
---|---|
StartActivityForResult | 通用的Contract,不做任何转换,Intent作为输入,ActivityResult作为输出,这也是最常用的一个协定 |
RequestPermission | 用于请求单个权限 |
RequestMultiplePermissions | 用于请求一组权限 |
TakePicturePreview | 调用MediaStore.ACTION_IMAGE_CAPTURE拍照,返回值为Bitmap图片 |
TakePicture | 调用MediaStore.ACTION_IMAGE_CAPTURE拍照,并将图片保存到给定的Uri地址,返回true表示保存成功 |
TakeVideo | 调用MediaStore.ACTION_VIDEO_CAPTURE 拍摄视频,保存到给定的Uri地址,返回一张缩略图 |
PickContact | 从通讯录APP获取联系人 |
GetContent | 提示用选择一条内容,返回一个通过ContentResolver.openInputStream(Uri)访问原生数据的Uri地址(content://形式) 。默认情况下,它增加了Intent.CATEGORY_OPENABLE, 返回可以表示流的内容 |
CreateDocument | 提示用户选择一个文档,返回一个(file:/http:/content:)开头的Uri |
OpenMultipleDocuments | 提示用户选择文档(可以选择多个),分别返回它们的Uri,以List的形式 |
OpenDocumentTree | 提示用户选择一个目录,并返回用户选择的作为一个Uri返回,应用程序可以完全管理返回目录中的文档 |
注意:所有的registerForActivityResult()都必须的Activity生命周期中的onStart()方法前调用,换言之创建ActivityResultLauncher 这种操作全部都只能在onCreate()里面,那么也就意味着 ActivityResultLauncher 类型的所有变量都必须为全局的,如果在按钮或View带的点击监听中取注册一个ActivityResultLauncher ,那么他就是在View点击的时候创建的,就会报错,所以只能在onCreate里面创建,然后在View的监听里进行操作,类似于:
@Override
protected void onCreate(Bundle savedInstanceState) {
// 前面的一些操作
ActivityResultLauncher<Intent> launcher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
if (result.getResultCode() == RESULT_OK) {
Intent data = result.getData();
if (data != null) {
String user = data.getStringExtra("user");
String pwd = data.getStringExtra("pwd");
username.setText(user);
password.setText(pwd);
}
}
});
register.setOnClickListener(view -> {
launcher.launch(new Intent(MainActivity.this, SecondActivity.class));
});
}
合着其实好像以没方便到哪去…(小声bb),可能是我还没发现更方便的操作吧。registerForActivityResult()不止可以使用这些内置的接口,还可以自定义,关于自定义本蒟蒻没有涉及太多,常用的场景官方都已经封装的差不多了。
下面是代码
Activity测试代码:
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.ContentValues;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.provider.ContactsContract;
import android.provider.MediaStore;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import com.bumptech.glide.Glide;
import java.util.List;
import java.util.Map;
import java.util.Set;
@RequiresApi(api = Build.VERSION_CODES.Q)
public class TakePhoto extends AppCompatActivity {
private Button requestPermission;
private Button requestPermissions;
private Button selectPhoto;
private Button takePhotoBitmap;
private Button takePhotoUri;
private Button readContacts;
private Button selectDoc;
private Button selectDocs;
private Button selectTree;
private LinearLayout body;
private Button selectPhotos;
private Button selectMany;
private ActivityResultLauncher<String> permissionResult;
private ActivityResultLauncher<String[]> permissionsResult;
private ActivityResultLauncher<String[]> selectPhotoResult;
private ActivityResultLauncher<String[]> selectPhotosResult;
private ActivityResultLauncher<Void> takePhotoBitmapResult;
private ActivityResultLauncher<Uri> takePhotoResult;
private ActivityResultLauncher<Void> readContactsResult;
private ActivityResultLauncher<String[]> selectTextResult;
private ActivityResultLauncher<String[]> selectTextsResult;
private ActivityResultLauncher<Uri> selectTreeResult;
private ActivityResultLauncher<String[]> selectManyResult;
private Uri uri;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_take_photo);
initView();
initRegisterActivity();
}
private void initRegisterActivity() {
permissionResult = registerForActivityResult(new ActivityResultContracts.RequestPermission(), new ActivityResultCallback<Boolean>() {
@Override
public void onActivityResult(Boolean result) {
if (result) {
Toast.makeText(TakePhoto.this, "您同意了该权限", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(TakePhoto.this, "您拒绝了该权限", Toast.LENGTH_SHORT).show();
}
}
//在这里加入你要请求的权限
});
//为了更好的理解这个回调 没有简写成lambda表达式
permissionsResult = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback<Map<String, Boolean>>() {
@Override
public void onActivityResult(Map<String, Boolean> result) {
Set<Map.Entry<String, Boolean>> set = result.entrySet();
for (Map.Entry<String, Boolean> item : set) {
switch (item.getKey()) {
case Manifest.permission.CAMERA: {
showMsg(item, "相机");
break;
}
case Manifest.permission.WRITE_EXTERNAL_STORAGE: {
showMsg(item, "读写");
break;
}
case Manifest.permission.READ_CONTACTS: {
showMsg(item, "读取通讯录");
break;
}
}
}
}
});
selectPhotoResult = registerForActivityResult(new ActivityResultContracts.OpenDocument(), new ActivityResultCallback<Uri>() {
@Override
public void onActivityResult(Uri result) {
ImageView imageView = getImageView();
addView(imageView);
Glide.with(TakePhoto.this).load(result).into(imageView);
}
});
selectPhotosResult = registerForActivityResult(new ActivityResultContracts.OpenMultipleDocuments(), new ActivityResultCallback<List<Uri>>() {
@Override
public void onActivityResult(List<Uri> result) {
if (result != null && result.size() != 0) {
GridView gridView = new GridView(TakePhoto.this);
gridView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
gridView.setNumColumns(2);
gridView.setAdapter(new MyAdapter(result));
addView(gridView);
Toast.makeText(TakePhoto.this, ""+result.size(), Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(TakePhoto.this, "你没有选择任何图片", Toast.LENGTH_SHORT).show();
}
}
});
takePhotoBitmapResult = registerForActivityResult(new ActivityResultContracts.TakePicturePreview(), new ActivityResultCallback<Bitmap>() {
@Override
public void onActivityResult(Bitmap result) {
ImageView imageView = getImageView();
imageView.setImageBitmap(result);
addView(imageView);
}
//因为只需要获得预览图
});
takePhotoResult = registerForActivityResult(new ActivityResultContracts.TakePicture(), new ActivityResultCallback<Boolean>() {
@Override
public void onActivityResult(Boolean result) {
if (result) {
ImageView imageView = getImageView();
Glide.with(TakePhoto.this).load(uri).into(imageView);
Log.d("takePhotoUri", uri.getPath());
addView(imageView);
} else {
//没有拍照
Toast.makeText(TakePhoto.this, "没有拍摄照片", Toast.LENGTH_SHORT).show();
}
}
});
readContactsResult = registerForActivityResult(new ActivityResultContracts.PickContact(), new ActivityResultCallback<Uri>() {
@Override
public void onActivityResult(Uri result) {
//but为啥还要用ContentResolver...害
if (result != null) {
Cursor query = getContentResolver().query(result, null, null, null);
if (query.moveToFirst()) {
@SuppressLint("Range") String name = query.getString(query.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
@SuppressLint("Range") String number = query.getString(query.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER));
Toast.makeText(TakePhoto.this, "联系人名称:" + name + "\n" + "电话号码:" + number, Toast.LENGTH_SHORT).show();
}
}
}
});
selectTextResult = registerForActivityResult(new ActivityResultContracts.OpenDocument(), new ActivityResultCallback<Uri>() {
@Override
public void onActivityResult(Uri result) {
//我暂时也不知道拿到uri之后应该干些啥坏事
Toast.makeText(TakePhoto.this, result.getPath(), Toast.LENGTH_SHORT).show();
}
});
selectTextsResult = registerForActivityResult(new ActivityResultContracts.OpenMultipleDocuments(), new ActivityResultCallback<List<Uri>>() {
@Override
public void onActivityResult(List<Uri> result) {
//就显示拿了几个吧
Toast.makeText(TakePhoto.this, "" + result.size(), Toast.LENGTH_SHORT).show();
}
});
selectTreeResult = registerForActivityResult(new ActivityResultContracts.OpenDocumentTree(), new ActivityResultCallback<Uri>() {
@Override
public void onActivityResult(Uri result) {
//提示用户选择一个目录,并返回用户选择的作为一个Uri返回,应用程序可以完全管理返回目录中的文档。
Toast.makeText(TakePhoto.this, result.getPath(), Toast.LENGTH_SHORT).show();
}
});
selectManyResult = registerForActivityResult(new ActivityResultContracts.OpenMultipleDocuments(), new ActivityResultCallback<List<Uri>>() {
@Override
public void onActivityResult(List<Uri> result) {
Toast.makeText(TakePhoto.this, "" + result.size(), Toast.LENGTH_SHORT).show();
}
});
}
private void initView() {
requestPermission = (Button) findViewById(R.id.request_permission);
requestPermissions = (Button) findViewById(R.id.request_permissions);
selectPhoto = (Button) findViewById(R.id.select_photo);
selectPhotos = (Button) findViewById(R.id.select_photos);
takePhotoBitmap = (Button) findViewById(R.id.take_photo_bitmap);
takePhotoUri = (Button) findViewById(R.id.take_photo_uri);
readContacts = (Button) findViewById(R.id.read_contacts);
selectDoc = (Button) findViewById(R.id.select_doc);
selectDocs = (Button) findViewById(R.id.select_docs);
selectTree = (Button) findViewById(R.id.select_tree);
selectMany = (Button) findViewById(R.id.select_many);
body = (LinearLayout) findViewById(R.id.body);
//获取单个权限
requestPermission.setOnClickListener(view -> {
permissionResult.launch(Manifest.permission.CAMERA);
});
//获取多个权限
requestPermissions.setOnClickListener(view -> {
//在这里加入你要请求的权限列表,为String[]的形式,和老版本差不多
permissionsResult.launch(new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_CONTACTS});
});
//选择一张图片
selectPhoto.setOnClickListener(view -> {
selectPhotoResult.launch(new String[]{"image/*"});
});
//选择多张图片
selectPhotos.setOnClickListener(view -> {
selectPhotosResult.launch(new String[]{"image/*"});
});
//获得拍照图片预览图
takePhotoBitmap.setOnClickListener(view -> {
takePhotoBitmapResult.launch(null);
});
//拍摄照片并获得Uri
takePhotoUri.setOnClickListener(view -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "测试图片.jpg");
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);
uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
takePhotoResult.launch(uri);
} else {
//写过比这个版本底的拍照获取图片方式QAQ
Toast.makeText(this, "版本太低自行适配滑稽", Toast.LENGTH_SHORT).show();
}
});
//读取联系人
readContacts.setOnClickListener(view -> {
readContactsResult.launch(null);
});
//选择一个文本文件
selectDoc.setOnClickListener(view -> {
selectTextResult.launch(new String[]{"text/plain"});
});
//选择多个文本文件
selectDocs.setOnClickListener(view -> {
selectTextsResult.launch(new String[]{"text/plain"});
});
//选择一个目录
selectTree.setOnClickListener(view -> {
//我也暂时不知道这个目录用来干嘛QAQ
//selectTreeResult.launch();
});
//选择多种文件
selectMany.setOnClickListener(view -> {
selectManyResult.launch(new String[]{"image/*","text/plain"});
});
}
private void addView(View view) {
body.addView(view);
new Handler().postDelayed(() -> {
body.removeView(view);
}, 10000);
}
private void showMsg(Map.Entry<String, Boolean> item, String name) {
if (item.getValue()) {
Toast.makeText(this, name + "权限获取成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, name + "权限获取失败", Toast.LENGTH_SHORT).show();
}
}
//动态生成一个ImageView,用来显示拍照或相册选择出的图片
private ImageView getImageView() {
ImageView imageView = new ImageView(TakePhoto.this);
imageView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
return imageView;
}
//选取多张图片后GridView的适配器
class MyAdapter extends BaseAdapter {
List<Uri> data;
public MyAdapter(List<Uri> data) {
this.data = data;
}
@Override
public int getCount() {
return data.size();
}
@Override
public Object getItem(int i) {
return data.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
@SuppressLint("ViewHolder") View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.grid_view_item, null);
ImageView imageView = v.findViewById(R.id.image_view);
Glide.with(viewGroup.getContext()).load(data.get(i)).into(imageView);
return v;
}
}
}
layout布局文件
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="5dp"
tools:context=".TakePhoto">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center_horizontal"
>
<Button
android:layout_width="300dp"
android:layout_height="wrap_content"
android:text="请求单个权限"
android:id="@+id/request_permission"
/>
<Button
android:layout_width="300dp"
android:layout_height="wrap_content"
android:text="请求多个权限"
android:id="@+id/request_permissions"
/>
<Button
android:layout_width="300dp"
android:layout_height="wrap_content"
android:text="选择一张图片"
android:id="@+id/select_photo"
/>
<Button
android:layout_width="300dp"
android:layout_height="wrap_content"
android:text="选择多张图片"
android:id="@+id/select_photos"
/>
<Button
android:layout_width="300dp"
android:layout_height="wrap_content"
android:text="拍照返回Bitmap"
android:id="@+id/take_photo_bitmap"
/>
<Button
android:layout_width="300dp"
android:layout_height="wrap_content"
android:text="拍照返回Uri"
android:id="@+id/take_photo_uri"
/>
<Button
android:layout_width="300dp"
android:layout_height="wrap_content"
android:text="读取联系人"
android:id="@+id/read_contacts"
/>
<Button
android:layout_width="300dp"
android:layout_height="wrap_content"
android:text="选择一个文档"
android:id="@+id/select_doc"
/>
<Button
android:layout_width="300dp"
android:layout_height="wrap_content"
android:text="选择多个文档"
android:id="@+id/select_docs"
/>
<Button
android:layout_width="300dp"
android:layout_height="wrap_content"
android:text="选择一个目录"
android:id="@+id/select_tree"
/>
<Button
android:layout_width="300dp"
android:layout_height="wrap_content"
android:text="选择多种文件"
android:id="@+id/select_many"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="300dp"
android:id="@+id/body"
android:orientation="vertical"
>
</LinearLayout>
</LinearLayout>
</ScrollView>
GridView的item布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
android:gravity="center">
<androidx.cardview.widget.CardView
android:layout_width="150dp"
android:layout_height="110dp"
app:cardCornerRadius="10dp"
app:cardElevation="0dp"
>
<ImageView
android:layout_width="150dp"
android:layout_height="110dp"
android:id="@+id/image_view"
android:scaleType="centerCrop"
android:layout_gravity="center"
/>
</androidx.cardview.widget.CardView>
</LinearLayout>
最后,附上项目地址:https://gitee.com/thor19/register-for-activity-result.git
Anddroid萌新,有问题欢迎指正,大佬们轻点喷。