1 繁琐的 Android 拍照
Android 中的拍照是一件很繁琐的事情,因为它必须使用 Intent,并通过 startActivityForResult 方法去启动相机,完成后需要在 onActivityResult 中接受回调,获得拍照的结果,也就是说拍照和拍照的回调结果是隔离的。我们看下一段伪代码。
1private File photoFile = createFile();
2
3onClick.. goToPhoto
4
5public static void goToPhoto(Activity activity,File file) {
6 Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
7 intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
8 activity.startActivityForResult(intent, ConstantUtil.TAKE_PHOTOS);
9}
10
11
12@Override
13 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
14 switch (requestCode) {
15 case ConstantUtil.TAKE_PHOTOS:
16 if (photoFile != null && photoFile.exists()) {
17
18 }
19 break;
20 }
21 }
复制代码
由于拍照和取结果是异步的,所以我们需要在调用拍照的地方,先声明一个 file 成员变量,因为 onActivityResult 中是无法拿到我们拍的照片的路径的。
2 传统的 Android 拍照方式
我们概括一下,传统的拍照方式(不考虑高版本适配)
声明一个 file 成员,用来记录拍的照片的存储路径
通过 Intent 发起拍照
在 onActivityResult 中获取拍照的回调(注意这里并没有照片地址,需要使用前面的 file)
可能你会说,这也不算麻烦啊。
是的,如果是在 Activity 里,是不算太麻烦。
如果放到 fragment 中呢?也还好。我们可以使用 fragment 的 startActivityForResult 去拍照。
但是!!如果业务场景是在一个列表中,item 中需要进行拍照。那就比较恶心了。
为什么麻烦呢,我来解释一下。
由于拍照的回调和拍照是隔离的,也就是说我们只能在onActivityResult,也就是Activity或者Fragment中才能拿到拍照的结果。而我们拍照的地方是在Adapter里,即使我们可以通过接口把拍照的行为放到Activity或Fragment中处理,但是一样是隔离的,我们无法关联拍照和拍照结果。
这意味着,你在列表里的某个item发起拍照,拍照完成后,你根本不知道是这个照片要应用到哪个item上。你可以通过复杂的回调来解决。总之,你可能需要很麻烦的处理,才能友好的处理。
省省吧!!!
今天我就给你介绍一种不一样的拍照方法。
3Android6.0 版本适配之动态权限
明明是在说拍照,为什么来了个版本适配。做过 Android 动态权限适配的人都知道:和拍照类似,动态权限的申请必须由 Activty 或 Fragment 发起,权限的授予结果必须在 Activity 或者 Fragment 的 onRequestPermissionsResult 回调方法中获取。
同样的问题,申请和结果回调是隔离的,如果我们在一个普通类中发起权限申请,我们是无法在这个类中获取回调结果的。当然,你可以定义一个回调,把activity中的结果回调过来。是的这样可以解决问题。但是有没有更好的方式呢。当然。
前面我们有说到 Fragment 也可以用来调起拍照、权限申请。那么我们是否可以利用一个 Fragment 去作为一个承载来完成动作与回调的同步呢。当然可以。Github 有很多开源的动态权限工具类,如 RxPermission,AndPermission,他们使用的都是同一种思想。
我们在Activity中添加一个空的Fragment,用这个Fragment来发起权限申请,授权回调同样会回调给这个Fragment。然后我们通过接口将这个结果回调出来。从而实现申请和结果回调聚合在一起。
4PhotoTaker
好了,回到我们今天的主题。话不多说,你明白这种巧妙的思想之后,你也就能理解我们的拍照 PhotoTaker 了。直接上代码。
1public class TakePhotoFragment extends Fragment {
2 private PhotoTaker photoTaker;
3
4 public void bindRxPhotoTaker(PhotoTaker photoTaker) {
5 this.photoTaker = photoTaker;
6 }
7
8 @Override
9 public void onCreate(@Nullable Bundle savedInstanceState) {
10 super.onCreate(savedInstanceState);
11 setRetainInstance(true);
12 }
13
14 @Override
15 public void onActivityResult(int requestCode, int resultCode, Intent data) {
16 super.onActivityResult(requestCode, resultCode, data);
17 photoTaker.onActivityResult(requestCode, resultCode, data);
18 }
19}
复制代码
我们的 Fragment 很简单,就是用来发送和接收拍照结果,将结果回调给 PhotoTaker。然后我们看下 PhotoTaker 的代码。
1
2 * android快速实现拍照的方式
3 */
4public class PhotoTaker {
5 private static final String TAG = "PhotoTaker";
6 private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMddmmss");
7 public static int RESULT_TAKE_PHOTO = 1;
8 public static int RESULT_PEEK_ALBUM = 2;
9
10 private TakePhotoFragment takePhotoFragment;
11 private BaseDialog dialog;
12 private FragmentActivity activity;
13 private String savePhotoPath;
14 private Callback callback;
15
16 public PhotoTaker(FragmentActivity activity, Callback callback) {
17 this.activity = activity;
18 this.callback = callback;
19 takePhotoFragment = getRxTakePhotoFragment(activity);
20 takePhotoFragment.bindRxPhotoTaker(this);
21 }
22
23 private TakePhotoFragment getRxTakePhotoFragment(FragmentActivity activity) {
24 TakePhotoFragment fragment = findRxPermissionsFragment(activity);
25 boolean isNewInstance = fragment == null;
26 if (isNewInstance) {
27 fragment = new TakePhotoFragment();
28 FragmentManager fragmentManager = activity.getSupportFragmentManager();
29 fragmentManager
30 .beginTransaction()
31 .add(fragment, TAG)
32 .commitAllowingStateLoss();
33 fragmentManager.executePendingTransactions();
34 }
35 return fragment;
36 }
37
38 private TakePhotoFragment findRxPermissionsFragment(FragmentActivity activity) {
39 return (TakePhotoFragment) activity.getSupportFragmentManager().findFragmentByTag(TAG);
40 }
41
42 public void show() {
43 if (dialog == null) {
44 dialog = new BaseDialog(activity);
45 dialog.setGravity(Gravity.BOTTOM);
46 dialog.setContentView(R.layout.dialog_take_photo);
47 dialog.setLayout(-1, -2);
48 dialog.findViewById(R.id.tv_take_photo).setOnClickListener(new View.OnClickListener() {
49 @Override
50 public void onClick(View v) {
51 dialog.dismiss();
52 takePhoto();
53 }
54 });
55 dialog.findViewById(R.id.tv_peek_album).setOnClickListener(new View.OnClickListener() {
56 @Override
57 public void onClick(View v) {
58 dialog.dismiss();
59 peekImageFromAlbum(takePhotoFragment, RESULT_PEEK_ALBUM);
60 }
61 });
62 dialog.findViewById(R.id.tv_cancel).setOnClickListener(new View.OnClickListener() {
63 @Override
64 public void onClick(View v) {
65 dialog.dismiss();
66 }
67 });
68 }
69 dialog.show();
70 }
71
72
73 * 从相册中选择照片
74 */
75 private void peekImageFromAlbum(Fragment fragment, int resultCode) {
76 Intent intent = new Intent(Intent.ACTION_PICK, null);
77 intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
78 fragment.startActivityForResult(intent, resultCode);
79 }
80
81 private void takePhoto() {
82
83 RxPermissions rxPermissions = new RxPermissions(activity);
84 rxPermissions.request(Manifest.permission.CAMERA)
85 .subscribe(new Action1<Boolean>() {
86 @Override
87 public void call(Boolean granted) {
88 if (granted) {
89 savePhotoPath = createFile(activity);
90 openCamera(takePhotoFragment, savePhotoPath, RESULT_TAKE_PHOTO);
91 }
92 }
93 });
94 }
95
96
97 * 打开相机,兼容7.0+
98 */
99 private void openCamera(Fragment fragment, String outputPath, int resultCode) {
100
101 File outputFile = new File(outputPath);
102 FileUtil.createFile(outputFile);
103 Uri imageUri;
104 if (
105 Build.VERSION.SDK_INT < 24) {
106 imageUri = Uri.fromFile(outputFile);
107 } else {
108
109 imageUri =
110 FileProvider.getUriForFile(fragment.getContext(), "org.jaaksi.photoker.demo.fileprovider",
111 outputFile);
112 }
113
114 Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
115 intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
116 fragment.startActivityForResult(intent, resultCode);
117 }
118
119
120 * data/packagename/files/
121 */
122 public static String createFile(Context context) {
123 return context.getExternalFilesDir(null) + "/" + DATE_FORMAT.format(new Date()) + ".jpg";
124 }
125
126 public void onActivityResult(int requestCode, int resultCode, Intent data) {
127 if (resultCode != Activity.RESULT_OK) return;
128
129 if (requestCode == PhotoTaker.RESULT_TAKE_PHOTO) {
130 callback.onResult(savePhotoPath);
131 } else if (requestCode == PhotoTaker.RESULT_PEEK_ALBUM) {
132 if (data != null) {
133 Uri selectedImage = data.getData();
134 if (selectedImage == null) return;
135 String[] filePathColumns = { MediaStore.Images.Media.DATA };
136 Cursor cursor = activity.getContentResolver()
137 .query(selectedImage, filePathColumns, null, null, null);
138 if (cursor != null) {
139 cursor.moveToFirst();
140 int columnIndex = cursor.getColumnIndex(filePathColumns[0]);
141 String imagePath = cursor.getString(columnIndex);
142 callback.onResult(imagePath);
143 cursor.close();
144 }
145 }
146 }
147 }
148
149 public interface Callback {
150
151 * @param filePath 图片路径
152 */
153 void onResult(String filePath);
154 }
155}
复制代码
代码很简单,支持拍照和从系统相册选取图片,适配高版本拍照。下面我们看下使用示例。老规矩,直接上代码。
1public void onClick(View view) {
2 new PhotoTaker(this, new PhotoTaker.Callback() {
3 @Override
4 public void onResult(String filePath) {
5 imageview.setImageURI(Uri.fromFile(new File(filePath)));
6 }
7 }).show();
8 }
复制代码
是不是很方便。通过这个示例,简单说一下。通过直接 new PhotoTaker 来创建 PhotoTaker。PhotoTaker 有两个参数,Activity,Callback。Callback 的回调方法 onResult,将拍照或者从相册选择的图片地址回调出来。这样我们就可以直接处理图片。在列表的 adapter 中是一样的使用方式。
最后附上 demo地址
作者介绍:
公台(企业代号名),贝壳找房 Android 工程师,目前负责新房 B 端 Android 研发工作。
本文转载自公众号贝壳产品技术(ID:gh_9afeb423f390)。
原文链接:
https://mp.weixin.qq.com/s/L8vuGvRao9HXm8OSxXqxig
评论