Android-如何開發一個功能強大的ImagePicker
圖片選擇器是Android開發中會經常用到的一個功能,特別對於社交類的應用,比如頭像設定,比如發圖片。自然ImagePicker的輪子很多,今天介紹一個功能強大的輪子SImagePicker
介紹
首先功能強大之處
- 首先基本的圖片讀取顯示,以及圖片更新監控
- 超大圖片預覽,比如一張19M,10000*5000px的圖片
- 圖片的裁剪功能
- 豐富的可配置項
廢話不說,先看效果 第一張頭像模式,第二張選擇多張圖片(包括動畫和順暢的跳轉),第三張是分片載入超大圖(19.5M,10000*5000px)
如何使用
1.首先初始化(推薦在Application的oncreate中呼叫)
SImagePicker.init(newPickerConfig
.setImageLoader(newFrescoImageLoader())
.setToolbaseColor(getColor(R.color.colorPrimary))
.build());
2.在需要選擇圖片的地方呼叫
SImagePicker
.from(MainActivity.this)
.maxCount(9)
.rowCount(3)
.pickMode(SImagePicker.MODE_IMAGE)
.fileInterceptor(newSingleFileLimitInterceptor())
.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中
@Override
protectedvoid onActivityResult(int requestCode,int resultCode,Intent data){
super.onActivityResult(requestCode, resultCode, data);
if(resultCode ==Activity.RESULT_OK && requestCode == REQUEST_CODE_IMAGE){
finalArrayList<String> pathList =
data.getStringArrayListExtra(PhotoPickerActivity.EXTRA_RESULT_SELECTION);
finalboolean original =
data.getBooleanExtra(PhotoPickerActivity.EXTRA_RESULT_ORIGINAL,false);
}
}
實現
圖片資料庫讀取CursorLoader
Android3.0中引入了載入器/裝載器(Loader)的功能,主要用於非同步的方式載入資料庫。裝載器Loader的特點:
- 裝載器提供非同步資料載入的能力
- 裝載器監視資料資源並且當內容改變時傳送新的結果;
- 在配置改變後重建的時候,裝載器自動的重連最後的裝載器遊標,因此,不需要重新查詢資料。
此專案也是使用loader去載入和監控圖片資料,對於Photo和Album即圖片和相簿分別有一個loader和一個controller,loader主要用於載入對應的資料,controller主要用於資料讀取到後的重新整理已經loader的釋放。 原始碼中的對應
PhotoLoader初始化
publicstaticCursorLoader newInstance(Context context,Album album,long minSize){
if(album ==null|| album.isAll()){
returnnewPhotoLoader(context,MediaStore.Images.Media.EXTERNAL_CONTENT_URI, PROJECTION,
SELECTION_SIZE,newString[]{minSize +""}, ORDER_BY);
}
returnnewPhotoLoader(context,MediaStore.Images.Media.EXTERNAL_CONTENT_URI, PROJECTION,
MediaStore.Images.Media.BUCKET_ID +" = ? and ("+ SELECTION_SIZE +")",newString[]{
album.getId(), minSize +""}, ORDER_BY);
}
超大圖片載入
對於超大的圖片如何展示,這個是個比較棘手的問題 比如這張圖http://7xpb9x.com1.z0.glb.clouddn.com/2017/01/20/b578e4755a32ac56a9c4b9a1f7e2822d.jpg 10000*5000的畫素,接近20M。
這種圖片肯定無法一次全部load到記憶體中,可以稍微計算一下即使是RGB_565的方式全部load進記憶體也要佔用幾乎90M的記憶體,顯然是不太可能。可以回頭看一下第三張demo gif,顯然使用者開啟一張圖時,在預設情況下,並不要求能看到細節,當用戶點選某區域放大時此時才會需要這一塊的清晰圖。那麼如何展示這種超大圖的思路基本基本就是
- 首先拿到檔案路徑,讀取出圖片的寬高,並且根據螢幕和圖片寬高計算出一個展示全圖的情況下的Samplesize,根據這個值去加載出一個全景的圖
- 對圖片進行分塊,分塊會分出不同放大倍數下(即選擇不同SampleSize)下的一個塊列表,比如放大2倍時,放大4倍時對應怎麼分塊
- 當用戶點選放大某一區域時,根據放大的倍數以及當前的中心點選擇對應的塊進行load和渲染
在SImagePicker專案中主要是用了subsamplingImageView 並且根據picker的需求做了些修改,來實現超大圖的預覽
圖片列表展示
展示
由於使用了cursorLoader,對於ListView的話有CursorAdapter可以使用,但是對於RecyclerView確沒有對應的Adapter,所以在原始碼中可以看到實現了一個RecycleCursorAdapter,用於實現從cursor獲取資料已經自動重新整理。
相容
為了能夠相容多個圖片載入器,SImagePicker抽象了一個ImageLoader介面用於讓使用者自定義對應的圖片載入器。
使用建議
SImagePicker提供了jitpack上的依賴庫,可以很快的接入業務中,但是由於大部分的APP對於ImagePicker的使用都有各種業務需求,且SImagePicker只是抽象出了比較通用的一些配置,用於讓使用者能快速整合,所以此處還是建議使用者儘可能原始碼引用的方式的使用SImagePicker,既方便做一些除錯,也可以很快的瞭解實現原理,說起來這種UI元件程式碼應該是很好讀的,因為本身並不複雜。