1. 程式人生 > >Android-如何開發一個功能強大的ImagePicker

Android-如何開發一個功能強大的ImagePicker

圖片選擇器是Android開發中會經常用到的一個功能,特別對於社交類的應用,比如頭像設定,比如發圖片。自然ImagePicker的輪子很多,今天介紹一個功能強大的輪子SImagePicker

介紹

首先功能強大之處

  • 首先基本的圖片讀取顯示,以及圖片更新監控
  • 超大圖片預覽,比如一張19M,10000*5000px的圖片
  • 圖片的裁剪功能
  • 豐富的可配置項

廢話不說,先看效果 第一張頭像模式,第二張選擇多張圖片(包括動畫和順暢的跳轉),第三張是分片載入超大圖(19.5M,10000*5000px)頭像模式,支援裁剪多選圖片,流暢的頁面跳轉超大圖預覽,可以看到漸變載入

如何使用

1.首先初始化(推薦在Application的oncreate中呼叫)

  1. SImagePicker.init(newPickerConfig
    .Builder().setAppContext(this)
  2. .setImageLoader(newFrescoImageLoader())
  3. .setToolbaseColor(getColor(R.color.colorPrimary))
  4. .build());

2.在需要選擇圖片的地方呼叫

  1. SImagePicker
  2. .from(MainActivity.this)
  3. .maxCount(9)
  4. .rowCount(3)
  5. .pickMode(SImagePicker.MODE_IMAGE)
  6. .fileInterceptor(newSingleFileLimitInterceptor())
  7. .forResult(REQUEST_CODE_IMAGE
    );

可配置項

1.全域性配置(即初始化時傳入的PickerConfig,此配置作用於SImagePicker整個使用過程)

配置引數 引數含義
setImageLoader(ImageLoader) 使用的圖片載入器。demo工程中實現了Fresco和Glide兩種ImageLoader,可以參考
setToolbarColor(int) Picker的主色調,預設值是App的primaryColor
setAppContext(Context) Picker內部用到的Context,傳入ApplicationContext即可

2.單次配置(即每次呼叫SImagePicker時傳入的引數,此引數只對這次呼叫生效)

配置引數 引數含義
from(Activity or Fragment) 呼叫圖片選擇器可從Activity或者Fragment進入,最後的結果會在onActivityResult()返回,現在返回的結果有兩個值,使用者選擇的圖片的路徑列表data.getStringArrayListExtra(PhotoPickerActivity.EXTRA_RESULT_SELECTION);使用者是否選擇了原圖data.getBooleanExtra(PhotoPickerActivity.EXTRA_RESULT_ORIGINAL, false);
maxCount(int) 此次選擇允許的最大選擇數量,預設是1.比如發朋友圈最多選擇9張圖就傳9
rowCount(int) 圖片列表單排展示多少張圖
setSelected(List) 預設已經被選中的圖片
pickMode(int) 選圖的模式,現在有頭像模式和普通模式兩種,頭像模式選中圖片後預設會跳到圖片裁剪頁面且預設只能選擇一張
cropFilePath(String) 頭像模式下裁剪圖片存放地址
showCamera(boolen) 是否要展示拍照入口
pickText(int) Picker裡右下角展示的文字資訊(比如配置選擇,傳送,完成)
fileInterceptor(FileChooseInterceptor) 圖片過濾器,比如使用者選擇的單張圖片大小有限制,即可寫在這個攔截器中,當用戶選擇過大圖片時可以提示並且過濾
forResult(int requestCode) 開啟圖片選擇器,並且傳入requestCode

獲取結果

在呼叫圖片選擇器的Fragment或者Activity中

  1. @Override
  2. protectedvoid onActivityResult(int requestCode,int resultCode,Intent data){
  3. super.onActivityResult(requestCode, resultCode, data);
  4. if(resultCode ==Activity.RESULT_OK && requestCode == REQUEST_CODE_IMAGE){
  5. finalArrayList<String> pathList =
  6. data.getStringArrayListExtra(PhotoPickerActivity.EXTRA_RESULT_SELECTION);
  7. finalboolean original =
  8. data.getBooleanExtra(PhotoPickerActivity.EXTRA_RESULT_ORIGINAL,false);
  9. }
  10. }

