RxImagePickerを用いた柔軟な画像選択アーキテクチャ
RxImagePickerは、Androidアプリにおいて画像の選択・撮影操作を簡素化するためのリアクティブライブラリです。RxJava2/RxJava3との統合により、非同期処理やイベントストリームの管理が容易になり、開発者はUIロジックに集中できます。本稿では、基本的な統合からカスタムUIの構築まで、実践的な実装パターンを解説します。
プロジェクト構成と依存関係の設定
まず、build.gradleファイルに必要なモジュールを追加します。AndroidX環境を前提としています。
// メインコア(必須)
implementation 'com.github.qingmei2:rximagepicker:3.0.0-beta02'
// システム標準UIサポート
implementation 'com.github.qingmei2:rximagepicker_support:3.0.0-beta02'
// 知乎風UIテーマ
implementation 'com.github.qingmei2:rximagepicker_support_zhihu:3.0.0-beta02'
// WeChat風UIテーマ
implementation 'com.github.qingmei2:rximagepicker_support_wechat:3.0.0-beta02'
基本的な画像選択フローの実装
画像選択機能は、インターフェース定義から開始します。アノテーションによって操作タイプを明示的に指定します。
interface ImageSelectionSource {
@Gallery
fun selectFromAlbum(context: Context): Observable<SelectionResult>
@Camera
fun captureWithCamera(context: Context): Observable<SelectionResult>
}
権限チェックを行い、許可後に選択処理を起動します。
private fun requestStoragePermission(action: () -> Unit) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {
action()
} else {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
REQUEST_PERMISSION_CODE
)
}
}
選択結果はRxJavaのストリームとして処理されます。
private fun launchGallerySelection() {
val picker = RxImagePicker.create(ImageSelectionSource::class.java)
picker.selectFromAlbum(this)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ result ->
// 選択されたURIをGlideで表示
Glide.with(this)
.load(result.uri)
.into(binding.imageViewPreview)
}, { error ->
Log.e("ImagePicker", "Selection failed", error)
Toast.makeText(this, "読み込み失敗", Toast.LENGTH_SHORT).show()
})
}
テーマ別UIの適用方法
知乎スタイルの導入
知乎風のデザインを採用する場合、専用のコンフィギュレーションビルダーを使用します。
private fun openZhihuStylePicker() {
val configuration = ZhihuConfigurationBuilder(MimeType.ofImage(), false)
.maxSelectable(9)
.capture(true)
.countable(true)
.spanCount(4)
.theme(R.style.Zhihu_Normal)
.build()
val pickerInterface = RxImagePicker.create(ZhihuPickerContract::class.java)
pickerInterface.openInNormalTheme(this, configuration)
.subscribe(handleSelectionResult())
}
WeChat風インターフェースの構築
WeChat風の体験を再現するには、独自のオプションを含む設定を行います。
private fun startWeChatStyleSelection() {
val config = WechatConfigrationBuilder(MimeType.ofAll(), false)
.maxSelectable(9)
.capture(true)
.spanCount(4)
.build()
RxImagePicker.create(WeChatImagePickerApi::class.java)
.openGallery(this, config)
.subscribe { result ->
val isOriginal = result.getBooleanExtra(EXTRA_ORIGINAL_SELECTION, false)
val mimeType = result.getStringExtra(EXTRA_MIME_TYPE)
processSelectedImage(result.uri, isOriginal, mimeType)
}
}
高度なカスタマイズ手法
カスタムUIコンポーネントの作成
既存テーマが要件を満たさない場合は、独自のビューを実装可能です。以下のインタフェースを実装します。
class CustomMediaPicker : ICustomPickerView {
override fun display(activity: FragmentActivity, containerId: Int, config: ICustomPickerConfiguration?) {
val fragment = MediaGridFragment.newInstance(config as? Bundle)
activity.supportFragmentManager
.beginTransaction()
.replace(containerId, fragment)
.commit()
}
override fun pick(): Observable<SelectionResult> {
return Observable.create { emitter ->
onImageSelected = { uri ->
if (!emitter.isDisposed) {
emitter.onNext(SelectionResult(uri))
emitter.onComplete()
}
}
}
}
}
拡張可能な設定クラスの設計
カスタムビューパイプラインに対応するための構成情報クラス。
data class PickerOptions(
val selectionLimit: Int,
val showCaptureButton: Boolean,
val gridColumns: Int,
val primaryColor: Int
) : ICustomPickerConfiguration, Parcelable {
// Parcelable実装省略
}
注釈駆動のAPI設計
RxImagePickerはアノテーションベースのディスパッチシステムを採用しており、以下のようなメタデータを定義できます。
| 属性 | 型 | 説明 |
|---|---|---|
| componentClazz | KClass<*> | 使用するアクティビティまたはフラグメントクラス |
| openAsFragment | Boolean | >フラグメントとして表示するかどうか|
| containerViewId | Int | フラグメント配置先のレイアウトID |