【Android】图片选择、裁剪、压缩、上传

作者: 吾非言 | 来源:发表于2017-10-11 14:55 被阅读394次

    作者:邹峰立,微博:zrunker,邮箱:zrunker@yahoo.com,微信公众号:书客创作,个人平台:www.ibooker.cc

    本文选自书客创作平台第22篇文章。阅读原文

    书客创作

    在文章开始讲解之前,大家需要先进行阅读一下这几篇文章,因为这篇文章中所提到的一些依赖与下面几篇文章有关,当然这些依赖大家也可以自行定义。

    【Android】图片选择、裁剪、压缩、上传(一)图片选择(拍照+本地相册)Dialog封装,使用

    【Android】文件管理类ZFile

    【Android】Bitmap管理类ZBitmap

    一、引入资源包

    Android Studio引入:

    在build.gradle文件中添加以下代码:
    allprojects {
       repositories {
          maven { url 'https://www.jitpack.io' }
       }
    }
    dependencies {
       compile 'com.github.zrunker:Zbitmap:v1.0.0'
       compile'com.github.zrunker:ZDialog:v1.0.0'
       compile'com.github.zrunker:ZFile:v1.0.3'
    }
    

    或Maven引入,在pom.xml文件中添加以下代码:

    <repositories>
       <repository>
          <id>jitpack.io</id>
          <url>https://jitpack.io</url>
       </repository>
    </repositories>
    
    <dependency>
       <groupId>com.github.zrunker</groupId>
       <artifactId>ZBitmap</artifactId>
       <version>v1.0.0</version>
    </dependency>
    
    <dependency>
       <groupId>com.github.zrunker</groupId>
       <artifactId>ZDialog</artifactId>
       <version>v1.0.0</version>
    </dependency>
    
    <dependency>
       <groupId>com.github.zrunker</groupId>
       <artifactId>ZFile</artifactId>
       <version>v1.0.3</version>
    </dependency>
    

    Zbitmap和ZFile是两个辅助类。

    其中com.github.zrunker:Zbitmap:v1.0.0是用来处理Bitmap的工具类,可以通过ZBitmap的使用(Bitmap管理)这篇文章进行了解。

    而compile'com.github.zrunker:ZFile:v1.0.3'是用来管理文件工具类,可以通过ZFile的使用(FileUtil)这篇文章进行了解。

    二、添加权限

    在AndroidManifest.xml(清单)文件中添加需要的权限:

    <!--用于写入数据到扩展存储卡(SD)-->
    <uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <!--往sdcard中读取数据的权限-->
    <uses-permissionandroid:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <!--在sdcard中创建/删除文件的权限-->
    <uses-permissionandroid:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
    <!--添加拍照权限-->
    <uses-permissionandroid:name="android.permission.CAMERA"/>
    <uses-feature
       android:name="android.hardware.camera"
       android:required="true"/>
    

    三、选择图片

    1、调用ZDialog中选择图片Dialog,这里定义一个全局变量。

    // 该类由ZDialog提供。
    private ChoosePictrueDialog choosePictrueDialog,ChoosePictrueDialog;
    
    //显示选择图片Dialog
    private void showChoosePictrueDialog() {
       if(choosePictrueDialog==null)
          choosePictrueDialog=new ChoosePictrueDialog(this);
       choosePictrueDialog.showChoosePictrueDialog();
    }
    
    //关闭选择图片Dialog
    private void closeChoosePictrueDialog() {
       if(choosePictrueDialog!=null)
          choosePictrueDialog.closeChoosePictrueDialog();
    }
    
    选择图片Dialog

    2、对选中图片回调方法处理onActivityResult,在此之前要先处理权限事件回调onRequestPermissionsResult。

    // 权限设置结果
    @Override
    public void onRequestPermissionsResult(intrequestCode,@NonNullString[] permissions,@NonNullint[] grantResults) {
       super.onRequestPermissionsResult(requestCode,permissions,grantResults);
       switch(requestCode) {
          case ZDialogConstantUtil.PERMISSION_CAMERA_REQUEST_CODE:
             if(grantResults.length==1&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                choosePictrueDialog.startPhoto();
             }else{
                Toast.makeText(this,"获取拍照权限失败",Toast.LENGTH_SHORT).show();
             }
          break;
          case ZDialogConstantUtil.PERMISSION_READ_EXTERNAL_STORAGE_REQUEST_CODE:
             if(grantResults.length==1&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                choosePictrueDialog.startPhoto();
             }else{
                Toast.makeText(this,"sdcard中读取数据的权限失败",Toast.LENGTH_SHORT).show();
             }
          break;
          case ZDialogConstantUtil.PERMISSION_WRITE_EXTERNAL_STORAGE_REQUEST_CODE:
             if(grantResults.length==1&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                choosePictrueDialog.startPhoto();
             }else{
                Toast.makeText(this,"写入数据到扩展存储卡(SD)权限失败",Toast.LENGTH_SHORT).show();
             }
          break;
       }
    }
    

    选择图片回调,包括拍照、选择图片、裁剪。其中ZDialogConstantUtil是由ZDialog提供的常量管理类。

    /**
     *通过回调方法处理图片
     */
    @Override
    protected void onActivityResult(intrequestCode, intresultCode,Intent data) {
       super.onActivityResult(requestCode,resultCode,data);
       switch(requestCode) {
          case ZDialogConstantUtil.RESULT_PHOTO_CODE:
             /**
              *拍照
              */
             closeChoosePictrueDialog();
             Uri photoUri = ChoosePictrueUtil.photoUri;
             if(photoUri !=null) {
                //通知系统刷新图库
                Intent localIntent =new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,photoUri);
                sendBroadcast(localIntent);
                //启动裁剪
                cropImageUri(photoUri);
             }
          break;
          case ZDialogConstantUtil.RESULT_LOAD_CODE:
             /**
              *从相册中选择图片
              */
             closeChoosePictrueDialog();
             if(data ==null) {
                return;
             }else{
                Uri uri = data.getData();//获取图片是以content开头
                if(uri !=null) {
                   cropImageUri(uri);//开始裁剪
                }
             }
          break;
          case ZDialogConstantUtil.REQUEST_CROP_CODE://裁剪图片结果处理
             if(data !=null) {
                compressUri(imgPath);
             }
          break;
          default:
          break;
       }
    }
    

    四、裁剪

    裁剪完之后,会将裁剪结果保存到一个临时的png文件,所以这里我们需要定义一个全局变量来方便后面操作。

    private String imgPath;
    
    /**
     *开启Android截图的功能
     */
    private voidcropImageUri(Uri uri) {
       try{
          String dirfilePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator+"Ibooker"+ File.separator+"photoCache";
          if(!FileUtil.isFileExist(dirfilePath)) {//创建文件夹
             FileUtil.createSDDirs(dirfilePath);
          }
          // 照片URL地址-获取系统时间 然后将裁剪后的图片保存至指定的文件夹
          imgPath= dirfilePath + File.separator+ System.currentTimeMillis() +".jpg";
          File imgFile =newFile(imgPath);
          Uri imageUri = Uri.fromFile(imgFile);
          /**
           *开启Android截图的功能
           */
          Intent intent =newIntent("com.android.camera.action.CROP");
          // 添加权限 Android7.0+
          intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
          intent.setDataAndType(uri,"image/*");
          intent.putExtra("crop","true");
          intent.putExtra("aspectX",100);
          intent.putExtra("aspectY",100);
          intent.putExtra("outputX",500);// X方向上的比例
          intent.putExtra("outputY",500);// Y方向上的比例
          intent.putExtra("scale", true);// 是否保留比例
          // 输出路径
          intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);
          // 输出格式
          intent.putExtra("outputFormat",Bitmap.CompressFormat.JPEG.toString());
          // 不启用人脸识别
          intent.putExtra("noFaceDetection", true);
          intent.putExtra("return-data", false);
          // 竖屏
          intent.putExtra(MediaStore.Images.ImageColumns.ORIENTATION,0);
          startActivityForResult(intent,ZDialogConstantUtil.REQUEST_CROP_CODE);
       }catch(Exception e) {
          e.printStackTrace();
       }
    }
    

    五、压缩、转换

    一般情况下,拍照或者本地相册选择的图片,都是非常大,那么可以通过之前引入的ZBitmap进行图片的处理,最后将压缩之后的Bitmap转换成Base64,可以方便平台对图片进行加密处理,而且可以兼容不同的后台。

    这里我们需要定义三个全局变量,保存处理之后的结果。

    private String imgfile;// 保存Base64转码结果
    private Bitmap picBitmap;// 保持Bitmap
    private ProDialog mProDialog;// 进度条Dialog由ZDialog提供
    private ExecutorService mExecutorService= Executors.newCachedThreadPool();// 线程池,管理线程,因为压缩和转码过程是一个耗时的过程,所以放在子线程中完成。
    
    // 压缩图片并显示
    private void compressUri(finalString imgPath) {
       if(!TextUtils.isEmpty(imgPath)) {
          showDialog();
          mProDialog.setMessage("图片压缩中...");
          Thread thread =new Thread(new Runnable() {
             @Override
             public void run() {
                try{
                   picBitmap= BitmapFun.imgPathToBitmap1(imgPath,BitmapFun.BitmapFunConfig.RGB_565);
                   if(picBitmap==null) {
                      imgfile="";
                   } else {
                      // 压缩
                      picBitmap=    BitmapFun.compressBitmapByRatio(picBitmap,400,400,BitmapFun.BitmapFunConfig.RGB_565,BitmapFun.Bitmap   FunCompressFormat.JPEG);
                      // 将字节转换成base64码
                      imgfile= bitMapToBase64(picBitmap);
                   }
                }catch(Exception e) {
                   e.printStackTrace();
                }
                myHandler.sendEmptyMessage(3);
             }
          });
          if(mExecutorService==null || mExecutorService.isShutdown())
             mExecutorService= Executors.newCachedThreadPool();
          mExecutorService.execute(thread);
       }
    }
    
    // 将BitMap转换成Base64
    private String bitMapToBase64(Bitmap bitmap) {
       try{
          ByteArrayOutputStream stream =new ByteArrayOutputStream();
          bitmap.compress(Bitmap.CompressFormat.JPEG,80,stream);
          byte[] bytes = stream.toByteArray();
          // 将字节转换成base64码
          String str = Base64.encodeToString(bytes,Base64.DEFAULT);
          stream.close();
          return str;
       }catch(Exception e) {
          e.printStackTrace();
       }
       return null;
    }
    

    这里要解释两点:

    1、进度条Dialog的使用:ProDialog是由ZDialog提供。

    private ProDialog mProDialog;
    //显示进度条Dialog
    private void showDialog() {
       if(mProDialog==null)
          mProDialog=new ProDialog(this);
       mProDialog.setProgressBar(true).showProDialog();
    }
    
    //关闭进度条Dialog
    private void closeDialog() {
       if(mProDialog!=null)
          mProDialog.closeProDialog();
    }
    

    2、线程池的理解:newCachedThreadPool()这表示,这里使用的是缓存线程池,在程序中产生线程交由线程池进行管理。只要在相应的界面退出时候,关闭线程池即可。

    六、显示、上传

    压缩,转换处理,都是在子线程中完成,所以要显示图片需要切换到主线程当中,这里采用的是Handler,完成主线程的操作,同时实现图片上传功能。

    /**
    *通过handler执行主线程
    */
    private MyHandler myHandler=new MyHandler(this);
       private static class MyHandler extends Handler {
          // 定义一个对象用来引用Activity中的方法
          private final WeakReference mActivity;
          MyHandler(Activity activity) {
             mActivity=new WeakReference<>(activity);
          }
          @Override
          public void handleMessage(Message msg) {
             super.handleMessage(msg);
             ChoosePictrueActivity currentActivity = (ChoosePictrueActivity)mActivity.get();
             currentActivity.closeDialog();
             currentActivity.closeChoosePictrueDialog();
             switch(msg.what) {
                case 3:// 上传图片
                   if(currentActivity.picBitmap!=null) {
                      // 更新用户图像,picImg(ImageView控件)
                      currentActivity.picImg.setImageBitmap(currentActivity.picBitmap);
                      //上传至服务端,updatePic方法
                      currentActivity.updatePic();
                   } else {
                     Toast.makeText(currentActivity,"图片获取失败",Toast.LENGTH_SHORT).show();
                }
             break;
          }
       }
    }
    

    WeakReference为弱引用,至于弱引用到底有什么好处,这里只提一点,回收,弱引用使用完之后,系统会马上进行回收。

    updatePic()上传图片,不同的平台,不同的框架可能处理的方式不同,这里不再给出具体的出来代码。

    七、最后,处理定义变量

    // 在onStop方法中关闭Dialog
    @Override
    protected void onStop() {
       super.onStop();
       closeChoosePictrueDialog();
       closeDialog();
    }
    
    // 进行系统回收,关闭线程池
    @Override
    protected void onDestroy() {
       super.onDestroy();
       if(picBitmap!=null)
          picBitmap.recycle();
       System.gc();
       if(mExecutorService!=null)
          mExecutorService.shutdownNow();
    }
    

    Github地址
    阅读原文


    微信公众号:书客创作

    相关文章

      网友评论

        本文标题:【Android】图片选择、裁剪、压缩、上传

        本文链接:https://www.haomeiwen.com/subject/gsjwyxtx.html