實現

圖片資料庫讀取CursorLoader

Android3.0中引入了載入器/裝載器(Loader)的功能,主要用於非同步的方式載入資料庫。裝載器Loader的特點:

  1. 裝載器提供非同步資料載入的能力
  2. 裝載器監視資料資源並且當內容改變時傳送新的結果;
  3. 在配置改變後重建的時候,裝載器自動的重連最後的裝載器遊標,因此,不需要重新查詢資料。

此專案也是使用loader去載入和監控圖片資料,對於Photo和Album即圖片和相簿分別有一個loader和一個controller,loader主要用於載入對應的資料,controller主要用於資料讀取到後的重新整理已經loader的釋放。 原始碼中的對應WechatIMG90.jpeg

PhotoLoader初始化

  1. publicstaticCursorLoader newInstance(Context context,Album album,long minSize){
  2. if(album ==null|| album.isAll()){
  3. returnnewPhotoLoader(context,MediaStore.Images.Media.EXTERNAL_CONTENT_URI, PROJECTION,
  4. SELECTION_SIZE,newString[]{minSize +""}, ORDER_BY);
  5. }
  6. returnnewPhotoLoader(context,MediaStore.Images.Media.EXTERNAL_CONTENT_URI, PROJECTION,
  7. MediaStore.Images.Media.BUCKET_ID +" = ? and ("+ SELECTION_SIZE +")",newString[]{
  8. album.getId(), minSize +""}, ORDER_BY);
  9. }

超大圖片載入

對於超大的圖片如何展示,這個是個比較棘手的問題 比如這張圖http://7xpb9x.com1.z0.glb.clouddn.com/2017/01/20/b578e4755a32ac56a9c4b9a1f7e2822d.jpg 10000*5000的畫素,接近20M。

這種圖片肯定無法一次全部load到記憶體中,可以稍微計算一下即使是RGB_565的方式全部load進記憶體也要佔用幾乎90M的記憶體,顯然是不太可能。可以回頭看一下第三張demo gif,顯然使用者開啟一張圖時,在預設情況下,並不要求能看到細節,當用戶點選某區域放大時此時才會需要這一塊的清晰圖。那麼如何展示這種超大圖的思路基本基本就是

  1. 首先拿到檔案路徑,讀取出圖片的寬高,並且根據螢幕和圖片寬高計算出一個展示全圖的情況下的Samplesize,根據這個值去加載出一個全景的圖
  2. 對圖片進行分塊,分塊會分出不同放大倍數下(即選擇不同SampleSize)下的一個塊列表,比如放大2倍時,放大4倍時對應怎麼分塊
  3. 當用戶點選放大某一區域時,根據放大的倍數以及當前的中心點選擇對應的塊進行load和渲染

在SImagePicker專案中主要是用了subsamplingImageView 並且根據picker的需求做了些修改,來實現超大圖的預覽

圖片列表展示

展示

由於使用了cursorLoader,對於ListView的話有CursorAdapter可以使用,但是對於RecyclerView確沒有對應的Adapter,所以在原始碼中可以看到實現了一個RecycleCursorAdapter,用於實現從cursor獲取資料已經自動重新整理。

相容

為了能夠相容多個圖片載入器,SImagePicker抽象了一個ImageLoader介面用於讓使用者自定義對應的圖片載入器。

使用建議

SImagePicker提供了jitpack上的依賴庫,可以很快的接入業務中,但是由於大部分的APP對於ImagePicker的使用都有各種業務需求,且SImagePicker只是抽象出了比較通用的一些配置,用於讓使用者能快速整合,所以此處還是建議使用者儘可能原始碼引用的方式的使用SImagePicker,既方便做一些除錯,也可以很快的瞭解實現原理,說起來這種UI元件程式碼應該是很好讀的,因為本身並不複雜。