Android 拍照、图片压缩(兼容7.0)
公司的项目最紧要用到百度Face(人脸识别),人脸采集后要上传到后台,但是总是报错
人脸识别的sdk接入整理好再秀,反正也是被秀的头皮发麻0.0
毕竟现在的手机注重的是高像素0.0 动不动一个图片两三个M
简单介绍一下常用的图片压缩的框架
LuBan
https://github.com/Curzibn/Luban
该框架对图片进行单纯的裁剪压缩,但是裁切成多少,压缩成多少却很难控制好,裁切过头图片太小,质量压缩过头则显示效果太差。所以,他通过微信朋友圈发送近100张不同分辨率图片,对比原图与微信压缩后的图片逆向推算出来的压缩算法,具体的算法实现在项目中有详细说明介绍。使用上,支持普通调用方式外,也支持RxJava!
implementation 'top.zibin:Luban:1.1.8'// 项目中导入
异步调用
Luban内部采用IO线程进行图片压缩,外部调用只需设置好结果监听即可:
Luban.with(this)
.load(photos)
.ignoreBy(100)
.setTargetDir(getPath())
.filter(new CompressionPredicate() {
@Override
public boolean apply(String path) {
return !(TextUtils.isEmpty(path) || path.toLowerCase().endsWith(".gif"));
}
})
.setCompressListener(new OnCompressListener() {
@Override
public void onStart() {
// TODO 压缩开始前调用,可以在方法内启动 loading UI
}
@Override
public void onSuccess(File file) {
// TODO 压缩成功后调用,返回压缩后的图片文件
}
@Override
public void onError(Throwable e) {
// TODO 当压缩过程出现问题时调用
}
}).launch();
同步调用
同步方法请尽量避免在主线程调用以免阻塞主线程,下面以rxJava调用为例
Flowable.just(photos)
.observeOn(Schedulers.io())
.map(new Function<List<String>, List<File>>() {
@Override public List<File> apply(@NonNull List<String> list) throws Exception {
// 同步方法直接返回压缩后的文件
return Luban.with(MainActivity.this).load(list).get();
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe();
上面都是github上的内容,同步调用没有测试,只是用了异步调用
上菜了
1. 拍照
2. 图片压缩
手机6.0以上对访问危险系数较高的权限进行了更加人性化的操作(扯淡)
权限问题好像还没搞明白,直接上代码吧
// 照片所在的Uri地址
private Uri imageUri;
//照片的url
private String imageUrl;
onSinglePermissionApply(Manifest.permission.CAMERA);//调用下面方法,并传入申请的权限
/**
* 单个权限申请
*
* @param permission : Manifest.permission.CAMERA
*
*/
private void onSinglePermissionApply(String permission) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{permission}, 0);
} else {
LogUtil.e("权限已经申请");//== Log.e();
doCamera();// 已经有权限了,直接调用打开照相机的方法
}
}
}
/**
* 打开相机,并设置照片存放的位置
*
*/
private void doCamera() {
String SDState = Environment.getExternalStorageState();
if (SDState.equals(Environment.MEDIA_MOUNTED)) {
imageUrl = ImageUtil.getJpgFileAbsolutePath(System.currentTimeMillis() + ".jpg");//
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
imageUri = Uri.fromFile(new File(imageUrl));
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, 10001);
} else {
Toast.makeText(this, "内存卡不存在!", Toast.LENGTH_LONG).show();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 10001) {
//图片压缩开始
ImageUtil.luBanCompress(this, imageUrl, new ImageCompressCallBack() {
@Override
public void onSucceed(String data) {
//压缩后的结果回调
Log.e("ffffffffffff", data + "");
}
@Override
public void onFailure(String msg) {
Log.e("ffffffffffff111", msg + "");
}
});
}
}
/**
*
*权限处理的回调,测试版,就简单的Log一下
*
*/
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == 0) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
LogUtil.e("申请通过");
doCamera();
} else {
LogUtil.e("被拒绝了");
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
// android 7.0系统解决拍照的问题 在onCreate()里调用
private void initPhotoError() {
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
builder.detectFileUriExposure();
}
}
上面图片压缩和文件的存放位置封装了一下
import android.content.Context;
import android.os.Environment;
import android.text.TextUtils;
import com.fsnzzz.imp.ImageCompressCallBack;
import java.io.File;
import top.zibin.luban.Luban;
import top.zibin.luban.OnCompressListener;
/**
* Created by Fsnzzz on 2019/1/14
*/
public class ImageUtil {
private static String rootPath = "TakePhoto";
private static String playPath = "";
private final static String PHOTO_JPG_BASEPATH = "/" + rootPath + "/img/";
private final static String PHOTO_COMPRESS_JPG_BASEPATH = "/" + rootPath + "/CompressImgs/";
public static void setRootPath(String rootPath) {
ImageUtil.rootPath = rootPath;
}
/**
* @param fileName :System.currentTimeMillis() + ".jpg"//用时间戳
* @return 获取保存原始文件的位置
*/
public static String getJpgFileAbsolutePath(String fileName) {
if (TextUtils.isEmpty(fileName)) {
throw new NullPointerException("fileName isEmpty");
}
String photoPath = "";
if (!fileName.endsWith(".jpg")) {
fileName = fileName + ".jpg";
}
String fileBasePath = Environment.getExternalStorageDirectory().getAbsolutePath() + PHOTO_JPG_BASEPATH;
File file = new File(fileBasePath);
if (!file.exists()) {
file.mkdirs();
}
photoPath = fileBasePath + fileName;
return photoPath;
}
/**
* 获取保存压缩图片文件的位置
*
* @return
*/
public static String getCompressJpgFileAbsolutePath() {
String fileBasePath = Environment.getExternalStorageDirectory().getAbsolutePath() + PHOTO_COMPRESS_JPG_BASEPATH;
File file = new File(fileBasePath);
if (!file.exists()) {
file.mkdirs();
}
return fileBasePath;
}
/**
* 使用LuBan 压缩单张图片
*
* @param context
* @param imageUrl
* @param compressCallBack 结果回调
*/
public static void luBanCompress(Context context, String imageUrl, ImageCompressCallBack compressCallBack) {
Luban.with(context)
.load(new File(imageUrl))
.ignoreBy(100)//低于100的图片不用压缩
.putGear(3)//设置压缩级别 默认是3
.setTargetDir(ImageUtil.getCompressJpgFileAbsolutePath())
.setCompressListener(new OnCompressListener() {
@Override
public void onStart() {
}
@Override
public void onSuccess(File file) {
File oldfile = new File(imageUrl);//删掉旧文件
if (oldfile.exists()) {
oldfile.delete();
}
compressCallBack.onSucceed(file.getAbsolutePath());
}
@Override
public void onError(Throwable e) {
compressCallBack.onFailure(e.getMessage());
}
}).launch();
}
ImageCompressCallBack 接口类,做回调
/**
* @author Fsnzzz
* @date 2019/1/15
* @month 01
* @package com.fsnzzz.imp
*/
public interface ImageCompressCallBack {
void onSucceed(String data);
void onFailure(String msg);
}
只是简单地实现,没有做什么代码优化。
前几天也学了试着写了一个压缩工具类,采样、比例、质量等压缩,下次整理好再搞0.0