先看一下效果圖:
(一)頭像裁切、上傳服務(wù)器(效果圖)
一般都是有圓形顯示頭像的,這里我自定義了一個ImageView,頁面很干凈但是看著很上檔次吧!
點擊頭像從底部彈出一個對話框,提示用戶頭像來自相機或者相冊,這都是常規(guī)流程。
上傳完成后默認(rèn)的“程序員頭像”換成了萌妹子



(二)普通圖片上傳服務(wù)器(效果圖)
模仿QQ空間發(fā)動態(tài)的布局隨意捏造一個界面出來
點擊添加圖片從底部彈出一個對話框,提示用戶圖片來自相機或者相冊,這也都是常規(guī)流程。
上傳過程中,有可能圖片很大,顯示一個進度圈(其實頭像上傳也有,只是文件小,還沒顯示就上傳完成了)
上傳完成后把剛才的照片亮出來顯示到按鈕的地方,當(dāng)然大家根據(jù)需要還可以自己擴展(比如長按抖動出現(xiàn)刪除、繼續(xù)添加N張等等)。




下面簡單鋪一下代碼:
(一)頭像裁切、上傳服務(wù)器(代碼)
這里上邊的按鈕是頭像的點擊事件,彈出底部的頭像選擇框,下邊的按鈕跳到下個頁面,進行原圖上傳。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16@Override
publicvoidonClick(View v) {
switch(v.getId()) {
caseR.id.avatarImg:// 更換頭像點擊事件
menuWindow =newSelectPicPopupWindow(mContext, itemsOnClick);
menuWindow.showAtLocation(findViewById(R.id.mainLayout),
Gravity.BOTTOM|Gravity.CENTER_HORIZONTAL,0,0);
break;
caseR.id.loginBtn://登錄按鈕跳轉(zhuǎn)事件
startActivity(newIntent(mContext, UploadActivity.class));
break;
default:
break;
}
}
彈出窗綁定一個按鈕事件
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//為彈出窗口實現(xiàn)監(jiān)聽類
privateOnClickListener itemsOnClick =newOnClickListener() {
@Override
publicvoidonClick(View v) {
menuWindow.dismiss();
switch(v.getId()) {
// 拍照
caseR.id.takePhotoBtn:
Intent takeIntent =newIntent(MediaStore.ACTION_IMAGE_CAPTURE);
//下面這句指定調(diào)用相機拍照后的照片存儲的路徑
takeIntent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.fromFile(newFile(Environment.getExternalStorageDirectory(), IMAGE_FILE_NAME)));
startActivityForResult(takeIntent, REQUESTCODE_TAKE);
break;
// 相冊選擇圖片
caseR.id.pickPhotoBtn:
Intent pickIntent =newIntent(Intent.ACTION_PICK,null);
// 如果朋友們要限制上傳到服務(wù)器的圖片類型時可以直接寫如:image/jpeg 、 image/png等的類型
pickIntent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, image/*);
startActivityForResult(pickIntent, REQUESTCODE_PICK);
break;
default:
break;
}
}
};
為圖像選取返回的接收處理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23@Override
publicvoidonActivityResult(intrequestCode,intresultCode, Intent data) {
switch(requestCode) {
caseREQUESTCODE_PICK:// 直接從相冊獲取
try{
startPhotoZoom(data.getData());
}catch(NullPointerException e) {
e.printStackTrace();// 用戶點擊取消操作
}
break;
caseREQUESTCODE_TAKE:// 調(diào)用相機拍照
File temp =newFile(Environment.getExternalStorageDirectory() + / + IMAGE_FILE_NAME);
startPhotoZoom(Uri.fromFile(temp));
break;
caseREQUESTCODE_CUTTING:// 取得裁剪后的圖片
if(data !=null) {
setPicToView(data);
}
break;
}
super.onActivityResult(requestCode, resultCode, data);
}
把圖片顯示出來,然后上傳
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/**
* 裁剪圖片方法實現(xiàn)
* @param uri
*/
publicvoidstartPhotoZoom(Uri uri) {
Intent intent =newIntent(com.android.camera.action.CROP);
intent.setDataAndType(uri, image/*);
// crop=true是設(shè)置在開啟的Intent中設(shè)置顯示的VIEW可裁剪
intent.putExtra(crop, true);
// aspectX aspectY 是寬高的比例
intent.putExtra(aspectX, 1);
intent.putExtra(aspectY, 1);
// outputX outputY 是裁剪圖片寬高
intent.putExtra(outputX, 300);
intent.putExtra(outputY, 300);
intent.putExtra(return-data, true);
startActivityForResult(intent, REQUESTCODE_CUTTING);
}
/**
* 保存裁剪之后的圖片數(shù)據(jù)
* @param picdata
*/
privatevoidsetPicToView(Intent picdata) {
Bundle extras = picdata.getExtras();
if(extras !=null) {
// 取得SDCard圖片路徑做顯示
Bitmap photo = extras.getParcelable(data);
Drawable drawable =newBitmapDrawable(null, photo);
urlpath = FileUtil.saveFile(mContext, temphead.jpg, photo);
avatarImg.setImageDrawable(drawable);
// 新線程后臺上傳服務(wù)端
pd = ProgressDialog.show(mContext,null, 正在上傳圖片,請稍候...);
newThread(uploadImageRunnable).start();
}
}
/**
* 使用HttpUrlConnection模擬post表單進行文件
* 上傳平時很少使用,比較麻煩
* 原理是: 分析文件上傳的數(shù)據(jù)格式,然后根據(jù)格式構(gòu)造相應(yīng)的發(fā)送給服務(wù)器的字符串。
*/
Runnable uploadImageRunnable =newRunnable() {
@Override
publicvoidrun() {
if(TextUtils.isEmpty(imgUrl)){
Toast.makeText(mContext, 還沒有設(shè)置上傳服務(wù)器的路徑!, Toast.LENGTH_SHORT).show();
return;
}
Map textParams =newHashMap();
Map fileparams =newHashMap();
try{
// 創(chuàng)建一個URL對象
URL url =newURL(imgUrl);
textParams =newHashMap();
fileparams =newHashMap();
// 要上傳的圖片文件
File file =newFile(urlpath);
fileparams.put(image, file);
// 利用HttpURLConnection對象從網(wǎng)絡(luò)中獲取網(wǎng)頁數(shù)據(jù)
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// 設(shè)置連接超時(記得設(shè)置連接超時,如果網(wǎng)絡(luò)不好,Android系統(tǒng)在超過默認(rèn)時間會收回資源中斷操作)
conn.setConnectTimeout(5000);
// 設(shè)置允許輸出(發(fā)送POST請求必須設(shè)置允許輸出)
conn.setDoOutput(true);
// 設(shè)置使用POST的方式發(fā)送
conn.setRequestMethod(POST);
// 設(shè)置不使用緩存(容易出現(xiàn)問題)
conn.setUseCaches(false);
conn.setRequestProperty(Charset, UTF-8);//設(shè)置編碼
// 在開始用HttpURLConnection對象的setRequestProperty()設(shè)置,就是生成HTML文件頭
conn.setRequestProperty(ser-Agent, Fiddler);
// 設(shè)置contentType
conn.setRequestProperty(Content-Type, multipart/form-data; boundary= + NetUtil.BOUNDARY);
OutputStream os = conn.getOutputStream();
DataOutputStream ds =newDataOutputStream(os);
NetUtil.writeStringParams(textParams, ds);
NetUtil.writeFileParams(fileparams, ds);
NetUtil.paramsEnd(ds);
// 對文件流操作完,要記得及時關(guān)閉
os.close();
// 服務(wù)器返回的響應(yīng)嗎
intcode = conn.getResponseCode();// 從Internet獲取網(wǎng)頁,發(fā)送請求,將網(wǎng)頁以流的形式讀回來
// 對響應(yīng)碼進行判斷
if(code ==200) {// 返回的響應(yīng)碼200,是成功
// 得到網(wǎng)絡(luò)返回的輸入流
InputStream is = conn.getInputStream();
resultStr = NetUtil.readString(is);
}else{
Toast.makeText(mContext, 請求URL失?。? Toast.LENGTH_SHORT).show();
}
}catch(Exception e) {
e.printStackTrace();
}
handler.sendEmptyMessage(0);// 執(zhí)行耗時的方法之后發(fā)送消給handler
}
};
Handler handler =newHandler(newHandler.Callback() {
@Override
publicbooleanhandleMessage(Message msg) {
switch(msg.what) {
case0:
pd.dismiss();
try{
// 返回數(shù)據(jù)示例,根據(jù)需求和后臺數(shù)據(jù)靈活處理
// {status:1,statusMessage:上傳成功,imageUrl:http://120.24.219.49/726287_temphead.jpg}
JSONObject jsonObject =newJSONObject(resultStr);
// 服務(wù)端以字符串“1”作為操作成功標(biāo)記
if(jsonObject.optString(status).equals(1)) {
BitmapFactory.Options option =newBitmapFactory.Options();
// 壓縮圖片:表示縮略圖大小為原始圖片大小的幾分之一,1為原圖,3為三分之一
option.inSampleSize =1;
// 服務(wù)端返回的JsonObject對象中提取到圖片的網(wǎng)絡(luò)URL路徑
String imageUrl = jsonObject.optString(imageUrl);
Toast.makeText(mContext, imageUrl, Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(mContext, jsonObject.optString(statusMessage), Toast.LENGTH_SHORT).show();
}
}catch(JSONException e) {
e.printStackTrace();
}
break;
default:
break;
}
returnfalse;
}
});
(二)普通圖片上傳服務(wù)器(代碼)
直接從這里開始,和頭像那里基本沒什么區(qū)別,我把拍照什么的單獨抽出了方法,思路更清晰
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22//為彈出窗口實現(xiàn)監(jiān)聽類
privateOnClickListener itemsOnClick =newOnClickListener() {
@Override
publicvoidonClick(View v) {
// 隱藏彈出窗口
menuWindow.dismiss();
switch(v.getId()) {
caseR.id.takePhotoBtn:// 拍照
takePhoto();
break;
caseR.id.pickPhotoBtn:// 相冊選擇圖片
pickPhoto();
break;
caseR.id.cancelBtn:// 取消
break;
default:
break;
}
}
};
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/**
* 拍照獲取圖片
*/
privatevoidtakePhoto() {
// 執(zhí)行拍照前,應(yīng)該先判斷SD卡是否存在
String SDState = Environment.getExternalStorageState();
if(SDState.equals(Environment.MEDIA_MOUNTED)) {
Intent intent =newIntent(MediaStore.ACTION_IMAGE_CAPTURE);
/***
* 需要說明一下,以下操作使用照相機拍照,拍照后的圖片會存放在相冊中的
* 這里使用的這種方式有一個好處就是獲取的圖片是拍照后的原圖
* 如果不使用ContentValues存放照片路徑的話,拍照后獲取的圖片為縮略圖不清晰
*/
ContentValues values =newContentValues();
photoUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, photoUri);
startActivityForResult(intent, SELECT_PIC_BY_TACK_PHOTO);
}else{
Toast.makeText(this, 內(nèi)存卡不存在, Toast.LENGTH_LONG).show();
}
}
/***
* 從相冊中取圖片
*/
privatevoidpickPhoto() {
Intent intent =newIntent();
// 如果要限制上傳到服務(wù)器的圖片類型時可以直接寫如:image/jpeg 、 image/png等的類型
intent.setType(image/*);
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(intent, SELECT_PIC_BY_PICK_PHOTO);
}
處理一下圖片選取的頁面回調(diào)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18@Override
protectedvoidonActivityResult(intrequestCode,intresultCode, Intent data) {
// 點擊取消按鈕
if(resultCode == RESULT_CANCELED){
return;
}
// 可以使用同一個方法,這里分開寫為了防止以后擴展不同的需求
switch(requestCode) {
caseSELECT_PIC_BY_PICK_PHOTO:// 如果是直接從相冊獲取
doPhoto(requestCode, data);
break;
caseSELECT_PIC_BY_TACK_PHOTO:// 如果是調(diào)用相機拍照時
doPhoto(requestCode, data);
break;
}
super.onActivityResult(requestCode, resultCode, data);
}
接下來就是顯示圖片和上傳服務(wù)器了,上傳和頭像是同一個流程,只是不進行裁切
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/**
* 選擇圖片后,獲取圖片的路徑
*
* @param requestCode
* @param data
*/
privatevoiddoPhoto(intrequestCode, Intent data) {
// 從相冊取圖片,有些手機有異常情況,請注意
if(requestCode == SELECT_PIC_BY_PICK_PHOTO) {
if(data ==null) {
Toast.makeText(this, 選擇圖片文件出錯, Toast.LENGTH_LONG).show();
return;
}
photoUri = data.getData();
if(photoUri ==null) {
Toast.makeText(this, 選擇圖片文件出錯, Toast.LENGTH_LONG).show();
return;
}
}
String[] pojo = { MediaColumns.DATA };
// The method managedQuery() from the type Activity is deprecated
//Cursor cursor = managedQuery(photoUri, pojo, null, null, null);
Cursor cursor = mContext.getContentResolver().query(photoUri, pojo,null,null,null);
if(cursor !=null) {
intcolumnIndex = cursor.getColumnIndexOrThrow(pojo[0]);
cursor.moveToFirst();
picPath = cursor.getString(columnIndex);
// 4.0以上的版本會自動關(guān)閉 (4.0--14;; 4.0.3--15)
if(Integer.parseInt(Build.VERSION.SDK) <14) {
cursor.close();
}
}
// 如果圖片符合要求將其上傳到服務(wù)器
if(picPath !=null&& (??? picPath.endsWith(.png) ||
picPath.endsWith(.PNG) ||
picPath.endsWith(.jpg) ||
picPath.endsWith(.JPG))) {
BitmapFactory.Options option =newBitmapFactory.Options();
// 壓縮圖片:表示縮略圖大小為原始圖片大小的幾分之一,1為原圖
option.inSampleSize =1;
// 根據(jù)圖片的SDCard路徑讀出Bitmap
Bitmap bm = BitmapFactory.decodeFile(picPath, option);
// 顯示在圖片控件上
picImg.setImageBitmap(bm);
pd = ProgressDialog.show(mContext,null, 正在上傳圖片,請稍候...);
newThread(uploadImageRunnable).start();
}else{
Toast.makeText(this, 選擇圖片文件不正確, Toast.LENGTH_LONG).show();
}
}
/**
* 使用HttpUrlConnection模擬post表單進行文件
* 上傳平時很少使用,比較麻煩
* 原理是: 分析文件上傳的數(shù)據(jù)格式,然后根據(jù)格式構(gòu)造相應(yīng)的發(fā)送給服務(wù)器的字符串。
*/
Runnable uploadImageRunnable =newRunnable() {
@Override
publicvoidrun() {
if(TextUtils.isEmpty(imgUrl)){
Toast.makeText(mContext, 還沒有設(shè)置上傳服務(wù)器的路徑!, Toast.LENGTH_SHORT).show();
return;
}
Map textParams =newHashMap();
Map fileparams =newHashMap();
try{
// 創(chuàng)建一個URL對象
URL url =newURL(imgUrl);
textParams =newHashMap();
fileparams =newHashMap();
// 要上傳的圖片文件
File file =newFile(picPath);
fileparams.put(image, file);
// 利用HttpURLConnection對象從網(wǎng)絡(luò)中獲取網(wǎng)頁數(shù)據(jù)
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// 設(shè)置連接超時(記得設(shè)置連接超時,如果網(wǎng)絡(luò)不好,Android系統(tǒng)在超過默認(rèn)時間會收回資源中斷操作)
conn.setConnectTimeout(5000);
// 設(shè)置允許輸出(發(fā)送POST請求必須設(shè)置允許輸出)
conn.setDoOutput(true);
// 設(shè)置使用POST的方式發(fā)送
conn.setRequestMethod(POST);
// 設(shè)置不使用緩存(容易出現(xiàn)問題)
conn.setUseCaches(false);
// 在開始用HttpURLConnection對象的setRequestProperty()設(shè)置,就是生成HTML文件頭
conn.setRequestProperty(ser-Agent, Fiddler);
// 設(shè)置contentType
conn.setRequestProperty(Content-Type, multipart/form-data; boundary= + NetUtil.BOUNDARY);
OutputStream os = conn.getOutputStream();
DataOutputStream ds =newDataOutputStream(os);
NetUtil.writeStringParams(textParams, ds);
NetUtil.writeFileParams(fileparams, ds);
NetUtil.paramsEnd(ds);
// 對文件流操作完,要記得及時關(guān)閉
os.close();
// 服務(wù)器返回的響應(yīng)嗎
intcode = conn.getResponseCode();// 從Internet獲取網(wǎng)頁,發(fā)送請求,將網(wǎng)頁以流的形式讀回來
// 對響應(yīng)碼進行判斷
if(code ==200) {// 返回的響應(yīng)碼200,是成功
// 得到網(wǎng)絡(luò)返回的輸入流
InputStream is = conn.getInputStream();
resultStr = NetUtil.readString(is);
}else{
Toast.makeText(mContext, 請求URL失??!, Toast.LENGTH_SHORT).show();
}
}catch(Exception e) {
e.printStackTrace();
}
handler.sendEmptyMessage(0);// 執(zhí)行耗時的方法之后發(fā)送消給handler
}
};
Handler handler =newHandler(newHandler.Callback() {
@Override
publicbooleanhandleMessage(Message msg) {
switch(msg.what) {
case0:
pd.dismiss();
try{
JSONObject jsonObject =newJSONObject(resultStr);
// 服務(wù)端以字符串“1”作為操作成功標(biāo)記
if(jsonObject.optString(status).equals(1)) {
// 用于拼接發(fā)布說說時用到的圖片路徑
// 服務(wù)端返回的JsonObject對象中提取到圖片的網(wǎng)絡(luò)URL路徑
String imageUrl = jsonObject.optString(imageUrl);
// 獲取緩存中的圖片路徑
Toast.makeText(mContext, imageUrl, Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(mContext, jsonObject.optString(statusMessage), Toast.LENGTH_SHORT).show();
}
}catch(JSONException e) {
e.printStackTrace();
}
break;
default:
break;
}
returnfalse;
}
});
364595326