最近項目在搞用戶圖片上傳功能,要從本地選擇圖片或者camera拍攝的圖片顯示預覽並上傳,之前爲了方便一直想着用別人的輪子來造車,結果各種問題,要麼應用崩潰,要麼適配性極差,最後沒辦法,只能返璞歸真,使用系統提供的方式來獲取照片.然而,開發並不是一帆風順的,網上提供的解決方案都是使用bitmap獲取圖片,得到的圖片分辨率比較低,雖然網上找了很多文章,但很多都是答非所問,又或者一大堆問題,還不如自己慢慢琢磨研究.
在之前找了好多參考文章之後,都沒能解決問題,有點絕望,沒想到自己琢磨倒是搞好了.下面分享一波,也記錄一下,方便日後使用.
先來看看最終實現的效果吧:
效果比較簡單,只涉及到圖片uri獲取和展示,至於文件上傳一般都是通過文件路徑得到file,然後上傳,本文並不介紹上傳功能,大家可以自行研究,文章重點只爲解決獲取camera和本地圖片模糊問題,下面來具體說明一下實現思路.
- 調用系統相機並設置輸出路徑
private void takePhoto() { //跳轉到新的Activity去拍照片 //進來時就同時擁有兩個權限,調用系統拍照 String state = Environment.getExternalStorageState(); if (state.equals(Environment.MEDIA_MOUNTED)) { Intent photoIn = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); String out_file_path = SAVED_IMAGE_DIR_PATH; File dir = new File(out_file_path); if (!dir.exists()) { dir.mkdirs(); } capturePath = dir.getAbsolutePath() + "/" + Calendar.getInstance().getTimeInMillis() + ".jpg"; loge("要保存圖片的路徑爲===="+capturePath); File file = new File(capturePath); photoIn.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file)); photoIn.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1); startActivityForResult(photoIn, TAKE_PHOTO_REQUEST); } else { Toast.makeText(getApplicationContext(), "請確認已經插入SD卡", Toast.LENGTH_LONG).show(); } }
通過MediaStore.EXTRA_OUTPUT方法可以設置拍照圖片的輸出路徑,方便定位圖片位置和使用
- 訪問系統圖庫並返回圖片uri
訪問系統圖庫有多種方式,這裏使用Intent.GET_ACTION_CONTENT,然後篩選image類型的資源,如下所示:
private void getSystemImage() { Intent localIntent = new Intent(); localIntent.setType("image/*"); /* 使用Intent.ACTION_GET_CONTENT這個Action */ localIntent.setAction(Intent.ACTION_GET_CONTENT); /* 取得相片後返回本畫面 */ startActivityForResult(localIntent, CUT_PICTURE); }下面就要探究一下如何在onActivityResult()方法中接收圖片數據的問題了.
- 獲取返回的圖片uri
之前說過,一般情況下,都是使用bitmap來獲取到圖片並顯示,這樣獲取的好處在於得到的圖片是縮略圖,不會佔用太多資源,但是同樣也會帶來一個問題:如果我們需要得到高質量或者原圖,bitmap的方式就無法達到需求,因爲android系統內部作了相關限制,即使是最高質量的bitmap,最終也看起來不會清晰.那麼有什麼方法可以解決問題呢?答案肯定是有的. 先來看看下面的這部分onActivityResult()代碼:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { // super.onActivityResult(requestCode, resultCode, data); loge("拍照完了==" + resultCode); if (requestCode == TAKE_PHOTO_REQUEST && resultCode == RESULT_OK) { isCameraImage = true; isLocalImage = false; loge("拍照成功,拍照路徑爲"+capturePath); File file = new File(capturePath); uploadImagePath = capturePath; Uri uri = Uri.fromFile(file); showUploadImage(uri.toString()); } else if (requestCode == TAKE_PHOTO_REQUEST && resultCode != RESULT_OK) { loge("取消了拍照"); } else if(requestCode == CUT_PICTURE && resultCode == RESULT_OK){ isLocalImage = true; isCameraImage =false; localUri = data.getData(); loge("傳回來的本地圖片uri爲==="+localUri.toString()); showUploadImage(localUri.toString()); } }
從代碼中不難看出,最終我們想要得到的都是圖片的uri,有人可能會問爲什麼要得到uri而不是file,當然是因爲我們要快速展示出來,沒錯,就是使用glide來展示.剛剛takePhoto()中capturePath是作爲拍照圖片保存的輸出路徑,在onActivityResult()拍照成功返回後,獲取file,並得到uri,進而得以顯示原圖片,並沒有通過bitmap來顯示.
至於從本地圖庫選擇圖片後返回,我們可以直接得到圖片的uri. 由於本地圖片選擇並不會改變路徑,所以無法爲其向前者一樣指定輸出路徑,但是uri就是我們最終希望得到的.OK,這樣就算大功告成了,下面附上showUploadImage()顯示返回圖片的方法:
private void showUploadImage(String imageUri) { if(imageUri!=null){ add_back_view.setVisibility(View.GONE); iv_upload_image.setVisibility(View.VISIBLE); Glide .with(this) .load(imageUri) .centerCrop() .thumbnail(0.3f) .into(iv_upload_image); bt_upload.setEnabled(true); bt_upload.setAlpha(1); } }
- 如果想要得到圖片文件用於上傳至服務器那該怎麼辦?
String[] proj = { MediaStore.Images.Media.DATA }; Cursor actualimagecursor = managedQuery(localUri,proj,null,null,null); int actual_image_column_index = actualimagecursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); actualimagecursor.moveToFirst(); String img_path = actualimagecursor.getString(actual_image_column_index);沒看錯,就是這短短几行代碼就能得到image的路徑,接下來就可以放心的上傳圖片啦