【Android】图片选择、裁剪、压缩、上传
作者:邹峰立,微博:zrunker,邮箱:zrunker@yahoo.com,微信公众号:书客创作,个人平台:www.ibooker.cc。
书客创作
在文章开始讲解之前,大家需要先进行阅读一下这几篇文章,因为这篇文章中所提到的一些依赖与下面几篇文章有关,当然这些依赖大家也可以自行定义。
【Android】图片选择、裁剪、压缩、上传(一)图片选择(拍照+本地相册)Dialog封装,使用
一、引入资源包
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();
}
微信公众号:书客创作