Android实现图片裁切
介绍
在应用开发中,如果涉及到个人信息,头像一般是不可避免的,类似这种情况,我们就需要用到图片裁切的功能,实现头像裁切,然后上传给服务器。
一般裁切的做法就是图层叠加选取框,然后根据坐标,计算裁切区域,通过图形函数裁切,既然了解大概原理,造轮子的事情就不做了,上github找开源库,发现了一个叫做edmodo/cropper的库,是原生实现的裁切。
地址:https://github.com/edmodo/cropper
但是使用后发现这个库还存以下两个主要问题,体验就很不好了。
1、图片太大会出现无法显示
2、图片过小又无法自适应
难道就没有更好的办法了?
又百度了一下,发现原来android的Intent已经自带有裁切的action了,而且体验非常好,只需要在Intent附上参数就可以实现相册/相机裁切图片。
地址:https://github.com/ryanhoo/PhotoCropper
原理
1、请看参数

2、拍照裁切需要先将结果保存在本地,然后再读取保存的结果裁切,否则默认情况下拍照直接返回的结果是缩略图。
3、通过onActivityResult来处理结果。
实现
参考的项目是经过重构的,但我总觉得看的晕,所以demo就全部放在一个页面,加上了个人理解注释,方便学习。
CropParams类
package com.example.cropimage; import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Environment; public class CropParams { public static final String CROP_TYPE = "image/*";
public static final String OUTPUT_FORMAT = Bitmap.CompressFormat.JPEG
.toString(); public static final int DEFAULT_ASPECT = 1;
public static final int DEFAULT_OUTPUT = 300; public Uri uri; public String type;
public String outputFormat;
public String crop; public boolean scale;
public boolean returnData;
public boolean noFaceDetection;
public boolean scaleUpIfNeeded; public int aspectX;
public int aspectY; public int outputX;
public int outputY; public CropParams() {
uri = Uri.fromFile(Environment.getExternalStorageDirectory())
.buildUpon().appendPath("crop_cache_file.jpg").build();
type = CROP_TYPE;
outputFormat = OUTPUT_FORMAT;
crop = "true";
scale = true;
returnData = false;
noFaceDetection = true;
scaleUpIfNeeded = true;
aspectX = DEFAULT_ASPECT;
aspectY = DEFAULT_ASPECT;
outputX = DEFAULT_OUTPUT;
outputY = DEFAULT_OUTPUT;
}
}
核心代码
package com.example.cropimage; import java.io.File;
import java.io.FileNotFoundException; import android.support.v7.app.ActionBarActivity;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ImageView;
import android.widget.Toast; public class MainActivity extends ActionBarActivity {
ImageView imageView1;
CropParams mCropParams = new CropParams();
public static final int REQUEST_CROP = 127;
public static final int REQUEST_CAMERA = 128; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView1 = (ImageView) findViewById(R.id.imageView1); // 弹出提示框选择照片
final String[] arrayFruit = new String[] { "拍照", "从相册选择照片" };
Dialog alertDialog = new AlertDialog.Builder(MainActivity.this)
.setIcon(R.drawable.ic_launcher)
.setItems(arrayFruit, new DialogInterface.OnClickListener() { @Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case 0:
// 进入相机
startActivityForResult(
buildCaptureIntent(mCropParams.uri),
REQUEST_CAMERA);
break;
case 1:
// 进入相册
startActivityForResult(
buildCropFromGalleryIntent(mCropParams),
REQUEST_CROP);
break;
default:
break;
}
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
}
}).create();
alertDialog.show();
} @Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
} @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_CANCELED) {
Toast.makeText(this, "Crop canceled!", Toast.LENGTH_LONG).show();
} else if (resultCode == Activity.RESULT_OK) {
switch (requestCode) {
case REQUEST_CROP:
Log.d("cropImage", "Photo cropped!");
Toast.makeText(this, "Photo cropped!", Toast.LENGTH_LONG)
.show();
imageView1.setImageURI(mCropParams.uri);
break;
case REQUEST_CAMERA:
Intent intent = buildCropFromUriIntent(mCropParams);
startActivityForResult(intent, REQUEST_CROP);
break;
}
}
super.onActivityResult(requestCode, resultCode, data);
} @Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
} /**
* 结束后删除临时裁切图片,或者不删除,用来干别的。
*/
@Override
protected void onDestroy() {
File file = new File(mCropParams.uri.getPath());
if (file.exists()) {
boolean result = file.delete();
if (result)
Log.i("cropImage", "Cached crop file cleared.");
else
Log.e("cropImage", "Failed to clear cached crop file."); } else {
Log.w("cropImage",
"Trying to clear cached crop file but it does not exist.");
}
super.onDestroy();
} /**
* 创建裁切Intent
*
* @param action
* 操作
* @param params
* 参数
* @return
*/
public static Intent buildCropIntent(String action, CropParams params) {
return new Intent(action, null)
.setDataAndType(params.uri, params.type)
// .setType(params.type)
.putExtra("crop", params.crop).putExtra("scale", params.scale)
.putExtra("aspectX", params.aspectX)
.putExtra("aspectY", params.aspectY)
.putExtra("outputX", params.outputX)
.putExtra("outputY", params.outputY)
.putExtra("return-data", params.returnData)
.putExtra("outputFormat", params.outputFormat)
.putExtra("noFaceDetection", params.noFaceDetection)
.putExtra("scaleUpIfNeeded", params.scaleUpIfNeeded)
.putExtra(MediaStore.EXTRA_OUTPUT, params.uri);
} /**
* 这一步是在相机拍照完成之后调用,注意action。
*
* @param params
* @return
*/
public static Intent buildCropFromUriIntent(CropParams params) {
return buildCropIntent("com.android.camera.action.CROP", params);
} /**
* 创建相册裁切Intent
*
* @param params
* 奥秘全在params里
* @return
*/
public static Intent buildCropFromGalleryIntent(CropParams params) {
return buildCropIntent(Intent.ACTION_GET_CONTENT, params);
} /**
* 创建相机拍照Intent,由于相机拍照直接返回的是缩略图,所以一般的做法是拍照保存在本地之后,通过uri再读取一次
*
* @param uri
* 保存路径
* @return
*/
public static Intent buildCaptureIntent(Uri uri) {
return new Intent(MediaStore.ACTION_IMAGE_CAPTURE).putExtra(
MediaStore.EXTRA_OUTPUT, uri);
} /**
* 解析uri成bitmap
*
* @param context
* @param uri
* @return
*/
public static Bitmap decodeUriAsBitmap(Context context, Uri uri) {
if (context == null || uri == null)
return null; Bitmap bitmap;
try {
bitmap = BitmapFactory.decodeStream(context.getContentResolver()
.openInputStream(uri));
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
}
return bitmap;
}
}
完成之后看起来是这样的

