Android圖片上傳(頭像裁切+原圖原樣)

奮鬥年輕人發表於2016-11-09

先看一下效果圖:

(一)頭像裁切、上傳伺服器(效果圖)

一般都是有圓形顯示頭像的,這裡我自定義了一個ImageView,頁面很乾淨但是看著很上檔次吧!

點選頭像從底部彈出一個對話方塊,提示使用者頭像來自相機或者相簿,這都是常規流程。

上傳完成後預設的“程式設計師頭像”換成了萌妹子




(二)普通圖片上傳伺服器(效果圖)

模仿QQ空間發動態的佈局隨意捏造一個介面出來

點選新增圖片從底部彈出一個對話方塊,提示使用者圖片來自相機或者相簿,這也都是常規流程。

上傳過程中,有可能圖片很大,顯示一個進度圈(其實頭像上傳也有,只是檔案小,還沒顯示就上傳完成了)

上傳完成後把剛才的照片亮出來顯示到按鈕的地方,當然大家根據需要還可以自己擴充套件(比如長按抖動出現刪除、繼續新增N張等等)。




下面簡單鋪一下程式碼:

(一)頭像裁切、上傳伺服器(程式碼)

這裡上邊的按鈕是頭像的點選事件,彈出底部的頭像選擇框,下邊的按鈕跳到下個頁面,進行原圖上傳。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public void onClick(View v) {
    switch (v.getId()) {
    case R.id.avatarImg:// 更換頭像點選事件
        menuWindow = new SelectPicPopupWindow(mContext, itemsOnClick); 
        menuWindow.showAtLocation(findViewById(R.id.mainLayout),
                Gravity.BOTTOM|Gravity.CENTER_HORIZONTAL, 0, 0);
        break;
    case R.id.loginBtn://登入按鈕跳轉事件
        startActivity(new Intent(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
//為彈出視窗實現監聽類 
private OnClickListener itemsOnClick = new OnClickListener() {
    @Override
    public void onClick(View v) {
        menuWindow.dismiss();
        switch (v.getId()) {
        // 拍照
        case R.id.takePhotoBtn:
            Intent takeIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            //下面這句指定呼叫相機拍照後的照片儲存的路徑
            takeIntent.putExtra(MediaStore.EXTRA_OUTPUT,
                    Uri.fromFile(new File(Environment.getExternalStorageDirectory(), IMAGE_FILE_NAME)));
            startActivityForResult(takeIntent, REQUESTCODE_TAKE);
            break;
        // 相簿選擇圖片
        case R.id.pickPhotoBtn:
            Intent pickIntent = new Intent(Intent.ACTION_PICK, null);
            // 如果朋友們要限制上傳到伺服器的圖片型別時可以直接寫如: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
public void onActivityResult(int requestCode, int resultCode, Intent data) {
     
    switch (requestCode) {
    case REQUESTCODE_PICK:// 直接從相簿獲取
        try {
            startPhotoZoom(data.getData());
        } catch (NullPointerException e) {
            e.printStackTrace();// 使用者點選取消操作
        }
        break;
    case REQUESTCODE_TAKE:// 呼叫相機拍照
        File temp = new File(Environment.getExternalStorageDirectory() + / + IMAGE_FILE_NAME);
        startPhotoZoom(Uri.fromFile(temp));
        break;
    case REQUESTCODE_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
/**
 * 裁剪圖片方法實現
 * @param uri
 */
public void startPhotoZoom(Uri uri) {
    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, 300);
    intent.putExtra(outputY, 300);
    intent.putExtra(return-data, true);
    startActivityForResult(intent, REQUESTCODE_CUTTING);
}
     
/**
 * 儲存裁剪之後的圖片資料
 * @param picdata
 */
private void setPicToView(Intent picdata) {
    Bundle extras = picdata.getExtras();
    if (extras != null) {
        // 取得SDCard圖片路徑做顯示
        Bitmap photo = extras.getParcelable(data);
        Drawable drawable = new BitmapDrawable(null, photo);
        urlpath = FileUtil.saveFile(mContext, temphead.jpg, photo);
        avatarImg.setImageDrawable(drawable);
 
        // 新執行緒後臺上傳服務端
        pd = ProgressDialog.show(mContext, null, 正在上傳圖片,請稍候...);
        new Thread(uploadImageRunnable).start();
    }
}
 
/**
 * 使用HttpUrlConnection模擬post表單進行檔案
 * 上傳平時很少使用,比較麻煩
 * 原理是: 分析檔案上傳的資料格式,然後根據格式構造相應的傳送給伺服器的字串。
 */
Runnable uploadImageRunnable = new Runnable() {
    @Override
    public void run() {
         
        if(TextUtils.isEmpty(imgUrl)){
            Toast.makeText(mContext, 還沒有設定上傳伺服器的路徑!, Toast.LENGTH_SHORT).show();
            return;
        }
         
        Map<string, string=""> textParams = new HashMap<string, string="">();
        Map<string, file=""> fileparams = new HashMap<string, file="">();
         
        try {
            // 建立一個URL物件
            URL url = new URL(imgUrl);
            textParams = new HashMap<string, string="">();
            fileparams = new HashMap<string, file="">();
            // 要上傳的圖片檔案
            File file = new File(urlpath);
            fileparams.put(image, file);
            // 利用HttpURLConnection物件從網路中獲取網頁資料
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            // 設定連線超時(記得設定連線超時,如果網路不好,Android系統在超過預設時間會收回資源中斷操作)
            conn.setConnectTimeout(5000);
            // 設定允許輸出(傳送POST請求必須設定允許輸出)
            conn.setDoOutput(true);
            // 設定使用POST的方式傳送
            conn.setRequestMethod(POST);
            // 設定不使用快取(容易出現問題)
            conn.setUseCaches(false);
            conn.setRequestProperty(Charset, UTF-8);//設定編碼  
            // 在開始用HttpURLConnection物件的setRequestProperty()設定,就是生成HTML檔案頭
            conn.setRequestProperty(ser-Agent, Fiddler);
            // 設定contentType
            conn.setRequestProperty(Content-Type, multipart/form-data; boundary= + NetUtil.BOUNDARY);
            OutputStream os = conn.getOutputStream();
            DataOutputStream ds = new DataOutputStream(os);
            NetUtil.writeStringParams(textParams, ds);
            NetUtil.writeFileParams(fileparams, ds);
            NetUtil.paramsEnd(ds);
            // 對檔案流操作完,要記得及時關閉
            os.close();
            // 伺服器返回的響應嗎
            int code = conn.getResponseCode(); // 從Internet獲取網頁,傳送請求,將網頁以流的形式讀回來
            // 對響應碼進行判斷
            if (code == 200) {// 返回的響應碼200,是成功
                // 得到網路返回的輸入流
                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);// 執行耗時的方法之後傳送消給handler
    }
};
 
Handler handler = new Handler(new Handler.Callback() {
     
    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
        case 0:
            pd.dismiss();
             
            try {
                // 返回資料示例,根據需求和後臺資料靈活處理
                // {status:1,statusMessage:上傳成功,imageUrl:http://120.24.219.49/726287_temphead.jpg}
                JSONObject jsonObject = new JSONObject(resultStr);
                 
                // 服務端以字串“1”作為操作成功標記
                if (jsonObject.optString(status).equals(1)) {
                    BitmapFactory.Options option = new BitmapFactory.Options();
                    // 壓縮圖片:表示縮圖大小為原始圖片大小的幾分之一,1為原圖,3為三分之一
                    option.inSampleSize = 1;
                     
                    // 服務端返回的JsonObject物件中提取到圖片的網路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;
        }
        return false;
    }
});</string,></string,></string,></string,></string,></string,>

 

(二)普通圖片上傳伺服器(程式碼)

直接從這裡開始,和頭像那裡基本沒什麼區別,我把拍照什麼的單獨抽出了方法,思路更清晰
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//為彈出視窗實現監聽類 
private OnClickListener itemsOnClick = new OnClickListener() {
    @Override
    public void onClick(View v) {
        // 隱藏彈出視窗
        menuWindow.dismiss();
         
        switch (v.getId()) {
        case R.id.takePhotoBtn:// 拍照
            takePhoto();
            break;
        case R.id.pickPhotoBtn:// 相簿選擇圖片
            pickPhoto();
            break;
        case R.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
/**
 * 拍照獲取圖片
 */
private void takePhoto() {
    // 執行拍照前,應該先判斷SD卡是否存在
    String SDState = Environment.getExternalStorageState();
    if (SDState.equals(Environment.MEDIA_MOUNTED)) {
 
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        /***
         * 需要說明一下,以下操作使用照相機拍照,拍照後的圖片會存放在相簿中的
         * 這裡使用的這種方式有一個好處就是獲取的圖片是拍照後的原圖
         * 如果不使用ContentValues存放照片路徑的話,拍照後獲取的圖片為縮圖不清晰
         */
        ContentValues values = new ContentValues();
        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, 記憶體卡不存在, Toast.LENGTH_LONG).show();
    }
}
 
/***
 * 從相簿中取圖片
 */
private void pickPhoto() {
    Intent intent = new Intent();
    // 如果要限制上傳到伺服器的圖片型別時可以直接寫如:image/jpeg 、 image/png等的型別
    intent.setType(image/*);
    intent.setAction(Intent.ACTION_GET_CONTENT);
    startActivityForResult(intent, SELECT_PIC_BY_PICK_PHOTO);
}
處理一下圖片選取的頁面回撥
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // 點選取消按鈕
        if(resultCode == RESULT_CANCELED){
            return;
        }
         
        // 可以使用同一個方法,這裡分開寫為了防止以後擴充套件不同的需求
        switch (requestCode) {
        case SELECT_PIC_BY_PICK_PHOTO:// 如果是直接從相簿獲取
            doPhoto(requestCode, data);
            break;
        case SELECT_PIC_BY_TACK_PHOTO:// 如果是呼叫相機拍照時
            doPhoto(requestCode, 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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/**
 * 選擇圖片後,獲取圖片的路徑
 *
 * @param requestCode
 * @param data
 */
private void doPhoto(int requestCode, 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) {
        int columnIndex = cursor.getColumnIndexOrThrow(pojo[0]);
        cursor.moveToFirst();
        picPath = cursor.getString(columnIndex);
         
        // 4.0以上的版本會自動關閉 (4.0--14;; 4.0.3--15)
        if (Integer.parseInt(Build.VERSION.SDK) < 14) {
            cursor.close();
        }
    }
     
    // 如果圖片符合要求將其上傳到伺服器
    if (picPath != null && (    picPath.endsWith(.png) ||
                                picPath.endsWith(.PNG) ||
                                picPath.endsWith(.jpg) ||
                                picPath.endsWith(.JPG))) {
 
         
        BitmapFactory.Options option = new BitmapFactory.Options();
        // 壓縮圖片:表示縮圖大小為原始圖片大小的幾分之一,1為原圖
        option.inSampleSize = 1;
        // 根據圖片的SDCard路徑讀出Bitmap
        Bitmap bm = BitmapFactory.decodeFile(picPath, option);
        // 顯示在圖片控制元件上
        picImg.setImageBitmap(bm);
         
        pd = ProgressDialog.show(mContext, null, 正在上傳圖片,請稍候...);
        new Thread(uploadImageRunnable).start();
    } else {
        Toast.makeText(this, 選擇圖片檔案不正確, Toast.LENGTH_LONG).show();
    }
 
}
 
/**
 * 使用HttpUrlConnection模擬post表單進行檔案
 * 上傳平時很少使用,比較麻煩
 * 原理是: 分析檔案上傳的資料格式,然後根據格式構造相應的傳送給伺服器的字串。
 */
Runnable uploadImageRunnable = new Runnable() {
    @Override
    public void run() {
         
        if(TextUtils.isEmpty(imgUrl)){
            Toast.makeText(mContext, 還沒有設定上傳伺服器的路徑!, Toast.LENGTH_SHORT).show();
            return;
        }
         
        Map<string, string=""> textParams = new HashMap<string, string="">();
        Map<string, file=""> fileparams = new HashMap<string, file="">();
         
        try {
            // 建立一個URL物件
            URL url = new URL(imgUrl);
            textParams = new HashMap<string, string="">();
            fileparams = new HashMap<string, file="">();
            // 要上傳的圖片檔案
            File file = new File(picPath);
            fileparams.put(image, file);
            // 利用HttpURLConnection物件從網路中獲取網頁資料
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            // 設定連線超時(記得設定連線超時,如果網路不好,Android系統在超過預設時間會收回資源中斷操作)
            conn.setConnectTimeout(5000);
            // 設定允許輸出(傳送POST請求必須設定允許輸出)
            conn.setDoOutput(true);
            // 設定使用POST的方式傳送
            conn.setRequestMethod(POST);
            // 設定不使用快取(容易出現問題)
            conn.setUseCaches(false);
            // 在開始用HttpURLConnection物件的setRequestProperty()設定,就是生成HTML檔案頭
            conn.setRequestProperty(ser-Agent, Fiddler);
            // 設定contentType
            conn.setRequestProperty(Content-Type, multipart/form-data; boundary= + NetUtil.BOUNDARY);
            OutputStream os = conn.getOutputStream();
            DataOutputStream ds = new DataOutputStream(os);
            NetUtil.writeStringParams(textParams, ds);
            NetUtil.writeFileParams(fileparams, ds);
            NetUtil.paramsEnd(ds);
            // 對檔案流操作完,要記得及時關閉
            os.close();
            // 伺服器返回的響應嗎
            int code = conn.getResponseCode(); // 從Internet獲取網頁,傳送請求,將網頁以流的形式讀回來
            // 對響應碼進行判斷
            if (code == 200) {// 返回的響應碼200,是成功
                // 得到網路返回的輸入流
                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);// 執行耗時的方法之後傳送消給handler
    }
};
 
Handler handler = new Handler(new Handler.Callback() {
 
    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
        case 0:
            pd.dismiss();
             
            try {
                JSONObject jsonObject = new JSONObject(resultStr);
                // 服務端以字串“1”作為操作成功標記
                if (jsonObject.optString(status).equals(1)) {
 
                    // 用於拼接釋出說說時用到的圖片路徑
                    // 服務端返回的JsonObject物件中提取到圖片的網路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;
        }
        return false;
    }
});</string,></string,></string,></string,></string,></string,>

相關文章