Android实现拍照、图库选择图片、权限动态申请

 Hygge   2021-04-28 09:35   180 人阅读  2 条评论


前言

Android四大组件有Activity,Service服务,Content Provider内容提供,BroadcastReceiver广播接收器。

但是在开发智慧城市或者其他项目的过程中,只用到了Activity..

目前要做的功能是拍照上传和从图库中选择图片上传,这两个功能都涉及到了ContentProvider,太难上手了,写篇文章记录一下。


1619574045611.png

一、界面布局

基本的ImageView和Button,底部的ListView是用代码实现的

button = view.findViewById(R.id.button);
iv_show = view.findViewById(R.id.iv_show);
// 写 一个弹出的底部列表
ListView listView = new ListView(getContext());
listView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
String[] str = {"摄像机", "图库", "取消"};
listView.setAdapter(new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, str));

监听Listview的Item点击事件

listView.setOnItemClickListener((parent, view1, position, id) -> {
   if (position == 0) {
       // position 0 - 摄像机
       // 摄像机的Intent 隐式意图 背下来吧
       Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

       // 这个File 是用作保存拍照后的照片的,后面会将这个File设置为Intent的输出文件
       File photoFile = new File(getContext().getExternalCacheDir(), "test.png");

       // android 7.0 后禁止使用file://uri  需要使用content://uri
       // 将上面定义的file文件 转成合适的uri
       Uri uri = FileProvider.getUriForFile(getContext(), "com.hygge.testdemo1.fileProvider", photoFile);

       // 将输出的文件uri 应用到intent中。
       intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);

       // 启动intent并等待返回值
       startActivityForResult(intent, REQUEST_CAMERA);

   }else if (position == 1){
       // position 0 - 图库
       // 打开intent 隐式意图 相册
       Intent intent = new Intent(Intent.ACTION_PICK);

       // 设置需要的类型为图片
       intent.setType("image/*");

       // 启动intent 并等待返回值
       startActivityForResult(intent, REQUEST_PICKER);
   }
});

按照上面写之后是无法运行的,因为有一个com.hygge.testdemo1.fileProvider

涉及到了Android 7.0 行为变更 通过FileProvider在应用间共享文件吧

需要在AndroidManifest中定义标签

<application>
...
   <provider
           android:authorities="包名.fileProvider" // 也就是上面的getUriForFile中的参数
           android:name="androidx.core.content.FileProvider" // FileProvider全类名
           android:exported="false" 
           android:grantUriPermissions="true">
           <meta-data
               android:name="android.support.FILE_PROVIDER_PATHS" // 固定内容
               android:resource="@xml/provider_paths"/> // 需要自己在xml文件夹中创建文件
       </provider>
</application>



二、内存和拍照权限动态申请

/**
    * 记录所有权限
    */
private static final String[] permissions = {Manifest.permission.CAMERA,
                                            Manifest.permission.READ_EXTERNAL_STORAGE,
                                            Manifest.permission.WRITE_EXTERNAL_STORAGE};

/**
    *  进行权限检查
    */
private void checkPermissions() {
   // 记录需要申请哪些权限
   List<String> target = new ArrayList<>();
   // 过滤所有的权限
   Arrays.stream(permissions).filter(s -> getContext().checkSelfPermission(s) != PackageManager.PERMISSION_GRANTED).forEach(target::add);
   // 申请权限
   if (target.size() == 0) Toast.makeText(getContext(), "权限申请全部通过", Toast.LENGTH_SHORT).show();
   else requestPermissions(target.toArray(new String[target.size()]),REQUEST_PERMISSION);
}


// 接受到权限请求的结果回传
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
   // 需要再次申请的列表
   List<String> again = new ArrayList<>();

   if (requestCode == REQUEST_PERMISSION) {
       // 遍历申请权限的结果,如果有未通过的 就将对应的权限名放到again中 准备再次申请
       for(int i = 0;i < grantResults.length;i++){
           if(grantResults[i] != PackageManager.PERMISSION_GRANTED) again.add(permissions[i]);
       }
   }

   // 判断是否需要再次申请权限
   if(again.size() != 0) requestPermissions(again.toArray(new String[again.size()]),REQUEST_PERMISSION);
   else Toast.makeText(getContext(), "权限请求全部通过", Toast.LENGTH_SHORT).show();
}


三、provider_paths.xml文件

res/xml/provider_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
   <!-- 使用getExternalCacheDir()可直接拿到下面定义的路径 -->
   <external-cache-path
       name="cachef"
       path="."/>
</paths>

四、接受回传的参数

@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
   if (requestCode == REQUEST_CAMERA && resultCode == RESULT_OK) {
       Glide.with(getContext()).load(getContext().getExternalCacheDir() + "/test.png").into(iv_show);
   }else if(requestCode == REQUEST_PICKER){
       Uri uri = null;
       // try 是有可能未选择图片就进行返回,会造成空指针
       try {
           uri = data.getData(); //获取系统返回的照片uri
       }catch (NullPointerException e){
           e.printStackTrace();
       }finally {
           if(uri != null){
               // 使用ContentResolve 进行读取选择的图片路径
               String[] strings = {MediaStore.Images.Media.DATA};
               Cursor cursor = getContext().getContentResolver().query(uri, strings, null, null, null);
               cursor.moveToFirst();
               int index = cursor.getColumnIndex(strings[0]);
               String path = cursor.getString(index); //获取图片路径
               cursor.close();
               Log.d("fantasychong_path", path);
               Glide.with(getContext()).load(path).into(iv_show);
           }
       }
   }
}


五、demo

项目使用的接口不方便暴露

所以demo中只有manifest和xml和实现本文章所需要功能的fragment

链接:https://pan.baidu.com/s/1thNPNpFnz5kji0rJSP2Q_g  提取码:t2v1 复制这段内容后打开百度网盘手机App,操作更方便哦--来自百度网盘超级会员V3的分享


结语

我也不太熟,刚学会,全程需要记忆的东西比较多(毕竟之前写都需要背什么hhh,唯手熟尔)

隐式意图和FileProvider的定义等等,总之实现想要的功能是没问题的。

学习过程中,参考了下面两篇文章


FileProvider 的使用 : https://www.jianshu.com/p/47fcd7873f39

Android开发丶从相册中选择照片,调用摄像头拍照以及录制小视 :https://blog.csdn.net/u014078990/article/details/103868455


本文地址:https://blog.lisok.cn/post/40.html
版权声明:本文为原创文章,版权归 Hygge 所有,欢迎分享本文,转载请保留出处!

 发表评论


表情

 评论列表

  1. 访客
    访客  @回复

    真长,不愧是java