demo地址:
链接:http://pan.baidu.com/s/1c0xqxEw 密码:u2ot
参考:
http://ryanhoo.github.io/blog/2014/05/26/the-ultimate-approach-to-crop-photos-on-android-1/
Android实现图片裁切的更多相关文章
- Android压缩图片到100K以下并保持不失真的高效方法
前言:目前一般手机的相机都能达到800万像素,像我的Galaxy Nexus才500万像素,拍摄的照片也有1.5M左右.这么大的照片上传到服务器,不仅浪费流量,同时还浪费时间. 在开发Android企 ...
- 仿优酷Android客户端图片左右滑动(自动滑动)
最终效果: 页面布局main.xml: <?xml version="1.0" encoding="utf-8"?> <LinearLayou ...
- Javascript图片裁切
最近浏览了不少网站的图片裁切效果,大部分的做法如下图所示(借用一张脚本之家的图片),通过改变裁切框的大小来选取合适的位置. 但本文介绍的是另外一种裁切方式,裁切框由开发者决定,图片大小由用户决定,通过 ...
- 用ticons指令结合ImageMagickDisplay工具批量生成Android适应图片
用ticons指令结合ImageMagickDisplay工具批量生成Android适应图片 ticons的用法可以百度 这里记录下具体的编译方法 在安装了ticons和ImageMagickDisp ...
- Android 实现图片画画板
本文主要讲述了Android 实现图片画画板 设计项目布局: <RelativeLayout xmlns:android="http://schemas.android.com/apk ...
- 关于Android中图片大小、内存占用与drawable文件夹关系的研究与分析
原文:关于Android中图片大小.内存占用与drawable文件夹关系的研究与分析 相关: Android drawable微技巧,你所不知道的drawable的那些细节 经常会有朋友问我这个问题: ...
- android 拉伸图片
Android拉伸图片用的是9.png格式的图片,这种图片可以指定图片的那一部分拉伸,那一部分显示内容,美工给的小图片也能有很好的显示效果. 原背景图片 可以看到原背景图片很小,即使在再长的文字,背景 ...
- Android大图片裁剪终极解决方案(上:原理分析)
转载声明:Ryan的博客文章欢迎您的转载,但在转载的同时,请注明文章的来源出处,不胜感激! :-) http://my.oschina.net/ryanhoo/blog/86842 约几个月前,我正 ...
- 图片裁切插件jCrop的使用心得(三)
在这一篇里,我来具体讲讲代码该如何写. 下面是jCrop的初始化代码 //图片裁剪插件Jcrop初始化 function initJcrop() { // 图片加载完成 document.getEle ...
随机推荐
- Guid和Int还有Double、Date的ToString方法的常见格式
Guid的常见格式: 1.Guid.NewGuid().ToString("N") 结果为: 38bddf48f43c48588e0d78761eaa1ce6 2.Gu ...
- jquery中如何退出each循环
在for循环中我们用continue退出当前循环,进入下一循环.用break跳出所有循环. 可是在jQuery中却并没有这两条命令,那么如何退出each循环呢? 经过查询得知: 在jQuery中用 r ...
- Kerberos的组件和术语(翻译和注解)
之所以要翻译这篇文章,是因为提到了一些通常于对Kerberos协议简介性质的文章所没有提到的细节,而这些细节对于理解Kerberos的工作原理,以及Kerberos协议实现的使用都是很有必要的. 1. ...
- HDOJ 1028 Ignatius and the Princess III (母函数)
Ignatius and the Princess III Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K ...
- tomcat与IIS在多IP服务器下的支持
同一个服务器下,双IP(或更多IP),实现tomcat与IIS公用80端口. 操作其实也很简单的,首先禁用iis的套接字池,iis绑定一个ip,然后tomcat在绑定另一个ip,最后重启下服务器即可. ...
- JAVA 异常对于性能的影响
陶炳哲 - MAY 12, 2015 在对OneAPM的客户做技术支持时,我们常常会看到很多客户根本没意识到的异常.在消除了这些异常之后,代码运行速度与以前相比大幅提升.这让我们产生一种猜测,就是在代 ...
- Ruby中的语句中断和返回
李哲 - APRIL 28, 2015 return,break,next 这几个关键字的使用都涉及到跳出作用域的问题,而他们的不同 则在于不同的关键字跳出去的目的作用域的不同,因为有代码块则导致有一 ...
- opencv face-detection 代码分析 (1)人脸识别后的数据
2014,3,16 老师的工作建议如下: 1. 与四民沟通下,把openCV这边的源代码和调用接口发给四民同时抄送给我. 2. 根据openCV的实时检测结果,实现对屏幕的调整(下周一前基本实 ...
- WebSocket API简介
WebSocket是html5新增加的一种通信协议,目前流行的浏览器都支持这个协议,例如Chrome,Safari,Firefox,Opera,IE等等,对该协议支持最早的应该是chrome,从chr ...
- lintcode:背包问题II
背包问题II 给出n个物品的体积A[i]和其价值V[i],将他们装入一个大小为m的背包,最多能装入的总价值有多大? 注意事项 A[i], V[i], n, m均为整数.你不能将物品进行切分.你所挑选的 ...