RxAndroidでSwipeRefreshLayoutを使う


  • ListViewを使うときによくあるアクションとして下に引っ張って更新するPull to Refreshというような動きがありますが、AndroidではSupport LibraryからSwipeRefreshLayoutというものが提供されています。今回はこれをRxAndroidと一緒に使ってみました。
  • 今、Kotlinでアプリを書いているので、サンプルのコードもKotlinになっていますがJavaで適時置き換えてください。
  • 事前の準備は KotlinでRxAndroid1.0を使う を参照。

RxSwipeRefreshLayout

  • RxAndroidからはRxSwipeRefreshLayoutというBinding用のクラスが提供されています。
// refreshingはRefreshLayoutのプログレスを切り替えることができるAction1を返す
val refreshLayoutRefreshing = RxSwipeRefreshLayout.refreshing(refreshLayout)

// refreshesはProgress状態になったら配信されるObservable<void>を返す
val refreshLayoutStream = RxSwipeRefreshLayout.refreshes(refreshLayout)

試してみる

LayoutにSwipeRefreshLayoutを追加

  • SwipeRefreshLayoutでListViewを囲みます。
    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/refresh"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ListView
            android:id="@+id/list_event"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </android.support.v4.widget.SwipeRefreshLayout>

Activityに実装

  • サンプルとしてAtndのAPIを叩いてイベント情報をObservableで返すというものを作っています。下に引っ張ったらプログレスが表示されて次ページ分が読み込まれるというサンプルになっています。
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {

        val view = inflater.inflate(R.layout.fragment_item, container, false)

        // ListViewにAdaptorとリスナをセット
        mListView = view.findViewById(R.id.list_event) as AbsListView
        mListView?.adapter = mAdapter
        mListView?.onItemClickListener = this

        // Observerを作成
        val observer = Observers.create<AtndApi.Companion.Entity>(
                { t ->
                    // onNext
                    mAdapter?.add(t.title)
                    mAdapter?.notifyDataSetChanged()
                },
                { e ->
                    // onError
                    println("Error!! ${e.message}")
                },
                {
                    // onComplete
                    mAdapter?.notifyDataSetChanged()
                })

        // Observableを作成
        // 配信時は新しいスレッド
        // 監視者はメインスレッド
        val observable = AtndApi.request(mWordForSearch, 1)
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())

        val refreshLayout = view.findViewById(R.id.refresh) as SwipeRefreshLayout

        // ローディング中のプログレスの色を設定
        refreshLayout.setColorSchemeColors(Color.RED, Color.GREEN, Color.BLUE)
        refreshLayout.setProgressBackgroundColorSchemeColor(Color.LTGRAY)

        val refreshLayoutRefreshing = RxSwipeRefreshLayout.refreshing(refreshLayout)
        val refreshLayoutStream = RxSwipeRefreshLayout.refreshes(refreshLayout)

        // 購読を開始

        // 初回のAPIコール
        mSubscriptions.add(observable.subscribe(observer))

        // RefreshLayoutを監視
        mSubscriptions.add(refreshLayoutStream.subscribe({

            // ローディングが始まったらAPIコール
            val subscription = AtndApi.request(mWordForSearch, mAdapter?.count!! + 1)
                    .subscribeOn(Schedulers.newThread())
                    .observeOn(AndroidSchedulers.mainThread())
                    .finallyDo({
                        // 読み込みが終わったらRefreshLayoutのローディング状態を解除
                        refreshLayoutRefreshing.call(false)
                    })
                    .subscribe(observer)

            mSubscriptions.add(subscription)
        }))

        return view
    }

    override fun onDestroyView() {
        super.onDestroyView()

        // 購読を解除する
        mSubscriptions.unsubscribe()
    }
  • プログレスはこんな感じで表示されます。