1--安卓多媒體之圖片綜合篇
零、前言
本篇將涉及:
1.呼叫系統相機、上傳到伺服器操作
2.大照片通過取樣並壓縮尺寸避免OOM
3.media中圖片的內容提供者使用方法,增刪改查,獲取手機所有圖片路徑
4.顯示最近100張照片
5.通過系統圖庫選擇一張載入到介面
實驗一:拍一張圖片上傳到伺服器:
1.開啟系統系統相機
// 注:Cons.CAMERA_RESULT = 1 startActivityForResult(new Intent(MediaStore.ACTION_IMAGE_CAPTURE), Cons.CAMERA_RESULT);
2.接收返回的Bitmap
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { //通過intent獲取Bundle中的"data"鍵的資料(即Bitmap) Bitmap bitmap = (Bitmap) data.getExtras().get("data"); //上傳照片邏輯 doUpload(bitmap); } }
3.基於OKHttp實現上傳邏輯
檢視原始碼發現RequestBody.create不止可以接收File,還可以接收位元組陣列byte[]。思路:
網路許可權: <uses-permission android:name="android.permission.INTERNET"/>
1.通過ByteArrayOutputStream拷貝Bitmap的位元組
2.通過MultipartBody模擬表單,生成請求物件Request
3.將Request封裝為Call物件,並執行Call
/** * 模擬表單上傳Bitmap:通過MultipartBody */ private void doUpload(Bitmap bitmap) { //0.準備一個位元組陣列,準備將Bitmap放入其中 ByteArrayOutputStream baos = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos); //1.獲取OkHttpClient物件 OkHttpClient okHttpClient = new OkHttpClient(); //2.獲取Request物件 RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream"), baos.toByteArray()); RequestBody requestBody = new MultipartBody.Builder() .setType(MultipartBody.FORM) .addFormDataPart("file", "test.jpg", fileBody) .build(); Request request = new Request.Builder() .url(Cons.BASE_URL + "upload").post(requestBody).build(); //3.將Request封裝為Call物件,並執行Call Call call = okHttpClient.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Log.e(TAG, "onFailure: " + e); } @Override public void onResponse(Call call, Response response) throws IOException { String result = response.body().string(); Log.e(TAG, "onResponse: " + result); runOnUiThread(() -> ToastUtil.showAtOnce(MainActivity.this, result)); } }); }
4.SpringBoot實現服務端程式碼
客戶端完成了,多檔案上傳的服務端使用SpringBoot很輕鬆可以實現
/** * 多檔案上傳(包括一個) * * @param files * @return */ @PostMapping(value = "/upload") public @ResponseBody String uploadImg(@RequestParam("file") List<MultipartFile> files) { StringBuilder result = new StringBuilder(); for (MultipartFile file : files) { if (file.isEmpty()) { return "false"; } String fileName = file.getOriginalFilename();//獲取名字 String path = "F:/SpringBootFiles/imgs/"; File dest = new File(path + "/" + fileName); if (!dest.getParentFile().exists()) { //判斷檔案父目錄是否存在 dest.getParentFile().mkdir(); } try { file.transferTo(dest); //儲存檔案 result.append(fileName + "上傳成功!\n"); } catch (IllegalStateException | IOException e) { e.printStackTrace(); result.append(fileName + "上傳失敗!\n"); } } return result.toString(); }
- 上傳成功:不過仔細一看大小--195*260

手機拍照.png
實驗二、Bitmap的取樣,載入大圖片
記憶體問題:避免大圖片導致的OOM(載入一個9.2M的圖片,點兩下):
private void bitmapTest() { //生成Bitmap物件 Bitmap bitmap = BitmapFactory.decodeFile( Environment.getExternalStorageDirectory() + "/DCIM/Camera/IMG20181018103528.jpg"); mImageView.setImageBitmap(bitmap); }

OOM時.png

傳說中的OOM.png
1.取樣後適量縮小,解決OOM:思路
1)建立Options物件op,對Bitmap做配置
2)只獲取Bitmap資訊,不分配記憶體: op.inJustDecodeBounds = true
3)設定壓縮率,根據需求自己設定,這裡為適應螢幕
4)設定op解析出可顯示的Bitmap,使用
private void bitmapTest() { String pathName = Environment.getExternalStorageDirectory() + "/DCIM/Camera/IMG20181018103528.jpg"; //1.建立Options物件op,對Bitmap做配置 BitmapFactory.Options op = new BitmapFactory.Options(); //2.只獲取Bitmap資訊,不分配記憶體 op.inJustDecodeBounds = true; //以op配置來解析出Bitmap:fakeBitmap由於inJustDecodeBounds=true,無法顯示 Bitmap fakeBitmap = BitmapFactory.decodeFile(pathName, op); L.d(op.outWidth + "*" + op.outHeight + L.l());// 6240*8320 //3.設定壓縮率 //獲取螢幕尺寸 int winH = ScrUtil.getScreenHeight(this); int winW = ScrUtil.getScreenWidth(this); op.inSampleSize = (int) (op.outHeight > op.outWidth ? Math.ceil(op.outHeight / winH) : Math.ceil(op.outWidth / winW)); L.d(op.inSampleSize + L.l());// 4 op.inJustDecodeBounds = false; //4.設定op解析出可顯示的Bitmap,使用 Bitmap realBitmap = BitmapFactory.decodeFile(pathName, op); mImageView.setImageBitmap(realBitmap);

載入正常.png
實驗三、圖片與內容提供者:
media作為手機的三鼎之一,自然是少不了內容提供者來向外界暴漏資訊
主要儲存在 external.db(外部)
和 internal.db(內部)
兩個資料庫中
資料庫中圖片的主要欄位有:
_id:id標識_data: 圖片絕對路徑_size: 圖片大小mime_type: 型別 data_added:新增的時間data_modifide:最後修改時間_display_name:顯示名稱description:描述 width:寬height:高

media的內容提供者資料庫.png
1.獲取內容提供者並新增一條自定義資訊的圖片
private void insertImg() { //1.建立ContentValues物件,記錄插入照片資訊 ContentValues values = new ContentValues(); values.put(MediaStore.Images.Media.DISPLAY_NAME, "張風捷特烈"); values.put(MediaStore.Images.Media.DESCRIPTION, "天下無雙"); values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); //2.獲取內容提供者,插入(外部圖片儲存Uri,values),返回插入圖片的Uri Uri imgFileUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); L.d(imgFileUri + L.l());//content://media/external/images/media/1064830 //3.通過開啟圖片的意圖新增額外資訊將imgFileUri傳送給系統相機 Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, imgFileUri); startActivity(intent); }
2.通過內容提供者讀取剛才插入的圖片
private void readImg() { try { //1.獲取內容提供者,通過剛才的Uri開啟輸入流 Uri imgUri = Uri.parse("content://media/external/images/media/1064830"); InputStream is = getContentResolver().openInputStream(imgUri); //2.將圖片解碼展示 Bitmap bitmap = BitmapFactory.decodeStream(is); mImageView.setImageBitmap(bitmap); } catch (FileNotFoundException e) { e.printStackTrace(); } }
3.更新描述資訊
private void updateImg() { ContentValues values = new ContentValues(2); values.put(MediaStore.Images.Media.DISPLAY_NAME, "Toly"); values.put(MediaStore.Images.Media.DESCRIPTION, "Only Me"); //1.獲取內容提供者,通過剛才的Uri開啟輸入流 Uri imgUri = Uri.parse("content://media/external/images/media/1064830"); getContentResolver().update(imgUri, values, null, null); }
4.查表
既然是內容提供者,玩個表再所難免,藉此回顧一下內容提供者的使用
可見上面的修改方法成功
private void queryImg() { //1.查詢獲得遊標 Cursor cursor = getContentResolver().query( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, "_id=1064830", null, null, null); //2.讓遊標移動,到尾為止 while (cursor.moveToNext()) { String filePath = cursor.getString(cursor.getColumnIndex("_data")); String name = cursor.getString(cursor.getColumnIndex("_display_name")); String description = cursor.getString(cursor.getColumnIndex("description")); L.d(filePath + L.l());//storage/emulated/0/DCIM/Camera/1539834068417.jpg L.d(name + L.l());//Toly L.d(description + L.l());//Only Me }
5.刪除
private void deleteImg() { Uri imgUri = Uri.parse("content://media/external/images/media/1064830"); int delete = getContentResolver().delete(imgUri, "_id=1064830", null); L.d(delete + L.l());//1 表示刪除了1行 }
6.獲取所有圖片的路徑
一共12540張圖片,方法耗時:1.289秒,屬於耗時操作應該放在子執行緒
可以獲取資料庫中的欄位,封裝一個圖片的實體類,以便使用
private ArrayList<String> queryAllImg() { ArrayList<String> imgPaths = new ArrayList<>(); //1.查詢獲得遊標 Cursor cursor = getContentResolver().query( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, "", null, null, null); //2.讓遊標移動,到尾為止 while (cursor.moveToNext()) { String filePath = cursor.getString(cursor.getColumnIndex("_data")); imgPaths.add(filePath); } return imgPaths; }
查詢所有圖片.png

查詢所有圖片.png
實驗四、顯示最近100張圖片
為了簡便,使用Picasso來載入圖片:詳情可見-- ofollow,noindex">O2-開源框架使用之Picasso

查詢最近100張圖片.png
1.獲取最近100條資料庫記錄
排序條件: "date_added desc"
表示根據date_added欄位倒序查詢
將資料盛放在List中,並根據列表元素個數來決定跳出while迴圈
private ArrayList<String> queryPic100() { ArrayList<String> imgPaths = new ArrayList<>(); //1.查詢獲得遊標 String queryCol = MediaStore.Images.Media.DATE_ADDED; Cursor cursor = getContentResolver().query( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, "", null, "date_added desc", null); //2.讓遊標移動,到尾為止 while (cursor.moveToNext()) { if (imgPaths.size() >= 100) { break; } String filePath = cursor.getString(cursor.getColumnIndex("_data")); imgPaths.add(filePath); } return imgPaths; }
2.RecyclerView的簡單使用(佈局很簡單就免了)
1).建立介面卡類和ViewHolder
2).設定RecyclerView樣式
/** * 介面卡 */ class PicRVAdapter extends RecyclerView.Adapter<PicViewHolder> { @NonNull @Override public PicViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(Pic100Activity.this).inflate(R.layout.item_img, null); return new PicViewHolder(view); } @Override public void onBindViewHolder(@NonNull PicViewHolder holder, int position) { //使用Picasso載入檔案圖片 Picasso.get().setIndicatorsEnabled(true); Picasso.get() .load(new File(mList.get(position)))//檔案 .resize(200,200)//重設尺寸 .into(holder.mIv_icon); } @Override public int getItemCount() { return mList.size(); } } /** * ViewHolder */ public class PicViewHolder extends RecyclerView.ViewHolder { public final ImageView mIv_icon; /** * itemView為MyViewHolder中onCreateViewHolder載入的佈局 * * @param itemView 條目 */ public PicViewHolder(View itemView) { super(itemView); mIv_icon = itemView.findViewById(R.id.id_iv_item); } }
//1.設定介面卡 mIdRvPic100.setAdapter(new PicRVAdapter()); //2.!!建立佈局管理器 mGLM = new GridLayoutManager(this, 4, GridLayoutManager.VERTICAL, false); //3.!!!設定佈局管理器 mIdRvPic100.setLayoutManager(mGLM);
實驗五、選擇圖片
@OnClick(R.id.imageView) public void onViewClicked() { //開啟相簿 Intent picIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); startActivityForResult(picIntent, 0); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { //獲取返回的圖片Uri Uri picUri = data.getData(); InputStream is = null; try { //將Uri開流,形成輸入流 is = getContentResolver().openInputStream(picUri); Bitmap bitmap = BitmapFactory.decodeStream(is); mImageView.setImageBitmap(bitmap); } catch (FileNotFoundException e) { e.printStackTrace(); } finally { try { if (is != null) { is.close(); } } catch (IOException e) { e.printStackTrace(); } } } }

選擇圖片.png
後記:捷文規範
1.本文成長記錄及勘誤表
專案原始碼 | 日期 | 備註 |
---|---|---|
V0.1--無 | 2018-10-24 | 1--安卓多媒體之圖片綜合篇 |
2.更多關於我
筆名 | 微信 | 愛好 | |
---|---|---|---|
張風捷特烈 | 1981462002 | zdl1994328 | 語言 |
我的github | 我的簡書 | 我的CSDN | 個人網站 |
3.宣告
1----本文由張風捷特烈原創,轉載請註明
2----歡迎廣大程式設計愛好者共同交流
3----個人能力有限,如有不正之處歡迎大家批評指證,必定虛心改正
4----看到這裡,我在此感謝你的喜歡與支援