精华内容
下载资源
问答
  • 文章目录导入依赖包multi_image_picker的使用显示图片上传图片可能遇到的问题 在Android中从手机相册选择一些图片出来是很常用的功能。Flutter也提供了很好用的第三方可以帮助我们快速实现这个需求。 实现效果如下...


    在Android中从手机相册选择一些图片出来是很常用的功能。Flutter也提供了很好用的第三方库可以帮助我们快速实现这个需求。

    实现效果如下

    在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

    接下来看看该怎么用。

    导入依赖包

    版本号可以到puv.dev上查找最新的替换上去。

    dependencies:
      multi_image_picker: ^4.6.1
    

    multi_image_picker的使用

    使用这个插件也很简单。首先导入包

    import 'package:multi_image_picker/multi_image_picker.dart';
    

    接着就访问相册选择图片。

    • 先定义一个List<Asset> resultList用于保存选择后的图片信息。
    • MultiImagePicker.pickImages的返回类型是List<Asset>
    • 相关参数使用看代码中的注释
      // 选择照片并上传
      Future<void> uploadImages() async {
        if (resultList == null) {
          resultList = List<Asset>();
        }
        try {
          var tmp = await MultiImagePicker.pickImages(
            // 可选参数, 若resultList不为空,再次打开选择界面的适合,可以显示之前选中的图片信息。 
            selectedAssets: resultList,
            // 选择图片的最大数量
            maxImages: 9,
            // 是否支持拍照
            enableCamera: true,
            materialOptions: MaterialOptions(
              // 显示所有照片,值为 false 时显示相册
                startInAllView: false,
                allViewTitle: '所有照片',
                actionBarColor: '#2196F3',
                textOnNothingSelected: '没有选择照片'),
          );
          if (tmp.length != 0) {
            resultList = tmp;
            setState(() {});
          }
        } on Exception catch (e) {
          e.toString();
        }
      }
    
    

    这样选择图片的功能就以及做好了。

    显示图片

    接下来实现显示图片的功能。

    使用gridview显示。

    因为选择后的图片是一个Asset类型。所以可以使用 AssetThumb显示图片

    AssetThumb(
        asset: resultList[index],
        width: 300,
        height: 300,
    )
    
     @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
        title: Text('发动态'),
        actions: [
          IconButton(
              icon: Icon(Icons.send),
              onPressed: () async {
    
    
              })
        ],
          ),
          body: Container(
        padding: EdgeInsets.all(5),
        child: ListView(
          children: [
            TextField(
              controller: _controller,
              decoration: InputDecoration(
                  border: OutlineInputBorder(), hintText: '说点什么……'),
              maxLines: 7,
            ),
            Row(
              children: [
                RaisedButton(
                  onPressed: () {
                    uploadImages();
                  },
                  child: Text('选择图片'),
                ),
              ],
            ),
            Container(
              width: double.infinity,
              height: 1000,
              child: GridView.builder(
                padding: EdgeInsets.all(0),
                gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                    crossAxisCount: 3,
                    crossAxisSpacing: 2,
                    mainAxisSpacing: 2),
                itemBuilder: (BuildContext context, int index) {
                  return _createGridViewItem(
                      AssetThumb(
                        asset: resultList[index],
                        width: 300,
                        height: 300,
                      ),
                      index);
                },
                itemCount: resultList.length,
              ),
            )
          ],
        ),
          ),
        );
      } 
    
    _createGridViewItem(widget, index) {
        return Container(
          height: 100,
          width: 100,
          padding: EdgeInsets.all(0),
          margin: EdgeInsets.all(0),
          child: Stack(
            children: [
              widget,
              Positioned(
                top: 0,
                right: 0,
                child: GestureDetector(
                  onTap: () {
                    setState(() {
                      resultList.removeAt(index);
                    });
                  },
                  child: Icon(
                    Icons.close,
                    color: Colors.grey,
                  ),
                ),
              )
            ],
          ),
        );
      }
    

    上传图片

    导入依赖dio

    dependencies:  
      dio: ^3.0.10
      dio_log: ^1.3.5
    

    dio中提供了文件上传的方式,具体可以去看官方文档。

    • MultipartFile 是Dio中用户文件上传的类
    • 如果一次性需要传多个图片,可以用一个List数组保存
              List<MultipartFile> files = List();
    					//  resultList就是之前获取选择图片的List
                  for (int i = 0; i < resultList.length; i++) {
                    // 获取 ByteData
                    ByteData byteData = await resultList[i].getByteData();
                    List<int> imageData = byteData.buffer.asUint8List();
    
                    MultipartFile multipartFile = MultipartFile.fromBytes(
                      imageData,
                      // 文件名
                      filename: 'some-file-name.jpg',
                      // 文件类型
                      contentType: MediaType("image", "jpg"),
                    );
    
                    files.add(multipartFile);
                  }
    

    封装到FormData里面。

    • 这里的image就是后端接口中接收文件的参数名。如果传入的是一个数组,则会自动加上[]
                  FormData formData = FormData.fromMap({
                    // 后端接口的参数名称
                    "image": files,
                  });
    

    之后就可以上传了。

                  HttpUtils.instance.post("/upload", formData,
                      success: (response) {
                          //这部分是对相应结果的处理,大家可以根据自己返回的数据类型进行修改
                        if (response['code'] == 200) {
                          List list = response['data'];
    
                          for (var i = 0; i < list.length; i++) {
                            if (i != 0) urls += "¥";
                            urls += list[i];
                          }
                        }
    
                      });
    

    可能遇到的问题

    理论上使用multi_image_picker是不用管权限问题的,但是如果遇到了Permission denied的情况的话,则打开
    android\app\src\main\AndroidManifest.xml这个文件
    然后加上这几个权限

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="cn.jxj4869.flutter_imagepick_demo">
    
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.CAMERA" />
    

    完整代码已经上传GitHubhttps://github.com/jiang4869/CSDNBlogCode/tree/master/flutter_imagepick_demo,喜欢的话可以start一些。这个demo是动态上传界面。可以发送文字和图片信息。

    拒绝白嫖,从点一键三连开始

    展开全文
  • Android进阶封装之“史无前例”一个类实现兼容Android 6.0权限、适配Android 拍照7.0: 相机与相册上传图片就用我好啦! 一、前言。 本篇博客基本的AndroidN开始说怎么适配其拍照,其与7.0以下有何...

    Android进阶封装之“史无前例”一个类实现兼容Android 6.0权限、适配Android 拍照7.0: 相机与相册上传图片就用我好啦!


    一、前言。


    • 本篇博客从基本的AndroidN开始说怎么适配其拍照,其与7.0以下有何区别?

    • 再详细分析如何封装在 avtivity和fragment中的区别? (重点

    • 再详细说明下本封装库如何集成与使用。


    博主最近为了适配 AndroidN的拍照,浏览了很多技术文章,有些是需要4到5个类来实现、有些不能在fragment中实现… … ,个人敢想 “可以一个类实现封装全部拍照工作吗?” , 扬起袖子就是干!在辛苦的四个小时,终于把这个封装给弄出来的!


    已经放在GitHub上了(强烈推荐Star):https://github.com/xuhongv/TakePhotoAndroidN-master


    • 已经兼容在小米手机出现Attempt to invoke interface method ‘boolean Android.database.Cursor.moveToFirst()问题 。(2017/8/19)

    • 已经兼容在fragment出现权限授权不回调的bug。(2017/8/19)

    • 已经兼容在fragmenr出现图片不回调的bug。(2017/8/18)


    二、Android7.0和其以下的版本在拍照时候有何区别?


    • 由于从Android7.0(下面统一为AndroidN)开始,直接使用真实的路径的Uri会被认为是不安全的,会抛出一个FileUriExposedException这样的异常。需要使用FileProvider,选择性地将封装过的Uri共享到外部。

    • 出于以上问题,很多事情都意味着要适配,比如你在AndroidN以下,可以跳转到拍照和图库界面,但是在AndroidN就不行了!但是会有error等级的log输出,出现FileUriExposedException这样的异常,原因是Andorid7.0的“私有目录被限制访问”,“StrictMode API 政策”。

    • 谷歌这样做,出自用户隐私的考虑。既然这样,我们就必须要通过FileProvider(Provider的一个子类)共享其URL到外部即可。


    问题来了,FileProvider应该如何写?


    • 1.在manifest中添加provider,毕竟Provider也是属于四大组件之一。配置中的authorities按照江湖规矩一般加上包名,${applicationId}是获取当前项目的包名,前提是在module下的gradle.buile文件中defaultConfig{}闭包中要有applicationId属性哦。
          <provider
                android:name="android.support.v4.content.FileProvider"
                android:authorities="${applicationId}.fileprovider"
                android:exported="false"
                android:grantUriPermissions="true">
                <meta-data
                    android:name="android.support.FILE_PROVIDER_PATHS"
                    android:resource="@xml/file_provider_paths"/>
            </provider>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 2、在您工程的 res 文件夹根目录下创建一个xml文件夹,里面再新建一个file_provider_paths文件夹,其对应都是在上面的meta-data标签下面的android:resource值的。

      • 代码中path=”“,是有特殊意义的,它代码根目录,也就是说你可以向其它的应用共享根目录及其子目录下任何一个文件了。其file_provider_paths内容如下:
             <?xml version="1.0" encoding="utf-8"?>
               <resources>
                 <paths>
                 <external-path path="" name="myFile"/>
                 </paths>
               </resources>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    • 3、 自此为止,已经把FileProvider的基本的环境搭建好了,别忘了加拍照权限、读取和存储SD卡的权限哦:
        <uses-permission android:name="android.permission.CAMERA"/>
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    • 1
    • 2
    • 3
    • 1
    • 2
    • 3

    三、熟悉拍照的整个流程。


    • ①、你先要创建一个URL作为你拍照后得到的图片的URL , 这里我们使用 intent , 制定Action为 MediaStore.ACTION_IMAGE_CAPTURE , 这样就可以跳转到相机界面了 ,别忘了,在intent上把你要传的URL放上去,名字一定要是 :MediaStore.EXTRA_OUTPUT 。最后使用startActivityForResult()跳转,别忘了回调码。

      Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
      intent.putExtra(MediaStore.EXTRA_OUTPUT, imgUri);
      startActivityForResult(intent, CODE_ORIGINAL_PHOTO_CAMERA)    
      

    • ②、等到跳转相机界面后,我们不需理会用户怎么操作,我们只需关心传回来的Uri数据是否为空,毕竟在拍照后用户可能点击了舍弃,导致拿到相片为空。

      @Override
          public void onActivityResult(int requestCode, int resultCode, Intent data) {
                //相片处理
                      if (resultCode != RESULT_CANCELED) {
                                     switch (requestCode) {
                                     //相册数据,回调码要和上面一致。
                                   case IMAGE_REQUEST_CODE:
      
                                  //判断返回的数据Uri是否为空?
                                       if(imgUri!=null){
                                       //doyourthings
                                       }
                                      break;
                                              }
                                   }                                 
      

    下面是整个拍照流程图:

    使用Intent跳转到手机相机,让用户选择图片。用户选择相机拍照。可能拍照后取消,或是直接取消拍照?onActivityResult()方法判断拿到Uri数据为空?自己处理。yesno

    四、熟悉从图库拿图片的整个流程。


    • ①、从本地图库拿图片的原理和相机拍照一样,也是靠Intent跳转到图库界面,指定的Action为Intent.ACTION_PICK,Type为 “image/*” ,别忘了回调码。
                   Intent intent = new Intent(Intent.ACTION_PICK);
                   intent.setType("image/*");
                   startActivityForResult(intent, IMAGE_REQUEST_CODE);
    • 1
    • 2
    • 3
    • 1
    • 2
    • 3
    • ②、与拍照不同的是,onActivityResult()方法成功返回的话。那么传回来的是一个图片文件哦。同样,先判断是否为空?

          @Override
                public void onActivityResult(int requestCode, int resultCode, Intent data) {
                      //相片处理
                            if (resultCode != RESULT_CANCELED) {
                                           switch (requestCode) {
                                           //相册数据,回调码要和上面一致。
                                         case IMAGE_REQUEST_CODE:
                                               //判断返回的数据data.getData()是否为空?
                                                if(data.getData()!=null){
                                                //doyourthings
                                               }
                                            break;
                                                    }
                                         }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    下面是整个相册选择图片流程图:

    使用Intent跳转到手机系统相册界面,让用户选择图片。用户选择相册图片。可能选择图片后取消?onActivityResult()方法判断拿到data.getData()数据为空?自己处理。yesno

    五、动态权限?


    • 让不少人烦恼的是,在安卓6.0之后,需要动态授权,那么作为拍照、写入SD卡和读取SD卡,这些“危险权限”,动态授权是必然的。

    • 我这里采用郭神的做法,如果用户拒绝的某些权限的话,会通过接口提示。代码如下:


        /**
         * 申请运行时权限
         * 来自郭神公开课
         */
        private void requestRuntimePermission(String[] permissions, PermissionListener listener) {
    
            permissionListener = listener;
            List<String> permissionList = new ArrayList<>();
            for (String permission : permissions) {
                if (ContextCompat.checkSelfPermission(mContext, permission) != PackageManager.PERMISSION_GRANTED) {
                    permissionList.add(permission);
                }
            }
    
            //此处兼容了无法在fragment回调监听事件
            if (!permissionList.isEmpty()) {
                if (isActicity) {
                    ActivityCompat.requestPermissions((Activity) mContext, permissionList.toArray(new String[permissionList.size()]), 1);
                } else {
                    mFragment.requestPermissions(permissionList.toArray(new String[permissionList.size()]), 1);
                }
    
                if (takeCallBacklistener != null) {
                    takeCallBacklistener.failed(1, permissionList);
                }
            } else {
                permissionListener.onGranted();
            }
        }
    
    • 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
    • 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

    六、关于裁剪的代码!


      private void statZoom(File srcFile, File output) {
    
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.setDataAndType(getImageContentUri(mContext, srcFile), "image/*");
    
        // crop为true是设置在开启的intent中设置显示的view可以剪裁
        intent.putExtra("crop", "true");
        // 是否缩放?如果不缩放,会出现黑边哦
        intent.putExtra("scale", true);
    
        // aspectX aspectY 是宽高的比例
        intent.putExtra("aspectX", aspectX);
        intent.putExtra("aspectY", aspectY);
    
        // outputX,outputY 是剪裁图片的宽高
        intent.putExtra("outputX", outputX);
        intent.putExtra("outputY", outputY);
        intent.putExtra("return-data", false);//true:不返回uri,false:返回uri
        intent.putExtra("scaleUpIfNeeded", true);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(output));
        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
    
        //此处兼容在fragment不会回调图片问题
        if (isActicity) {
            mActivity.startActivityForResult(intent, CODE_TAILOR_PHOTO);
        } else {
            mFragment.startActivityForResult(intent, CODE_TAILOR_PHOTO);
         }
        }
    

    七、封装的主角来了!


    6.1 、介绍只需三步的环境集成:


    • 第一步:把 demo下的res的 xml文件夹整个复制到你的工程res文件夹根目录下:

    第一步


    • 第二步:在你AndroidManifest.xml下的Application节点下加入以下代码:
              <provider
                android:name="android.support.v4.content.FileProvider"
                android:authorities="${applicationId}.fileprovider"
                android:exported="false"
                android:grantUriPermissions="true">
                <meta-data
                    android:name="android.support.FILE_PROVIDER_PATHS"
                    android:resource="@xml/file_provider_paths"/>
               </provider>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    • 如下所示

    这里写图片描述


    -第三步 :把demo的就仅仅一个类 TakePictureManager.class 复制过去就可以啦!别忘了在清单文件加相关权限哦!


    6.2 、怎么使用?

    示例:


    TakePictureManager takePictureManager takePictureManager = new TakePictureManager(this);
                    //开启裁剪 比例 1:3 宽高 350 350  (默认不裁剪)
                    takePictureManager.setTailor(1, 3, 350, 350);
                    //拍照方式
                    takePictureManager.startTakeWayByCarema();
                    //监听回调
                    takePictureManager.setTakePictureCallBackListener(new TakePictureManager.takePictureCallBackListener() {
             //成功拿到图片,isTailor 是否裁剪? ,outFile 拿到的文件 ,filePath拿到的URl
            @Override
            public void successful(boolean isTailor, File outFile, Uri filePath) {
                      }
            //失败回调
            @Override
            public void failed(int errorCode, List<String> deniedPermissions) {
    
                       }
                    });
    
        //把本地的onActivityResult()方法回调绑定到对象
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            takePictureManager.attachToActivityForResult(requestCode, resultCode, data);
        }
    
        //onRequestPermissionsResult()方法权限回调绑定到对象
        @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
            takePictureManager.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    
    • 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
    • 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

    使用详细步骤:


    • ①、先new一个TakePictureManager 对象,构造方法只需传this即可(不管你在Activity还是在Fragment)。

    • ②、重写onActivityResult()方法,调用对象的attachToActivityForResult()方法,参数依次是onActivityResult()回调的参数。 实现把拍照或相册回调发数据绑定在对象方法处理。

    • ③、重写onRequestPermissionsResult()方法,调用对象的onRequestPermissionsResult()方法,参数依次是onRequestPermissionsResult()回调的参数。 实现把权限回调绑定在对象方法处理。

    • ④、这时候,你只需调用对象方法,即可轻松调用相机或相册。具体的方法参数说明如下:


    方法名 参数 说明
    setTailor(int aspectX, int aspectY, int outputX, int outputY) 要裁剪的宽比例、要裁剪的高比例、要裁剪图片的宽、要裁剪图片的高 一旦调用,表示要裁剪,默认不裁剪
    startTakeWayByCarema() 无参数 调用相机
    startTakeWayByAlbum() 无参数 调用相册
    setTakePictureCallBackListener(takePictureCallBackListener listener) takePictureCallBackListener 回调接口 调用相机或相册后的回调

    接口 方法 说明
    takePictureCallBackListener successful(boolean isTailor, File outFile, Uri filePath) 成功回调! isTailor : 是否已裁剪, outFile :输出的照片文件 ,filePath :输出的照片Uri 。
      failed(int errorCode, List deniedPermissions) 失败回调!errorCode :0表示相片已移除或不存在! 1表示权限被拒绝,deniedPermissions当权限被拒绝时候,会通过list传回

    八、造轮子时候遇到的问题:


    • 在Fragment使用时候,回调的相片数据被依附的activity的onActivityResult()方法拦截了!相信这个问题困扰许多人的问题,在使用他人代码时候,在Activity可以使用,但是在Fragment却失败。原因在于:

      • 在Fragment就存在startActivityForResult()方法,不需要 getActivity().startActivityForResult() , 也就是说不需要调用 依附的Activity的此方法,本身就有此方法。这是我翻阅 Fragment源码发现。截图如下:

    这里写图片描述


    • 于是乎我在封装时候,特意这样做:

    这里写图片描述




    展开全文
  • 高性能 (使用leveldb作为kv) 高可靠(设计极其简单,使用成熟组件) 无中心设计(所有节点都可以同时读写) 优点 无依赖(单一文件) 自动同步 失败自动修复 按天分目录方便维护 支持不同的场景 文件自动去重 ...
  • 但是这个方法也可以用来打造自己的图片文件(只不过按文章的方法,获取图片链接有点不便),mark备用~ 尝试2:在wiki里创建文章 回头再看老师文中图片链接的后半部分:……...
  • asp.net知识

    2015-06-18 08:45:45
    怎么在ASP.NET 2.0使用Membership asp.net 2.0-实现数据访问(1) ASP.NET 2.0 新特性 .NET 2.0里使用强类型数据创建多层应用 在MastPage引用脚本资源 2.0正式版callback的一些变化+使用示例(ASP.NET 2.0)...
  • Android进阶封装之“史无前例”一个类实现兼容Android 6.0权限、适配Android 拍照7.0: 相机与相册上传图片就用我好啦!一、前言。 本篇博客基本的AndroidN开始说怎么适配其拍照,其与7.0以下有何区别? 再详细...

    Android进阶封装之“史无前例”一个类实现兼容Android 6.0权限、适配Android 拍照7.0: 相机与相册上传图片就用我好啦!


    一、前言。


    • 本篇博客从基本的AndroidN开始说怎么适配其拍照,其与7.0以下有何区别?

    • 再详细分析如何封装在 avtivity和fragment中的区别? (重点

    • 再详细说明下本封装库如何集成与使用。


    博主最近为了适配 AndroidN的拍照,浏览了很多技术文章,有些是需要4到5个类来实现、有些不能在fragment中实现… … ,个人敢想 “可以一个类实现封装全部拍照工作吗?” , 扬起袖子就是干!在辛苦的四个小时,终于把这个封装给弄出来的!


    已经放在GitHub上了(强烈推荐Star):https://github.com/xuhongv/TakePhotoAndroidN-master


    • 已经兼容在小米手机出现Attempt to invoke interface method ‘boolean android.database.Cursor.moveToFirst()问题 。(2017/8/19)

    • 已经兼容在fragment出现权限授权不回调的bug。(2017/8/19)

    • 已经兼容在fragmenr出现图片不回调的bug。(2017/8/18)


    二、Android7.0和其以下的版本在拍照时候有何区别?


    • 由于从Android7.0(下面统一为AndroidN)开始,直接使用真实的路径的Uri会被认为是不安全的,会抛出一个FileUriExposedException这样的异常。需要使用FileProvider,选择性地将封装过的Uri共享到外部。

    • 出于以上问题,很多事情都意味着要适配,比如你在AndroidN以下,可以跳转到拍照和图库界面,但是在AndroidN就不行了!但是会有error等级的log输出,出现FileUriExposedException这样的异常,原因是Andorid7.0的“私有目录被限制访问”,“StrictMode API 政策”。

    • 谷歌这样做,出自用户隐私的考虑。既然这样,我们就必须要通过FileProvider(Provider的一个子类)共享其URL到外部即可。


    问题来了,FileProvider应该如何写?


    • 1.在manifest中添加provider,毕竟Provider也是属于四大组件之一。配置中的authorities按照江湖规矩一般加上包名,${applicationId}是获取当前项目的包名,前提是在module下的gradle.buile文件中defaultConfig{}闭包中要有applicationId属性哦。
          <provider
                android:name="android.support.v4.content.FileProvider"
                android:authorities="${applicationId}.fileprovider"
                android:exported="false"
                android:grantUriPermissions="true">
                <meta-data
                    android:name="android.support.FILE_PROVIDER_PATHS"
                    android:resource="@xml/file_provider_paths"/>
            </provider>
    • 2、在您工程的 res 文件夹根目录下创建一个xml文件夹,里面再新建一个file_provider_paths文件夹,其对应都是在上面的meta-data标签下面的android:resource值的。

      • 代码中path=”“,是有特殊意义的,它代码根目录,也就是说你可以向其它的应用共享根目录及其子目录下任何一个文件了。其file_provider_paths内容如下:
             <?xml version="1.0" encoding="utf-8"?>
               <resources>
                 <paths>
                 <external-path path="" name="myFile"/>
                 </paths>
               </resources>

    • 3、 自此为止,已经把FileProvider的基本的环境搭建好了,别忘了加拍照权限、读取和存储SD卡的权限哦:
        <uses-permission android:name="android.permission.CAMERA"/>
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

    三、熟悉拍照的整个流程。


    • ①、你先要创建一个URL作为你拍照后得到的图片的URL , 这里我们使用 intent , 制定Action为 MediaStore.ACTION_IMAGE_CAPTURE , 这样就可以跳转到相机界面了 ,别忘了,在intent上把你要传的URL放上去,名字一定要是 :MediaStore.EXTRA_OUTPUT 。最后使用startActivityForResult()跳转,别忘了回调码。

      Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
      intent.putExtra(MediaStore.EXTRA_OUTPUT, imgUri);
      startActivityForResult(intent, CODE_ORIGINAL_PHOTO_CAMERA)    
      

    • ②、等到跳转相机界面后,我们不需理会用户怎么操作,我们只需关心传回来的Uri数据是否为空,毕竟在拍照后用户可能点击了舍弃,导致拿到相片为空。

      @Override
          public void onActivityResult(int requestCode, int resultCode, Intent data) {
                //相片处理
                      if (resultCode != RESULT_CANCELED) {
                                     switch (requestCode) {
                                     //相册数据,回调码要和上面一致。
                                   case IMAGE_REQUEST_CODE:
      
                                  //判断返回的数据Uri是否为空?
                                       if(imgUri!=null){
                                       //doyourthings
                                       }
                                      break;
                                              }
                                   }                                 
      

    下面是整个拍照流程图:

    Created with Raphaël 2.1.0使用Intent跳转到手机相机,让用户选择图片。用户选择相机拍照。可能拍照后取消,或是直接取消拍照?onActivityResult()方法判断拿到Uri数据为空?自己处理。yesno

    四、熟悉从图库拿图片的整个流程。


    • ①、从本地图库拿图片的原理和相机拍照一样,也是靠Intent跳转到图库界面,指定的Action为Intent.ACTION_PICK,Type为 “image/*” ,别忘了回调码。
                   Intent intent = new Intent(Intent.ACTION_PICK);
                   intent.setType("image/*");
                   startActivityForResult(intent, IMAGE_REQUEST_CODE);
    • ②、与拍照不同的是,onActivityResult()方法成功返回的话。那么传回来的是一个图片文件哦。同样,先判断是否为空?

          @Override
                public void onActivityResult(int requestCode, int resultCode, Intent data) {
                      //相片处理
                            if (resultCode != RESULT_CANCELED) {
                                           switch (requestCode) {
                                           //相册数据,回调码要和上面一致。
                                         case IMAGE_REQUEST_CODE:
                                               //判断返回的数据data.getData()是否为空?
                                                if(data.getData()!=null){
                                                //doyourthings
                                               }
                                            break;
                                                    }
                                         }

    下面是整个相册选择图片流程图:

    Created with Raphaël 2.1.0使用Intent跳转到手机系统相册界面,让用户选择图片。用户选择相册图片。可能选择图片后取消?onActivityResult()方法判断拿到data.getData()数据为空?自己处理。yesno

    五、动态权限?


    • 让不少人烦恼的是,在安卓6.0之后,需要动态授权,那么作为拍照、写入SD卡和读取SD卡,这些“危险权限”,动态授权是必然的。

    • 我这里采用郭神的做法,如果用户拒绝的某些权限的话,会通过接口提示。代码如下:


        /**
         * 申请运行时权限
         * 来自郭神公开课
         */
        private void requestRuntimePermission(String[] permissions, PermissionListener listener) {
    
            permissionListener = listener;
            List<String> permissionList = new ArrayList<>();
            for (String permission : permissions) {
                if (ContextCompat.checkSelfPermission(mContext, permission) != PackageManager.PERMISSION_GRANTED) {
                    permissionList.add(permission);
                }
            }
    
            //此处兼容了无法在fragment回调监听事件
            if (!permissionList.isEmpty()) {
                if (isActicity) {
                    ActivityCompat.requestPermissions((Activity) mContext, permissionList.toArray(new String[permissionList.size()]), 1);
                } else {
                    mFragment.requestPermissions(permissionList.toArray(new String[permissionList.size()]), 1);
                }
    
                if (takeCallBacklistener != null) {
                    takeCallBacklistener.failed(1, permissionList);
                }
            } else {
                permissionListener.onGranted();
            }
        }
    

    六、关于裁剪的代码!


      private void statZoom(File srcFile, File output) {
    
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.setDataAndType(getImageContentUri(mContext, srcFile), "image/*");
    
        // crop为true是设置在开启的intent中设置显示的view可以剪裁
        intent.putExtra("crop", "true");
        // 是否缩放?如果不缩放,会出现黑边哦
        intent.putExtra("scale", true);
    
        // aspectX aspectY 是宽高的比例
        intent.putExtra("aspectX", aspectX);
        intent.putExtra("aspectY", aspectY);
    
        // outputX,outputY 是剪裁图片的宽高
        intent.putExtra("outputX", outputX);
        intent.putExtra("outputY", outputY);
        intent.putExtra("return-data", false);//true:不返回uri,false:返回uri
        intent.putExtra("scaleUpIfNeeded", true);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(output));
        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
    
        //此处兼容在fragment不会回调图片问题
        if (isActicity) {
            mActivity.startActivityForResult(intent, CODE_TAILOR_PHOTO);
        } else {
            mFragment.startActivityForResult(intent, CODE_TAILOR_PHOTO);
         }
        }
    

    七、封装的主角来了!


    6.1 、介绍只需三步的环境集成:


    • 第一步:把 demo下的res的 xml文件夹整个复制到你的工程res文件夹根目录下:

    第一步


    • 第二步:在你AndroidManifest.xml下的Application节点下加入以下代码:
              <provider
                android:name="android.support.v4.content.FileProvider"
                android:authorities="${applicationId}.fileprovider"
                android:exported="false"
                android:grantUriPermissions="true">
                <meta-data
                    android:name="android.support.FILE_PROVIDER_PATHS"
                    android:resource="@xml/file_provider_paths"/>
               </provider>

    • 如下所示

    这里写图片描述


    -第三步 :把demo的就仅仅一个类 TakePictureManager.class 复制过去就可以啦!别忘了在清单文件加相关权限哦!


    6.2 、怎么使用?

    示例:


    TakePictureManager takePictureManager takePictureManager = new TakePictureManager(this);
                    //开启裁剪 比例 1:3 宽高 350 350  (默认不裁剪)
                    takePictureManager.setTailor(1, 3, 350, 350);
                    //拍照方式
                    takePictureManager.startTakeWayByCarema();
                    //监听回调
                    takePictureManager.setTakePictureCallBackListener(new TakePictureManager.takePictureCallBackListener() {
             //成功拿到图片,isTailor 是否裁剪? ,outFile 拿到的文件 ,filePath拿到的URl
            @Override
            public void successful(boolean isTailor, File outFile, Uri filePath) {
                      }
            //失败回调
            @Override
            public void failed(int errorCode, List<String> deniedPermissions) {
    
                       }
                    });
    
        //把本地的onActivityResult()方法回调绑定到对象
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            takePictureManager.attachToActivityForResult(requestCode, resultCode, data);
        }
    
        //onRequestPermissionsResult()方法权限回调绑定到对象
        @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
            takePictureManager.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    

    使用详细步骤:


    • ①、先new一个TakePictureManager 对象,构造方法只需传this即可(不管你在Activity还是在Fragment)。

    • ②、重写onActivityResult()方法,调用对象的attachToActivityForResult()方法,参数依次是onActivityResult()回调的参数。 实现把拍照或相册回调发数据绑定在对象方法处理。

    • ③、重写onRequestPermissionsResult()方法,调用对象的onRequestPermissionsResult()方法,参数依次是onRequestPermissionsResult()回调的参数。 实现把权限回调绑定在对象方法处理。

    • ④、这时候,你只需调用对象方法,即可轻松调用相机或相册。具体的方法参数说明如下:


    方法名 参数 说明
    setTailor(int aspectX, int aspectY, int outputX, int outputY) 要裁剪的宽比例、要裁剪的高比例、要裁剪图片的宽、要裁剪图片的高 一旦调用,表示要裁剪,默认不裁剪
    startTakeWayByCarema() 无参数 调用相机
    startTakeWayByAlbum() 无参数 调用相册
    setTakePictureCallBackListener(takePictureCallBackListener listener) takePictureCallBackListener 回调接口 调用相机或相册后的回调

    接口 方法 说明
    takePictureCallBackListener successful(boolean isTailor, File outFile, Uri filePath) 成功回调! isTailor : 是否已裁剪, outFile :输出的照片文件 ,filePath :输出的照片Uri 。
    failed(int errorCode, List deniedPermissions) 失败回调!errorCode :0表示相片已移除或不存在! 1表示权限被拒绝,deniedPermissions当权限被拒绝时候,会通过list传回

    八、造轮子时候遇到的问题:


    • 在Fragment使用时候,回调的相片数据被依附的activity的onActivityResult()方法拦截了!相信这个问题困扰许多人的问题,在使用他人代码时候,在Activity可以使用,但是在Fragment却失败。原因在于:

      • 在Fragment就存在startActivityForResult()方法,不需要 getActivity().startActivityForResult() , 也就是说不需要调用 依附的Activity的此方法,本身就有此方法。这是我翻阅 Fragment源码发现。截图如下:

    这里写图片描述


    • 于是乎我在封装时候,特意这样做:

    这里写图片描述




    展开全文
  • 黑马品优购项目

    2018-07-25 16:11:13
    图片怎么上传 1.4. 搜索 ​ 怎么实现 数据量大、 并发量高的搜索 怎么分词 1.5. 消息通知 ​ 哪些情况用到activeMq 1.6. 优化 seo怎么优化 怎么加快访问速度 1.7. 秒杀 ​ 怎么处理高并发 ​ 秒杀过程中怎么控制...
  • C#基础类库

    2018-07-11 08:45:26
    C#基础类 1.Chart图形 Assistant创建显示图像的标签和文件 OWCChart统计图的封装类 2.Cookie&Session;&Cache;缓存帮助类 CacheHelper C#操作缓存的帮助类,实现了怎么设置缓存,怎么取缓存,怎么清理缓存等...
  • 涂鸦文件、截图、远程抓图、图片库的xxxxPathFormat如果没有配置,默认等于imagePath. 远程文件库的xxxxPathFormat如果没有配置,默认等于filePath. settings : 字典值,配置项与ueditor/ueditor.config.js里面的...
  • 图床教程

    2020-12-08 23:55:00
    图片永不失效的方法是上传图片至图床</strong>,图片的储存位置在互联网上,那么,无论在什么情况下,你的 MarkDown 文件,甚至将 MarkDown 转换成的 html 文件在任何电脑中都能正常...
  • 文件上传:支持复制粘贴或者拖拽上传图片;支持上传普通文件;对 MP3 会使用在线播放器进行渲染 剪贴板处理:自动将复制的内容转换为 Markdown 格式;外链的图片自动上传站内 @用户:根据用户名自动补全,支持...
  • 本说明文档的图片存在我个人博客的CDN服务器。 1.9. 考研科目细节 群里搞到的新版参考书目,来源未知 这是个845的,408的统考官方并没有给出参考书目。 1.9.1. 初试 1.9.1.1. 数学一 划重点:不推荐张宇 你...
  • 没什么技术含量校园说说这个部分最大的问题就是适配的问题,有的图片多,有的文字多,怎么决定这个长度呢 所以我把每条说说分为四个部分,上往下依次是 用户信息和文字/图片部分/评论数目部分/评论部分 这样首先...
  • 因项目需求实现了一个调用手机拍照和相册上传图片到服务器的功能,昨天测试了一下发现在4.0版本的手机系统就会导致崩溃 ## 崩溃原因:Failed to ensure directory: /storage/sdcard1/Android/data/ ![图片说明]...
  • 图片,字体等都会自动上传到cdn上,最后生成一个无任何依赖的纯html,大概是下面的样子: <pre><code>html <meta charset="utf-8"><title>快言管理后台</title&...
  • 设置自定义字段的能力,客户端收集额外的信息,在为了安置过程。开发者友好:钩到BoxBilling你的php脚本事件修改订单状态更改期间发送的电子邮件模板。您可以设置自定义的电子邮件,任何产品组的特定产品。...
  • 并且,在看文章的过程中,可以方便的将新的单词增加进个人的熟悉程度库中,以增加词汇的积累。 ------------------------------------- 新版的部分功能说明: Dictionary 工具栏说明: EQU 以完全匹配的方式...
  • 注:图片上传后可能被压缩,如需无损版高清大图,可去微信公众号「CodeSheep」后台回复「高清大图」四个字获取即可 C语言/C++开发学习路线(偏后台) Linux学习路线 前端开发学习路线 项目推荐 看完这篇,别人...
  • demo有实现类似淘宝商品详情的效果,第一个放视频,后面的放的是图片,并且可以设置首尾不能滑动。 因为大家使用的播放器不一样业务环境也不同,具体情况自己把握,demo就是给一个思路哈!可以参考和修改 我想...
  • 新版Android开发教程.rar

    千次下载 热门讨论 2010-12-14 15:49:11
    ----------------------------...• 优化的图形 包括定制的 2D 图形, 3D 图形基于 OpenGL ES 1.0 (硬件加速可选) • SQLite SQLite SQLite SQLite 用作结构化的数据存储 • 多媒体支持 包括常见的音频、视频和...
  • ExtAspNet_v2.3.2_dll

    2010-09-29 14:37:08
    -Region控件删除SplitColor属性,增加CollapseMode, EnableSplitTip, SplitTip, CollapsibleSplitTip属性(feedback:bmck)。 -BorderPanel更名为RegionPanel。 -DropDownList拥有MarkInvalid方法(feedback:sun...
  • -Region控件删除SplitColor属性,增加CollapseMode, EnableSplitTip, SplitTip, CollapsibleSplitTip属性(feedback:bmck)。 -BorderPanel更名为RegionPanel。 -DropDownList拥有MarkInvalid方法(feedback:sun...
  • <p>ES6终将是要取代 ES5 的但是在 ES5 到 ES6 过度的过程,各个浏览器厂商对 ES6 支持的也不是很好。 主要是将ES6的代码编译为ES5。至于为什么要这么做,不是本文的内容,可以自行谷歌了解。 ...
  • 技术实现上,讲解了5个android平台下的完整综合实例及源代码分析,分别是rss阅读器、基于google map的个人gps、豆瓣网(web 2.0)客户端、在线音乐播放器、手机信息查看助手。本书注重对实际动手能力的指导,在遵循...
  • 深入学习shell脚本艺术

    热门讨论 2011-02-22 04:01:01
    一副扑克牌取出一张随机的牌 9-26. 两个指定值之间的随机数 9-27. 使用随机数来摇一个骰子 9-28. 重新分配随机数种子 9-29. 使用awk产生伪随机数 9-30. C风格的变量处理 10-1. 循环的一个简单例子 10-2. 每个...
  • <p><strong>Image</strong> 用于在UI显示图片。 <p><strong>Easing</strong> 也是用React Native创建动画的载体,它允许我们使用已经定义好的各种缓冲函数,例如:<strong>linear, <strong>ease...

空空如也

空空如也

1 2
收藏数 26
精华内容 10
关键字:

从图片库中怎么上传图片