UCrop.of(sourceUri, destinationUri)
.withAspectRatio(16, 9)
.withMaxResultSize(maxWidth, maxHeight)
.start(context);
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK && requestCode == UCrop.REQUEST_CROP) {
final Uri resultUri = UCrop.getOutput(data);
} else if (resultCode == UCrop.RESULT_ERROR) {
final Throwable cropError = UCrop.getError(data);
}
}
框架本身直接接收图片文件的uri,返回intent.
而在实际开发中,往往是从图库里选择图片,或者调用照相机拍照后,再跳到裁剪的页面.
onActivityResult里的代码有很多共通的地方,需要进行封装.
public static void pickFromGallery(Activity context) {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
context.startActivityForResult(Intent.createChooser(intent, "选择图片"), REQUEST_SELECT_PICTURE);
}
Uri mDestinationUri = buildUri();
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE)
.putExtra(MediaStore.EXTRA_OUTPUT, mDestinationUri);
context.startActivityForResult(intent, REQUEST_CAMERA);
这里要注意,一定要指定拍照后生成的图片的uri路径,并保证uri路径可访问.
如果不指定uri,那么系统会使用默认uri,并放在intent的data里,在onActivityResult里返回.但这只是原生系统的,各厂商改rom,这里经常会返回intent为null或者intent里的data为null.
图片uri的生成路径: sd卡目录/项目名/crop/xxx-yyy.jpg, 其中yyy可以通过System.currentTimeMills 来生成,保证每次都不同.
生成该路径的方法封装成:buildUri();
注意onActivityResult会调用两次,一次是选择了原图后,第二次是照片裁剪好后.
为了简化使用,我们希望达到的结果是,调用时用一行代码就写好了.
通用的处理逻辑封装到CropUtils..handleResult()里,而最终剪切成功和失败的处理提取成接口cropHandler.
如此一来,只要在具体的activity里实现CropHandler,处理剪切后的图片即可.
注意: 从图片库中选择的uri是可以通过intent传递回来的,而拍照时由于指定了uri,所以不需要从intent里拿,直接取存在静态变量uri里的值就行.
//使用时的调用:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
CropUtils.handleResult(this,cropHandler,requestCode,resultCode,data);
}
//方法的封装:
public static void handleResult(Activity context, CropHandler cropHandler, int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK) {
if (requestCode == REQUEST_SELECT_PICTURE) {//第一次,选择图片后返回
final Uri selectedUri = data.getData();
if (selectedUri != null) {
startCropActivity(context, data.getData());
} else {
Toast.makeText(context, "Cannot retrieve selected image", Toast.LENGTH_SHORT).show();
}
} else if (requestCode == UCrop.REQUEST_CROP) {//第二次返回,图片已经剪切好
Uri finalUri = UCrop.getOutput(data);
cropHandler.handleCropResult(finalUri,config.tag);
config = new CropConfig();//参数重置
} else if (requestCode == REQUEST_CAMERA) {//第一次,拍照后返回,因为设置了MediaStore.EXTRA_OUTPUT,所以data为null,数据直接就在uri中
startCropActivity(context, uri);
}
}
if (resultCode == UCrop.RESULT_ERROR) {
cropHandler.handleCropError(data);
config = new CropConfig();//参数重置
}
}
//CropHandler接口:
public interface CropHandler {
void handleCropResult(Uri uri,int tag);
void handleCropError(Intent data);
}
startCropActivity(context, uri);
主要动作是设置参数.在原框架里,是通过Urcop类的链式调用来设置到intent里,然后传递到UcropActivity里的.
我们外面封装时,为简化起见,将众多参数封装成一个类 CropConfig,并在初始化时赋予默认值.
public static class CropConfig{
public int aspectRatioX = 1;
public int aspectRatioY = 1;
public int maxWidth = 1080;
public int maxHeight = 1920;
//options
public int tag ;
public boolean isOval = false;//是否为椭圆
public int quality = 80;
public boolean hideBottomControls = true;//底部操作条
public boolean showGridLine = true;//内部网格
public boolean showOutLine = true;//最外面的矩形线
...
}
private static void startCropActivity(Activity context, Uri sourceUri) {
Uri mDestinationUri = buildUri();
UCrop uCrop = UCrop.of(sourceUri, mDestinationUri);
uCrop.withAspectRatio(config.aspectRatioX,config.aspectRatioY);
uCrop.withMaxResultSize(config.maxWidth,config.maxHeight);
UCrop.Options options = new UCrop.Options();
options.setCompressionFormat(Bitmap.CompressFormat.JPEG);
options.setAllowedGestures(UCropActivity.SCALE,UCropActivity.NONE,UCropActivity.NONE);
options.setCompressionQuality(config.quality);
options.setOvalDimmedLayer(config.isOval);
options.setShowCropGrid(config.showGridLine);
options.setHideBottomControls(config.hideBottomControls);
options.setShowCropFrame(config.showOutLine);
uCrop.withOptions(options);
uCrop.start(context);
}
最终使用者在使用时,实现 cropHandler: 基本上是作为成员变量使用.
private CropUtils.CropHandler cropHandler = new CropUtils.CropHandler() {
@Override
public void handleCropResult(Uri data, int tag) {
if (data != null) {
ResultActivity.startWithUri(context, data);
} else {
Toast.makeText(context, R.string.toast_cannot_retrieve_cropped_image, Toast.LENGTH_SHORT).show();
}
}
@Override
public void handleCropError(Intent data) {
final Throwable cropError = UCrop.getError(data);
if (cropError != null) {
Log.e("dd", "handleCropError: ", cropError);
Toast.makeText(context, cropError.getMessage(), Toast.LENGTH_LONG).show();
} else {
Toast.makeText(context, R.string.toast_unexpected_error, Toast.LENGTH_SHORT).show();
}
}
};
如果一个activity中有多个地方需要图片裁剪功能,那么就需要以tag区分.
public static void pickFromGallery(Activity context) {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
context.startActivityForResult(Intent.createChooser(intent, "选择图片"), REQUEST_SELECT_PICTURE);
}
public static void pickFromCamera(Activity context) {
Uri mDestinationUri = buildUri();
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE)
.putExtra(MediaStore.EXTRA_OUTPUT, mDestinationUri);
context.startActivityForResult(intent, REQUEST_CAMERA);
}
public static void pickFromGallery(Activity context,CropConfig config,int type) {
if (config != null){
CropUtils.config = config;
}
setType(type);
pickFromGallery(context);
}
宽高比为1:1
圆形蒙版,直接看到最终圆形头像显示效果,
没有表格线和最外框线
没有底部多余的操作条
宽高最大为400像素 – 能满足大多数应用了.
public static void pickAvatarFromGallery(Activity context){
pickFromGallery(context,null,TYPE_AVATAR);
}
private static void setType(int type) {
if (type == TYPE_AVATAR){
config.isOval = true;
config.aspectRatioX = 1;
config.aspectRatioY = 1;
config.hideBottomControls = true;
config.showGridLine = false;
config.showOutLine = false;
config.maxHeight = 400;
config.maxWidth = 400;
}else if (type == TYPE_NORMAL){//什么都不用做
}else {
}
}