很早就有有看到有朋友在讨论QQ头像的裁剪上传是怎么实现的,吼吼,之前小马也没做过,好奇之下学习下,发现以前项目中有类型的功能,结合官方文档里面的解释,就更好玩了,急急忙忙写51里的,今天听变3主题曲,重新记录在WorePress里,记录编程的过程,希望能与更多的朋友交流学习,文章中的截图是动态的,我晕………….貌似WorePress不支持Gif还是怎么了,今天不动了,jekyll也不支持,啊啊啊啊……想看动态效果看下51:http://mzh3344258.blog.51cto.com/blog/1823534/808837 不看的跳过, 如果觉得有用,请记得分享出去,只求能让更多开发的朋友学习使用,小马先谢谢了,一样的,先看下效果图(效果图小马不解释了,直接流水写下去,小马是直接在模拟器里写的,能在真机上使用,因为很简单),再看代码是怎么实现的:
一:主布局界面
二:点击控件触发事件后效果图
三:拍照完之后效果图
四:裁剪界面效果图
五:点击相册后返回的图片效果图
六:裁剪完从相册PICK的保存后的效果图
下面直接来看下主控制类代码,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
package com.xiaoma.piccut.demo; import java.io.File; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.provider.MediaStore; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.ImageButton; import android.widget.ImageView; /** * @Title: PicCutDemoActivity.java * @Package com.xiaoma.piccut.demo * @Description: 图片裁剪功能测试 * @author XiaoMa */ public class PicCutDemoActivity extends Activity implements OnClickListener { private ImageButton ib = null; private ImageView iv = null; private Button btn = null; private String tp = null; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //初始化 init(); } /** * 初始化方法实现 */ private void init() { ib = (ImageButton) findViewById(R.id.imageButton1); iv = (ImageView) findViewById(R.id.imageView1); btn = (Button) findViewById(R.id.button1); ib.setOnClickListener(this); iv.setOnClickListener(this); btn.setOnClickListener(this); } /** * 控件点击事件实现 * * 因为有朋友问不同控件的背景图裁剪怎么实现, * 我就在这个地方用了三个控件,只为了自己记录学习 * 大家觉得没用的可以跳过啦 */ @Override public void onClick(View v) { switch (v.getId()) { case R.id.imageButton1: ShowPickDialog(); break; case R.id.imageView1: ShowPickDialog(); break; case R.id.button1: ShowPickDialog(); break; default: break; } } /** * 选择提示对话框 */ private void ShowPickDialog() { new AlertDialog.Builder(this) .setTitle("设置头像...") .setNegativeButton("相册", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); /** * 刚开始,我自己也不知道ACTION_PICK是干嘛的,后来直接看Intent源码, * 可以发现里面很多东西,Intent是个很强大的东西,大家一定仔细阅读下 */ Intent intent = new Intent(Intent.ACTION_PICK, null); /** * 下面这句话,与其它方式写是一样的效果,如果: * intent.setData(MediaStore.Images.Media.EXTERNAL_CONTENT_URI); * intent.setType(""image/*");设置数据类型 * 如果朋友们要限制上传到服务器的图片类型时可以直接写如:"image/jpeg 、 image/png等的类型" * 这个地方小马有个疑问,希望高手解答下:就是这个数据URI与类型为什么要分两种形式来写呀?有什么区别? */ intent.setDataAndType( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*"); startActivityForResult(intent, 1); } }) .setPositiveButton("拍照", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { dialog.dismiss(); /** * 下面这句还是老样子,调用快速拍照功能,至于为什么叫快速拍照,大家可以参考如下官方 * 文档,you_sdk_path/docs/guide/topics/media/camera.html * 我刚看的时候因为太长就认真看,其实是错的,这个里面有用的太多了,所以大家不要认为 * 官方文档太长了就不看了,其实是错的,这个地方小马也错了,必须改正 */ Intent intent = new Intent( MediaStore.ACTION_IMAGE_CAPTURE); //下面这句指定调用相机拍照后的照片存储的路径 intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri .fromFile(new File(Environment .getExternalStorageDirectory(), "xiaoma.jpg"))); startActivityForResult(intent, 2); } }).show(); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { // 如果是直接从相册获取 case 1: startPhotoZoom(data.getData()); break; // 如果是调用相机拍照时 case 2: File temp = new File(Environment.getExternalStorageDirectory() + "/xiaoma.jpg"); startPhotoZoom(Uri.fromFile(temp)); break; // 取得裁剪后的图片 case 3: /** * 非空判断大家一定要验证,如果不验证的话, * 在剪裁之后如果发现不满意,要重新裁剪,丢弃 * 当前功能时,会报NullException,小马只 * 在这个地方加下,大家可以根据不同情况在合适的 * 地方做判断处理类似情况 * */ if(data != null){ setPicToView(data); } break; default: break; } super.onActivityResult(requestCode, resultCode, data); } /** * 裁剪图片方法实现 * @param uri */ public void startPhotoZoom(Uri uri) { /* * 至于下面这个Intent的ACTION是怎么知道的,大家可以看下自己路径下的如下网页 * yourself_sdk_path/docs/reference/android/content/Intent.html * 直接在里面Ctrl+F搜:CROP ,之前小马没仔细看过,其实安卓系统早已经有自带图片裁剪功能, * 是直接调本地库的,小马不懂C C++ 这个不做详细了解去了,有轮子就用轮子,不再研究轮子是怎么 * 制做的了...吼吼 */ Intent intent = new Intent("com.android.camera.action.CROP"); intent.setDataAndType(uri, "image/*"); //下面这个crop=true是设置在开启的Intent中设置显示的VIEW可裁剪 intent.putExtra("crop", "true"); // aspectX aspectY 是宽高的比例 intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); // outputX outputY 是裁剪图片宽高 intent.putExtra("outputX", 150); intent.putExtra("outputY", 150); intent.putExtra("return-data", true); startActivityForResult(intent, 3); } /** * 保存裁剪之后的图片数据 * @param picdata */ private void setPicToView(Intent picdata) { Bundle extras = picdata.getExtras(); if (extras != null) { Bitmap photo = extras.getParcelable("data"); Drawable drawable = new BitmapDrawable(photo); /** * 下面注释的方法是将裁剪之后的图片以Base64Coder的字符方式上 * 传到服务器,QQ头像上传采用的方法跟这个类似 */ /*ByteArrayOutputStream stream = new ByteArrayOutputStream(); photo.compress(Bitmap.CompressFormat.JPEG, 60, stream); byte[] b = stream.toByteArray(); // 将图片流以字符串形式存储下来 tp = new String(Base64Coder.encodeLines(b)); /**这个地方大家可以写下给服务器上传图片的实现,直接把tp直接上传就可以了, *服务器处理的方法是服务器那边的事了,吼吼 *如果下载到的服务器的数据还是以Base64Coder的形式的话,可以用以下方式转换 *为我们可以用的图片类型就OK啦...吼吼 */ Bitmap dBitmap = BitmapFactory.decodeFile(tp); Drawable drawable = new BitmapDrawable(dBitmap); */ ib.setBackgroundDrawable(drawable); iv.setBackgroundDrawable(drawable); } } } |
下面来看下裁剪中用到的类,大家详细看下头注释:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
package com.xiaoma.piccut.demo; /** * 下面这些注释是下载这个类的时候本来就有的,本来要删除的,但看了下竟然是license,吼吼, * 好东西,留在注释里,以备不时之用,大家有需要加license的可以到下面的网址找哦 */ //EPL, Eclipse Public License, V1.0 or later, http://www.eclipse.org/legal //LGPL, GNU Lesser General Public License, V2.1 or later, http://www.gnu.org/licenses/lgpl.html //GPL, GNU General Public License, V2 or later, http://www.gnu.org/licenses/gpl.html //AL, Apache License, V2.0 or later, http://www.apache.org/licenses //BSD, BSD License, http://www.opensource.org/licenses/bsd-license.php /** * A Base64 encoder/decoder. * * <p> * This class is used to encode and decode data in Base64 format as described in RFC 1521. * * <p> * Project home page: <a href="http://www.source-code.biz/base64coder/java/">www.source-code.biz/base64coder/java</a><br> * Author: Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland<br> * Multi-licensed: EPL / LGPL / GPL / AL / BSD. */ /** * 这个类在上面注释的网址中有,大家可以自行下载下,也可以直接用这个, * 公开的Base64Coder类(不用深究它是怎么实现的, * 还是那句话,有轮子直接用轮子),好用的要死人了... * 小马也很无耻的引用了这个网址下的东东,吼吼... * @Title: Base64Coder.java * @Package com.xiaoma.piccut.demo * @Description: TODO * @author XiaoMa */ public class Base64Coder { //The line separator string of the operating system. private static final String systemLineSeparator = System.getProperty("line.separator"); //Mapping table from 6-bit nibbles to Base64 characters. private static char[] map1 = new char[64]; static { int i=0; for (char c='A'; c<='Z'; c++) map1[i++] = c; for (char c='a'; c<='z'; c++) map1[i++] = c; for (char c='0'; c<='9'; c++) map1[i++] = c; map1[i++] = '+'; map1[i++] = '/'; } //Mapping table from Base64 characters to 6-bit nibbles. private static byte[] map2 = new byte[128]; static { for (int i=0; i<map2.length; i++) map2[i] = -1; for (int i=0; i<64; i++) map2[map1[i]] = (byte)i; } /** * Encodes a string into Base64 format. * No blanks or line breaks are inserted. * @param s A String to be encoded. * @return A String containing the Base64 encoded data. */ public static String encodeString (String s) { return new String(encode(s.getBytes())); } /** * Encodes a byte array into Base 64 format and breaks the output into lines of 76 characters. * This method is compatible with <code>sun.misc.BASE64Encoder.encodeBuffer(byte[])</code>. * @param in An array containing the data bytes to be encoded. * @return A String containing the Base64 encoded data, broken into lines. */ public static String encodeLines (byte[] in) { return encodeLines(in, 0, in.length, 76, systemLineSeparator); } /** * Encodes a byte array into Base 64 format and breaks the output into lines. * @param in An array containing the data bytes to be encoded. * @param iOff Offset of the first byte in <code>in</code> to be processed. * @param iLen Number of bytes to be processed in <code>in</code>, starting at <code>iOff</code>. * @param lineLen Line length for the output data. Should be a multiple of 4. * @param lineSeparator The line separator to be used to separate the output lines. * @return A String containing the Base64 encoded data, broken into lines. */ public static String encodeLines (byte[] in, int iOff, int iLen, int lineLen, String lineSeparator) { int blockLen = (lineLen*3) / 4; if (blockLen <= 0) throw new IllegalArgumentException(); int lines = (iLen+blockLen-1) / blockLen; int bufLen = ((iLen+2)/3)*4 + lines*lineSeparator.length(); StringBuilder buf = new StringBuilder(bufLen); int ip = 0; while (ip < iLen) { int l = Math.min(iLen-ip, blockLen); buf.append (encode(in, iOff+ip, l)); buf.append (lineSeparator); ip += l; } return buf.toString(); } /** * Encodes a byte array into Base64 format. * No blanks or line breaks are inserted in the output. * @param in An array containing the data bytes to be encoded. * @return A character array containing the Base64 encoded data. */ public static char[] encode (byte[] in) { return encode(in, 0, in.length); } /** * Encodes a byte array into Base64 format. * No blanks or line breaks are inserted in the output. * @param in An array containing the data bytes to be encoded. * @param iLen Number of bytes to process in <code>in</code>. * @return A character array containing the Base64 encoded data. */ public static char[] encode (byte[] in, int iLen) { return encode(in, 0, iLen); } /** * Encodes a byte array into Base64 format. * No blanks or line breaks are inserted in the output. * @param in An array containing the data bytes to be encoded. * @param iOff Offset of the first byte in <code>in</code> to be processed. * @param iLen Number of bytes to process in <code>in</code>, starting at <code>iOff</code>. * @return A character array containing the Base64 encoded data. */ public static char[] encode (byte[] in, int iOff, int iLen) { int oDataLen = (iLen*4+2)/3; // output length without padding int oLen = ((iLen+2)/3)*4; // output length including padding char[] out = new char[oLen]; int ip = iOff; int iEnd = iOff + iLen; int op = 0; while (ip < iEnd) { int i0 = in[ip++] & 0xff; int i1 = ip < iEnd ? in[ip++] & 0xff : 0; int i2 = ip < iEnd ? in[ip++] & 0xff : 0; int o0 = i0 >>> 2; int o1 = ((i0 & 3) << 4) | (i1 >>> 4); int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6); int o3 = i2 & 0x3F; out[op++] = map1[o0]; out[op++] = map1[o1]; out[op] = op < oDataLen ? map1[o2] : '='; op++; out[op] = op < oDataLen ? map1[o3] : '='; op++; } return out; } /** * Decodes a string from Base64 format. * No blanks or line breaks are allowed within the Base64 encoded input data. * @param s A Base64 String to be decoded. * @return A String containing the decoded data. * @throws IllegalArgumentException If the input is not valid Base64 encoded data. */ public static String decodeString (String s) { return new String(decode(s)); } /** * Decodes a byte array from Base64 format and ignores line separators, tabs and blanks. * CR, LF, Tab and Space characters are ignored in the input data. * This method is compatible with <code>sun.misc.BASE64Decoder.decodeBuffer(String)</code>. * @param s A Base64 String to be decoded. * @return An array containing the decoded data bytes. * @throws IllegalArgumentException If the input is not valid Base64 encoded data. */ public static byte[] decodeLines (String s) { char[] buf = new char[s.length()+3]; int p = 0; for (int ip = 0; ip < s.length(); ip++) { char c = s.charAt(ip); if (c != ' ' && c != '\r' && c != '\n' && c != '\t') buf[p++] = c; } while ((p % 4) != 0) buf[p++] = '0'; return decode(buf, 0, p); } /** * Decodes a byte array from Base64 format. * No blanks or line breaks are allowed within the Base64 encoded input data. * @param s A Base64 String to be decoded. * @return An array containing the decoded data bytes. * @throws IllegalArgumentException If the input is not valid Base64 encoded data. */ public static byte[] decode (String s) { return decode(s.toCharArray()); } /** * Decodes a byte array from Base64 format. * No blanks or line breaks are allowed within the Base64 encoded input data. * @param in A character array containing the Base64 encoded data. * @return An array containing the decoded data bytes. * @throws IllegalArgumentException If the input is not valid Base64 encoded data. */ public static byte[] decode (char[] in) { return decode(in, 0, in.length); } /** * Decodes a byte array from Base64 format. * No blanks or line breaks are allowed within the Base64 encoded input data. * @param in A character array containing the Base64 encoded data. * @param iOff Offset of the first character in <code>in</code> to be processed. * @param iLen Number of characters to process in <code>in</code>, starting at <code>iOff</code>. * @return An array containing the decoded data bytes. * @throws IllegalArgumentException If the input is not valid Base64 encoded data. */ public static byte[] decode (char[] in, int iOff, int iLen) { if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4."); while (iLen > 0 && in[iOff+iLen-1] == '=') iLen--; int oLen = (iLen*3) / 4; byte[] out = new byte[oLen]; int ip = iOff; int iEnd = iOff + iLen; int op = 0; while (ip < iEnd) { int i0 = in[ip++]; int i1 = in[ip++]; int i2 = ip < iEnd ? in[ip++] : 'A'; int i3 = ip < iEnd ? in[ip++] : 'A'; if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127) throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); int b0 = map2[i0]; int b1 = map2[i1]; int b2 = map2[i2]; int b3 = map2[i3]; if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0) throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); int o0 = ( b0 <<2) | (b1>>>4); int o1 = ((b1 & 0xf)<<4) | (b2>>>2); int o2 = ((b2 & 3)<<6) | b3; out[op++] = (byte)o0; if (op<oLen) out[op++] = (byte)o1; if (op<oLen) out[op++] = (byte)o2; } return out; } //Dummy constructor. private Base64Coder() {} } // end class Base64Coder |
最后,小DEMO源码此下载:http://mzh3344258.blog.51cto.com/blog/1823534/808837 (页面最下方小Demo源码即是),有需要的朋友可以下载下来,共同交流学习,也恳请高人回答下小马在文章注释中提出的问题,谢谢,文章是小马急急忙忙在家写的,在南京一家网吧发的,晕…不是我有多用功,这边下雨,讨厌下雨,下雨我就郁闷,来网吧玩的,顺带发下文章,吼吼,该玩的时候死命的玩,该工作的时候死命的工作,年轻时疯狂,老了不后悔,吼吼,加油加油!大家工作,也注意身体健康,嘿嘿,你懂的,不解释…哈哈,年轻时逼自己一把!!!
- 本文固定链接: https://www.xuanyusong.com/archives/1743
- 转载请注明: 小马果 于 雨松MOMO程序研究院 发表
捐 赠如果您愿意花20块钱请我喝一杯咖啡的话,请用手机扫描二维码即可通过支付宝直接向我捐款哦。
调用系统的剪裁 图片会变模糊吧!
三星,小米不支持自带图库裁剪哦
有空试试。
给力转
哈哈,谢谢谢谢,还有文章在总结的,欢迎指出、批评文章不足之处…
周末我也写一篇IOS的 蛤蛤。。
好文要转 加油小马!!
电脑中的贝赛尔曲线的DEMO找不找了,不然也发这了….吼吼,加油加油!!!