實作 Android 客製化相簿選擇器

最近專案上要做一個 App 內部使用的媒體選擇器,要可以列出手機目前的照片或影片,也要可以列出手機的相簿,使用者選擇相簿後,列表顯示該相簿的媒體。

其實用 Android 內建 Intent 就可以開啟然後選擇了,為何需要客製化呢? 原因就在於我們的需求不只是這樣,假設我們是一個照片分享的App,希望使用者能分享多張照片、且照片屬於高解析度的,那麼我們如果用原生的照片選擇器,使用者只能一張一張慢慢選,且如果選到解析度低的就會提示使用者不符合上傳規則,這樣的做法是一個很糟的使用者體驗。能客製化一個媒體選擇器就是我們該來做的事情。

需求分析

好,製作之前先來釐清我們客製化的需求是什麼

  • 一開啟時預設要顯示所有相簿的媒體,使用者可以點擊相簿名稱開啟相簿列表來選擇開啟其他相簿,然後列出該相簿的媒體。
choose(MimeType.ALL / MimeType.IMAGE / MimeType.VIDEO)
mutiple(true) / maxSelect(Int)
imageMaxSize(Long)
videoMaxSecond(Int) / videoMinSecond(Int)

架構設計

我們採用 MVVM 的架構來實作,搭配 RxJava 做資料存取,這邊來做職責劃分:

  1. Model 儲存媒體和相簿的資料類別
  2. Repository 用來存取相簿、媒體,做不同的資料查詢、過濾
  3. ViewModel 處理介面邏輯和資料串接
  4. View 單純顯示介面和傳遞使用者操作給 ViewModel。

實作

首先,別忘了我們開啟相簿需要先要求權限,我們在 AndroidManifest.xml 和 Runtime 都需要檢查:

Model

我們先定義三個資料類別,分別用來儲存相簿、媒體和開啟相簿設定值:

Repository

AlbumRepo 我們定義資料存取的介面,主要方法有

fetchAlbums()
getAlbums()

AlbumRepo 實作的部分我們會講兩個小重點:

首先是, fetchAlbums() 實作大方向是我們使用 ContentProvider 來查詢我們所有相簿和每個相簿內的所有媒體,查詢之後我們用一個列表儲存起來,同時從設定中過濾掉不符合條件限制的媒體,一併在加入列表之前過濾掉不符條件的媒體。

ContentProvider 的查詢方法類似 SQL (SELECT 欄位 FROM 位置 WHERE 條件) 的語句,你要指定查詢的欄位,我們這邊就是 selections 的變數內容, selectionArgs 就是我們 selections 裡面有一些數值要帶入 (selections 裡面的問號), projections 則是查詢出來後對應到的欄位名稱,用來取出該欄位的數值用的,其它用法可以參考 官網文件

再來就是 RxJava 的資料流走向,我們多宣告了一個 BehaviorSubject ,當 fetchAlbums() 查詢完資料後,會透過這個 Subject 去發布資料變更通知給 getAlbums() ,所以訂閱者可以呼叫完 fetchAlbums() 去強制更新後,再訂閱 getAlbums() 來取得更新資料。

ViewModel

ViewModel 宣告了儲存目前選擇媒體和相簿的 Subject,讓 View 可以訂閱變更,當有任何資料變更時,介面可以有對應的變動。 當目前選擇的媒體有變更時,單選相簿就會回傳該媒體,而多選相簿的時候則會更新按鈕顯示目前已選的數目。

View

這邊就實作單純的 UI 介面、Adapter 和事件,View 都從 ViewModel 訂閱資料流來做對應的變更,可以參考下列連結程式碼,這邊就不特別說明。

結語

完整程式碼就參考下面連結( 請給星 Star 、歡迎 fork 或者開 issue): https://github.com/enginebai/GalleryEngine

Advertisements