伴职创作「banzhi.cc」书客创作[ibooker.cc]互联网科技

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

2017-10-11  本文已影响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地址
阅读原文


微信公众号:书客创作
上一篇下一篇

猜你喜欢

热点阅读