花了两天时间看了下android的图片裁剪功能的实现。其实刚开始做这个我挺虚的,以为整个功能都需要自己写出来,但查了些资料,发现android已经提供了裁剪功能,需要的话自己调用就成了。soga,这下轻松多了。
原文地址请保留
首先推荐几篇博客
Android大图片裁剪终极解决方案
要想弄明白裁剪功能,这系列博客非常重要,你可以不看我下面总结的,但你一定要看他这系列的几篇文章。
Android 图片裁剪功能实现详解(类似QQ自定义头像裁剪)
这篇也不错,比较喜欢他的注释。虽然也有些误导,比如说他有一段对setData,setType和setDataAndType方法的区别疑问,他说两种写法一样效果,我就信了,害得我找bug找了两个小时,一直怀疑别的参数出问题,实际上是这两个方法的差别。这一点后面会说。
其他的相关博客有很多,但基本上大同小异,包括我这篇。有了上面的两个博客,就可以大概搞懂这方面的原理了。
我要写的,就是多写一些注释,改变一些写法,增加点说明,积累点经验,为了自己以后方便重温自己做过的东西,而已。
不再浪费你我的时间,开始了。
丑得不能忍的分割区
RyanHoo的Demo写的很详细。但要学习,我习惯先把代码简化,看的逻辑清楚些。我选择了最适应自己需求的选择大图片裁剪的部分代码
我测试的简化代码
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
2 xmlns:tools="http://schemas.android.com/tools"
3 android:layout_width="match_parent"
4 android:layout_height="match_parent"
5 tools:context=".MainActivity" >
6
7 <Button
8 android:id="@+id/button"
9 android:layout_width="wrap_content"
10 android:layout_height="wrap_content"
11 android:text="click me!" />
12
13 <ImageView
14 android:id="@+id/imageview"
15 android:layout_width="match_parent"
16 android:layout_height="match_parent"
17 android:scaleType="centerInside" />
18
19 </LinearLayout>
1 public class MainActivity extends Activity implements OnClickListener {
2
3 private Uri imageUri;
4 private static final String IMAGE_FILE_LOCATION = "file:///sdcard/temp.jpg";
5 private Button btn;
6 private ImageView iv;
7
8 @Override
9 protected void onCreate(Bundle savedInstanceState) {
10 super.onCreate(savedInstanceState);
11 setContentView(R.layout.activity_main);
12 btn = (Button) findViewById(R.id.button);
13 btn.setOnClickListener(this);
14 imageUri = Uri.parse(IMAGE_FILE_LOCATION);
15 iv = (ImageView) findViewById(R.id.imageview);
16 }
17
18 @Override
19 public boolean onCreateOptionsMenu(Menu menu) {
20 // Inflate the menu; this adds items to the action bar if it is present.
21 getMenuInflater().inflate(R.menu.activity_main, menu);
22 return true;
23 }
24
25 @Override
26 public void onClick(View v) {
27 // TODO Auto-generated method stub
28
29 // 试着改成打开自己写的图片浏览器
30 switch (v.getId()) {
31 case R.id.button:
32 // 这段代码使用ACTION_GET_CONTENT和ACTION_PICK效果相同
33 Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
34 // Intent intent = new Intent(Intent.ACTION_PICK, null);
35
36 // 如果使用com.android.camera.action.CROP 则直接打开裁剪照片的activity 那么可以用自己的图片浏览器选择图片 传入参数并使用之
37 // Intent intent = new Intent("com.android.camera.action.CROP");
38
39 // 如果不设置type,则 ACTION_GET_CONTENT 会弹出异常FATAL EXCEPTION:main android.content.ActivityNotFoundException
40 // 而 ACTION_PICK 会弹出可用程序列表 但没有打开图片相关的程序(在我的两个设备上是这样)
41 intent.setType("image/*");
42
43 // 设置在开启的Intent中设置显示的view可裁剪
44 // 这段代码里设置成false也能裁剪啊。。。这是为什么?懂的给我讲讲了
45 // 这段注释掉就不会跳转到裁剪的activity
46 intent.putExtra("crop", "true");
47
48 // 设置x,y的比例,截图方框就按照这个比例来截 若设置为0,0,或者不设置 则自由比例截图
49 intent.putExtra("aspectX", 2);
50 intent.putExtra("aspectY", 1);
51
52 // 裁剪区的宽和高 其实就是裁剪后的显示区域 若裁剪的比例不是显示的比例,则自动压缩图片填满显示区域。若设置为0,0 就不显示。若不设置,则按原始大小显示
53 intent.putExtra("outputX", 200);
54 intent.putExtra("outputY", 100);
55
56 // 不知道有啥用。。可能会保存一个比例值 需要相关文档啊
57 intent.putExtra("scale", true);
58
59 // true的话直接返回bitmap,可能会很占内存 不建议
60 intent.putExtra("return-data", false);
61 // 上面设为false的时候将MediaStore.EXTRA_OUTPUT即"output"关联一个Uri
62 intent.putExtra("output", imageUri);
63 // 看参数即可知道是输出格式
64 intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
65 // 面部识别 这里用不上
66 intent.putExtra("noFaceDetection", false);
67
68 // 想从Activity中获得返回数据,在启动Activity时候使用startActivityForResult方法
69 // 1为请求代码,可以是任意值,个人感觉用资源id会比较清楚,而且不会重复 比如当前控件的R.id.button
70 startActivityForResult(intent, 1);
71 break;
72 default:
73 break;
74 }
75 }
76
77 @Override
78 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
79 // TODO Auto-generated method stub
80 super.onActivityResult(requestCode, resultCode, data);
81 if (resultCode != Activity.RESULT_OK) {// result is not correct
82 return;
83 } else {
84 switch (requestCode) {
85 case 1:
86 if (imageUri != null) {
87 Bitmap bitmap = decodeUriAsBitmap(imageUri);
88 // 把解析到的位图显示出来
89 iv.setImageBitmap(bitmap);
90 }
91 break;
92 default:
93 break;
94 }
95
96 }
97 }
98
99 private Bitmap decodeUriAsBitmap(Uri uri) {
100 Bitmap bitmap = null;
101 try {
102 // 先通过getContentResolver方法获得一个ContentResolver实例,
103 // 调用openInputStream(Uri)方法获得uri关联的数据流stream
104 // 把上一步获得的数据流解析成为bitmap
105 bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri));
106 } catch (FileNotFoundException e) {
107 e.printStackTrace();
108 return null;
109 }
110 return bitmap;
111 }
112 }
其实用法看前面的博客就已经很清楚了,这里主要部分就是Intent附加数据的具体含义解释与使用方法,我都尽量写在代码的注释当中了。
再丑也得忍的分割线
我后来想只调用裁剪窗口,而选图片的时候使用自己写的图片选择器,那么这个参数怎么传,怎么调用裁剪activity呢?
使用裁剪功能用"com.android.camera.action.CROP"就可以。
传图片的话有两个方法,一个是intent直接传bitmap数据,另一个是传uri。
1 private void startCropIntent(String path) throws FileNotFoundException {
2 Bitmap bmp = BitmapFactory.decodeFile(path);
5 Intent intent = new Intent("com.android.camera.action.CROP"); 9 // Intent传输的bytes不能超过40k。不建议这样 无法处理大图
10 intent.putExtra("data", bmp);
11 // intent.setData(uri);
12 // intent.setType("image/*");
13 intent.setDataAndType(imageUri, "image/*");
14 intent.putExtra("crop", "true");
15 intent.putExtra("aspectX", 2);
16 intent.putExtra("aspectY", 1);
17 intent.putExtra("outputX", 300);
18 intent.putExtra("outputY", 150);
19 // 设置为true直接返回bitmap
20 intent.putExtra("return-data", true);
24 startActivityForResult(intent, 1);
25 }
26
27 @Override
28 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
29 super.onActivityResult(requestCode, resultCode, data);
30 if (resultCode != Activity.RESULT_OK) {// result is not correct
31 return;
32 } else {
33 switch (requestCode) {
34 case 1:
40 Bundle bundle = data.getExtras();
41 Bitmap bitmap = bundle.getParcelable("data");
42 iv.setImageBitmap(bitmap);
43
44 break;
45
46 default:
47 break;
48 }
49 }
51 }
这里参数path是选择的图片的绝对路径。
这种方法有局限性,因为intent传递的数据不超过40k,只能选择40k以下的图片裁剪
还是使用uri比较好
1 private void startCropIntent(String path) throws FileNotFoundException {
2 Bitmap bmp = BitmapFactory.decodeFile(path);
3
4 File file = new File(path);
5 Intent intent = new Intent("com.android.camera.action.CROP");
7 Uri uri = Uri.fromFile(file);// parse(pathUri);13 intent.setDataAndType(uri, "image/*");
14 intent.putExtra("crop", "true");
15 intent.putExtra("aspectX", 2);
16 intent.putExtra("aspectY", 1);
17 intent.putExtra("outputX", 300);
18 intent.putExtra("outputY", 150);
19 // 设置为true直接返回bitmap
20 intent.putExtra("return-data", false);
21 // 上面设为false的时候将MediaStore.EXTRA_OUTPUT关联一个Uri
22 intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
23 intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
24 startActivityForResult(intent, 1);
25 }
26
27 @Override
28 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
29 super.onActivityResult(requestCode, resultCode, data);
30 if (resultCode != Activity.RESULT_OK) {// result is not correct
31 return;
32 } else {
33 switch (requestCode) {
34 case 1:
35 if (imageUri != null) {
36 Bitmap bitmap = decodeUriAsBitmap(imageUri);
37 // 把解析到的位图显示出来
38 iv.setImageBitmap(bitmap);
39 }
44 break;
45
46 default:
47 break;
48 }
49 }
50
51 }
52
53 private Bitmap decodeUriAsBitmap(Uri uri) {
54 Bitmap bitmap = null;
55 try {
59 bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri));
60 } catch (FileNotFoundException e) {
61 e.printStackTrace();
62 return null;
63 }
64 return bitmap;
65 }
这里需要注意
intent.setData(uri);intent.setType("image/*");和
intent.setDataAndType(uri, "image/*");是有区别的。
我开始以为两个方法一样的,但看了源码就清楚了。
1 public Intent setDataAndType(Uri data, String type) {
2 mData = data;
3 mType = type;
4 return this;
5 }
1 public Intent setData(Uri data) {
2 mData = data;
3 mType = null;
4 return this;
5 }
1 public Intent setType(String type) {
2 mData = null;
3 mType = type;
4 return this;
5 }
好了,调用系统裁剪就这么些内容,这方面除了文档太难找,也没什么难的。
下面就是不调用系统,自己写一个裁剪图片的工具了。