最近有看到有朋友在討論QQ頭像的裁剪上傳是怎麼實現的,吼吼,小馬也沒做過,好奇之下學習下,發現以前項目中有類型的功能,結合官方文檔裡面的解釋,就更好玩瞭,周末,急急忙忙寫的,記錄在博客裡,希望能與大傢交流學習,也懇請高手能解答小馬在代碼註釋中提出的疑問,不管有沒有人回答,小馬先謝謝瞭,一樣的,先看下效果圖(效果圖小馬不解釋瞭,直接流水寫下去,小馬是直接在模擬器裡寫的,能在真機上使用,因為很簡單),再看代碼是怎麼實現的:
一:主佈局界面
二:點擊控件觸發事件後效果圖
三:拍照完之後效果圖
四:裁剪界面效果圖
五:點擊相冊後返回的圖片效果圖
六:裁剪完從相冊PICK的保存後的效果圖
下面直接來看下主控制類代碼,如下:
1. package com.xiaoma.piccut.demo;
2.
3. import java.io.File;
4. import android.app.Activity;
5. import android.app.AlertDialog;
6. import android.content.DialogInterface;
7. import android.content.Intent;
8. import android.graphics.Bitmap;
9. import android.graphics.drawable.BitmapDrawable;
10. import android.graphics.drawable.Drawable;
11. import android.net.Uri;
12. import android.os.Bundle;
13. import android.os.Environment;
14. import android.provider.MediaStore;
15. import android.view.View;
16. import android.view.View.OnClickListener;
17. import android.widget.Button;
18. import android.widget.ImageButton;
19. import android.widget.ImageView;
20. /**
21. * @Title: PicCutDemoActivity.java
22. * @Package com.xiaoma.piccut.demo
23. * @Description: 圖片裁剪功能測試
24. * @author XiaoMa
25. */
26. public class PicCutDemoActivity extends Activity implements OnClickListener {
27.
28. private ImageButton ib = null;
29. private ImageView iv = null;
30. private Button btn = null;
31. private String tp = null;
32.
33.
34. /** Called when the activity is first created. */
35. @Override
36. public void onCreate(Bundle savedInstanceState) {
37. super.onCreate(savedInstanceState);
38. setContentView(R.layout.main);
39. //初始化
40. init();
41. }
42.
43. /**
44. * 初始化方法實現
45. */
46. private void init() {
47. ib = (ImageButton) findViewById(R.id.imageButton1);
48. iv = (ImageView) findViewById(R.id.imageView1);
49. btn = (Button) findViewById(R.id.button1);
50. ib.setOnClickListener(this);
51. iv.setOnClickListener(this);
52. btn.setOnClickListener(this);
53. }
54.
55.
56. /**
57. * 控件點擊事件實現
58. *
59. * 因為有朋友問不同控件的背景圖裁剪怎麼實現,
60. * 我就在這個地方用瞭三個控件,隻為瞭自己記錄學習
61. * 大傢覺得沒用的可以跳過啦
62. */
63. @Override
64. public void onClick(View v) {
65. switch (v.getId()) {
66. case R.id.imageButton1:
67. ShowPickDialog();
68. break;
69. case R.id.imageView1:
70. ShowPickDialog();
71. break;
72. case R.id.button1:
73. ShowPickDialog();
74. break;
75.
76. default:
77. break;
78. }
79. }
80.
81. /**
82. * 選擇提示對話框
83. */
84. private void ShowPickDialog() {
85. new AlertDialog.Builder(this)
86. .setTitle("設置頭像…")
87. .setNegativeButton("相冊", new DialogInterface.OnClickListener() {
88. public void onClick(DialogInterface dialog, int which) {
89. dialog.dismiss();
90. /**
91. * 剛開始,我自己也不知道ACTION_PICK是幹嘛的,後來直接看Intent源碼,
92. * 可以發現裡面很多東西,Intent是個很強大的東西,大傢一定仔細閱讀下
93. */
94. Intent intent = new Intent(Intent.ACTION_PICK, null);
95.
96. /**
97. * 下面這句話,與其它方式寫是一樣的效果,如果:
98. * intent.setData(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
99. * intent.setType(""image/*");設置數據類型
100. * 如果朋友們要限制上傳到服務器的圖片類型時可以直接寫如:"image/jpeg 、 image/png等的類型"
101. * 這個地方小馬有個疑問,希望高手解答下:就是這個數據URI與類型為什麼要分兩種形式來寫呀?有什麼區別?
102. */
103. intent.setDataAndType(
104. MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
105. "image/*");
106. startActivityForResult(intent, 1);
107.
108. }
109. })
110. .setPositiveButton("拍照", new DialogInterface.OnClickListener() {
111. public void onClick(DialogInterface dialog, int whichButton) {
112. dialog.dismiss();
113. /**
114. * 下面這句還是老樣子,調用快速拍照功能,至於為什麼叫快速拍照,大傢可以參考如下官方
115. * 文檔,you_sdk_path/docs/guide/topics/media/camera.html
116. * 我剛看的時候因為太長就認真看,其實是錯的,這個裡面有用的太多瞭,所以大傢不要認為
117. * 官方文檔太長瞭就不看瞭,其實是錯的,這個地方小馬也錯瞭,必須改正
118. */
119. Intent intent = new Intent(
120. MediaStore.ACTION_IMAGE_CAPTURE);
121. //下面這句指定調用相機拍照後的照片存儲的路徑
122. intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri
123. .fromFile(new File(Environment
124. .getExternalStorageDirectory(),
125. "xiaoma.jpg")));
126. startActivityForResult(intent, 2);
127. }
128. }).show();
129. }
130.
131. @Override
132. protected void onActivityResult(int requestCode, int resultCode, Intent data) {
133. switch (requestCode) {
134. // 如果是直接從相冊獲取
135. case 1:
136. startPhotoZoom(data.getData());
137. break;
138. // 如果是調用相機拍照時
139. case 2:
140. File temp = new File(Environment.getExternalStorageDirectory()
141. + "/xiaoma.jpg");
142. startPhotoZoom(Uri.fromFile(temp));
143. break;
144. // 取得裁剪後的圖片
145. case 3:
146. /**
147. * 非空判斷大傢一定要驗證,如果不驗證的話,
148. * 在剪裁之後如果發現不滿意,要重新裁剪,丟棄
149. * 當前功能時,會報NullException,小馬隻
150. * 在這個地方加下,大傢可以根據不同情況在合適的
151. * 地方做判斷處理類似情況
152. *
153. */
154. if(data != null){
155. setPicToView(data);
156. }
157. break;
158. default:
159. break;
160.
161. }
162. super.onActivityResult(requestCode, resultCode, data);
163. }
164.
165. /**
166. * 裁剪圖片方法實現
167. * @param uri
168. */
169. public void startPhotoZoom(Uri uri) {
170. /*
171. * 至於下面這個Intent的ACTION是怎麼知道的,大傢可以看下自己路徑下的如下網頁
172. * yourself_sdk_path/docs/reference/android/content/Intent.html
173. * 直接在裡面Ctrl+F搜:CROP ,之前小馬沒仔細看過,其實安卓系統早已經有自帶圖片裁剪功能,
174. * 是直接調本地庫的,小馬不懂C C++ 這個不做詳細瞭解去瞭,有輪子就用輪子,不再研究輪子是怎麼
175. * 制做的瞭…吼吼
176. */
177. Intent intent = new Intent("com.android.camera.action.CROP");
178. intent.setDataAndType(uri, "image/*");
179. //下面這個crop=true是設置在開啟的Intent中設置顯示的VIEW可裁剪
180. intent.putExtra("crop", "true");
181. // aspectX aspectY 是寬高的比例
182. intent.putExtra("aspectX", 1);
183. intent.putExtra("aspectY", 1);
184. // outputX outputY 是裁剪圖片寬高
185. intent.putExtra("outputX", 150);
186. intent.putExtra("outputY", 150);
187. intent.putExtra("return-data", true);
188. startActivityForResult(intent, 3);
189. }
190.
191. /**
192. * 保存裁剪之後的圖片數據
193. * @param picdata
194. */
195. private void setPicToView(Intent picdata) {
196. Bundle extras = picdata.getExtras();
197. if (extras != null) {
198. Bitmap photo = extras.getParcelable("data");
199. Drawable drawable = new BitmapDrawable(photo);
200.
201. /**
202. * 下面註釋的方法是將裁剪之後的圖片以Base64Coder的字符方式上
203. * 傳到服務器,QQ頭像上傳采用的方法跟這個類似
204. */
205.
206. /*ByteArrayOutputStream stream = new ByteArrayOutputStream();
207. photo.compress(Bitmap.CompressFormat.JPEG, 60, stream);
208. byte[] b = stream.toByteArray();
209. // 將圖片流以字符串形式存儲下來
210.
211. tp = new String(Base64Coder.encodeLines(b));
212. 這個地方大傢可以寫下給服務器上傳圖片的實現,直接把tp直接上傳就可以瞭,
213. 服務器處理的方法是服務器那邊的事瞭,吼吼
214.
215. 如果下載到的服務器的數據還是以Base64Coder的形式的話,可以用以下方式轉換
216. 為我們可以用的圖片類型就OK啦…吼吼
217. Bitmap dBitmap = BitmapFactory.decodeFile(tp);
218. Drawable drawable = new BitmapDrawable(dBitmap);
219. */
220. ib.setBackgroundDrawable(drawable);
221. iv.setBackgroundDrawable(drawable);
222. }
223. }
224.
225. }
下面來看下裁剪中用到的類,大傢詳細看下頭註釋:
1. package com.xiaoma.piccut.demo;
2.
3.
4.
5.
6. /**
7. * 下面這些註釋是下載這個類的時候本來就有的,本來要刪除的,但看瞭下竟然是license,吼吼,
8. * 好東西,留在註釋裡,以備不時之用,大傢有需要加license的可以到下面的網址找哦
9. */
10.
11. //EPL, Eclipse Public License, V1.0 or later, http://www.eclipse.org/legal
12. //LGPL, GNU Lesser General Public License, V2.1 or later, http://www.gnu.org/licenses/lgpl.html
13. //GPL, GNU General Public License, V2 or later, http://www.gnu.org/licenses/gpl.html
14. //AL, Apache License, V2.0 or later, http://www.apache.org/licenses
15. //BSD, BSD License, http://www.opensource.org/licenses/bsd-license.php
16. /**
17. * A Base64 encoder/decoder.
18. *
19. * <p>
20. * This class is used to encode and decode data in Base64 format as described in RFC 1521.
21. *
22. * <p>
23. * Project home page: <a href="http://www.source-code.biz/base64coder/java/">www.source-code.biz/base64coder/java</a><br>
24. * Author: Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland<br>
25. * Multi-licensed: EPL / LGPL / GPL / AL / BSD.
26. */
27.
28. /**
29. * 這個類在上面註釋的網址中有,大傢可以自行下載下,也可以直接用這個,
30. * 公開的Base64Coder類(不用深究它是怎麼實現的,
31. * 還是那句話,有輪子直接用輪子),好用的要死人瞭…
32. * 小馬也很無恥的引用瞭這個網址下的東東,吼吼…
33. * @Title: Base64Coder.java
34. * @Package com.xiaoma.piccut.demo
35. * @Description: TODO
36. * @author XiaoMa
37. */
38.
39. public class Base64Coder {
40.
41. //The line separator string of the operating system.
42. private static final String systemLineSeparator = System.getProperty("line.separator");
43.
44. //Mapping table from 6-bit nibbles to Base64 characters.
45. private static char[] map1 = new char[64];
46. static {
47. int i=0;
48. for (char c='A'; c<='Z'; c++) map1[i++] = c;
49. for (char c='a'; c<='z'; c++) map1[i++] = c;
50. for (char c='0'; c<='9'; c++) map1[i++] = c;
51. map1[i++] = '+'; map1[i++] = '/'; }
52.
53. //Mapping table from Base64 characters to 6-bit nibbles.
54. private static byte[] map2 = new byte[128];
55. static {
56. for (int i=0; i<map2.length; i++) map2[i] = -1;
57. for (int i=0; i<64; i++) map2[map1[i]] = (byte)i; }
58.
59. /**
60. * Encodes a string into Base64 format.
61. * No blanks or line breaks are inserted.
62. * @param s A String to be encoded.
63. * @return A String containing the Base64 encoded data.
64. */
65. public static String encodeString (String s) {
66. return new String(encode(s.getBytes())); }
67.
68. /**
69. * Encodes a byte array into Base 64 format and breaks the output into lines of 76 characters.
70. * This method is compatible with <code>sun.misc.BASE64Encoder.encodeBuffer(byte[])</code>.
71. * @param in An array containing the data bytes to be encoded.
72. * @return A String containing the Base64 encoded data, broken into lines.
73. */
74. public static String encodeLines (byte[] in) {
75. return encodeLines(in, 0, in.length, 76, systemLineSeparator); }
76.
77. /**
78. * Encodes a byte array into Base 64 format and breaks the output into lines.
79. * @param in An array containing the data bytes to be encoded.
80. * @param iOff Offset of the first byte in <code>in</code> to be processed.
81. * @param iLen Number of bytes to be processed in <code>in</code>, starting at <code>iOff</code>.
82. * @param lineLen Line length for the output data. Should be a multiple of 4.
83. * @param lineSeparator The line separator to be used to separate the output lines.
84. * @return A String containing the Base64 encoded data, broken into lines.
85. */
86. public static String encodeLines (byte[] in, int iOff, int iLen, int lineLen, String lineSeparator) {
87. int blockLen = (lineLen*3) / 4;
88. if (blockLen <= 0) throw new IllegalArgumentException();
89. int lines = (iLen+blockLen-1) / blockLen;
90. int bufLen = ((iLen+2)/3)*4 + lines*lineSeparator.length();
91. StringBuilder buf = new StringBuilder(bufLen);
92. int ip = 0;
93. while (ip < iLen) {
94. int l = Math.min(iLen-ip, blockLen);
95. buf.append (encode(in, iOff+ip, l));
96. buf.append (lineSeparator);
97. ip += l; }
98. return buf.toString(); }
99.
100. /**
101. * Encodes a byte array into Base64 format.
102. * No blanks or line breaks are inserted in the output.
103. * @param in An array containing the data bytes to be encoded.
104. * @return A character array containing the Base64 encoded data.
105. */
106. public static char[] encode (byte[] in) {
107. return encode(in, 0, in.length); }
108.
109. /**
110. * Encodes a byte array into Base64 format.
111. * No blanks or line breaks are inserted in the output.
112. * @param in An array containing the data bytes to be encoded.
113. * @param iLen Number of bytes to process in <code>in</code>.
114. * @return A character array containing the Base64 encoded data.
115. */
116. public static char[] encode (byte[] in, int iLen) {
117. return encode(in, 0, iLen); }
118.
119. /**
120. * Encodes a byte array into Base64 format.
121. * No blanks or line breaks are inserted in the output.
122. * @param in An array containing the data bytes to be encoded.
123. * @param iOff Offset of the first byte in <code>in</code> to be processed.
124. * @param iLen Number of bytes to process in <code>in</code>, starting at <code>iOff</code>.
125. * @return A character array containing the Base64 encoded data.
126. */
127. public static char[] encode (byte[] in, int iOff, int iLen) {
128. int oDataLen = (iLen*4+2)/3; // output length without padding
129. int oLen = ((iLen+2)/3)*4; // output length including padding
130. char[] out = new char[oLen];
131. int ip = iOff;
132. int iEnd = iOff + iLen;
133. int op = 0;
134. while (ip < iEnd) {
135. int i0 = in[ip++] & 0xff;
136. int i1 = ip < iEnd ? in[ip++] & 0xff : 0;
137. int i2 = ip < iEnd ? in[ip++] & 0xff : 0;
138. int o0 = i0 >>> 2;
139. int o1 = ((i0 & 3) << 4) | (i1 >>> 4);
140. int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6);
141. int o3 = i2 & 0x3F;
142. out[op++] = map1[o0];
143. out[op++] = map1[o1];
144. out[op] = op < oDataLen ? map1[o2] : '='; op++;
145. out[op] = op < oDataLen ? map1[o3] : '='; op++; }
146. return out; }
147.
148. /**
149. * Decodes a string from Base64 format.
150. * No blanks or line breaks are allowed within the Base64 encoded input data.
151. * @param s A Base64 String to be decoded.
152. * @return A String containing the decoded data.
153. * @throws IllegalArgumentException If the input is not valid Base64 encoded data.
154. */
155. public static String decodeString (String s) {
156. return new String(decode(s)); }
157.
158. /**
159. * Decodes a byte array from Base64 format and ignores line separators, tabs and blanks.
160. * CR, LF, Tab and Space characters are ignored in the input data.
161. * This method is compatible with <code>sun.misc.BASE64Decoder.decodeBuffer(String)</code>.
162. * @param s A Base64 String to be decoded.
163. * @return An array containing the decoded data bytes.
164. * @throws IllegalArgumentException If the input is not valid Base64 encoded data.
165. */
166. public static byte[] decodeLines (String s) {
167. char[] buf = new char[s.length()+3];
168. int p = 0;
169. for (int ip = 0; ip < s.length(); ip++) {
170. char c = s.charAt(ip);
171. if (c != ' ' && c != '\r' && c != '\n' && c != '\t')
172. buf[p++] = c; }
173. while ((p % 4) != 0)
174. buf[p++] = '0';
175.
176. return decode(buf, 0, p); }
177.
178. /**
179. * Decodes a byte array from Base64 format.
180. * No blanks or line breaks are allowed within the Base64 encoded input data.
181. * @param s A Base64 String to be decoded.
182. * @return An array containing the decoded data bytes.
183. * @throws IllegalArgumentException If the input is not valid Base64 encoded data.
184. */
185. public static byte[] decode (String s) {
186. return decode(s.toCharArray()); }
187.
188. /**
189. * Decodes a byte array from Base64 format.
190. * No blanks or line breaks are allowed within the Base64 encoded input data.
191. * @param in A character array containing the Base64 encoded data.
192. * @return An array containing the decoded data bytes.
193. * @throws IllegalArgumentException If the input is not valid Base64 encoded data.
194. */
195. public static byte[] decode (char[] in) {
196. return decode(in, 0, in.length); }
197.
198. /**
199. * Decodes a byte array from Base64 format.
200. * No blanks or line breaks are allowed within the Base64 encoded input data.
201. * @param in A character array containing the Base64 encoded data.
202. * @param iOff Offset of the first character in <code>in</code> to be processed.
203. * @param iLen Number of characters to process in <code>in</code>, starting at <code>iOff</code>.
204. * @return An array containing the decoded data bytes.
205. * @throws IllegalArgumentException If the input is not valid Base64 encoded data.
206. */
207. public static byte[] decode (char[] in, int iOff, int iLen) {
208. if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4.");
209. while (iLen > 0 && in[iOff+iLen-1] == '=') iLen–;
210. int oLen = (iLen*3) / 4;
211. byte[] out = new byte[oLen];
212. int ip = iOff;
213. int iEnd = iOff + iLen;
214. int op = 0;
215. while (ip < iEnd) {
216. int i0 = in[ip++];
217. int i1 = in[ip++];
218. int i2 = ip < iEnd ? in[ip++] : 'A';
219. int i3 = ip < iEnd ? in[ip++] : 'A';
220. if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127)
221. throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
222. int b0 = map2[i0];
223. int b1 = map2[i1];
224. int b2 = map2[i2];
225. int b3 = map2[i3];
226. if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0)
227. throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
228. int o0 = ( b0 <<2) | (b1>>>4);
229. int o1 = ((b1 & 0xf)<<4) | (b2>>>2);
230. int o2 = ((b2 & 3)<<6) | b3;
231. out[op++] = (byte)o0;
232. if (op<oLen) out[op++] = (byte)o1;
233. if (op<oLen) out[op++] = (byte)o2; }
234. return out; }
235.
236. //Dummy constructor.
237. private Base64Coder() {}
238.
239. } // end class Base64Coder
最後,小馬還把小DEMO源碼放在附件裡面,有需要的朋友可以下載下來,共同交流學習,也懇請高人回答下小馬在文章註釋中提出的問題,謝謝,文章是小馬急急忙忙在傢寫的,在網吧發的,暈…不是我有多用功,這邊下雨,討厭下雨,下雨我就鬱悶,來網吧玩的,順帶發下文章,吼吼,該玩的時候死命的玩,該工作的時候死命的工作,年輕時瘋狂,老瞭不後悔,吼吼,加油加油!大傢工作,也註意身體健康,嘿嘿,你懂的,不解釋…哈哈
摘自 酷_莫名簡單