Androidにおける画像選択機能の設計と実装:RxImagePickerによるリアクティブアプローチ

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

タグ: rxjava Android ImagePicker Reactive Programming UI Customization

6月7日 20:48 投稿