【Android】RecyclerViewでEmptyViewをCustomViewで実装した時の思い出【Kotlin】

RecyclerViewにはListViewなどと異なりsetEmptyViewが用意されていないため、自分で実装する必要がある。
使うたびにActivityなどでgetItemCountを取得して切り替えてもいいんだけど
めんどくさいじゃん!何回も使うようならどこかで絶対忘れるじゃん!
という理由でCustomViewを作ることにしました。

紆余曲折の軌跡をここに記す・・・

最終的に書いたコード

RecyclerViewを継承したCustomViewの作成

SupportEmptyRecyclerView.kt
class SupportEmptyRecyclerView : RecyclerView {
 @JvmOverloads public constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0)
  : super(context, attrs, defStyleAttr) {
}
  var emptyView: View? = null

  private val emptyObserver = object : RecyclerView.AdapterDataObserver() {

    override fun onChanged() {
      if (adapter != null && emptyView != null) {
         val isEmpty = adapter.itemCount == 0
          emptyView!!.visibility = if (isEmpty) View.VISIBLE else View.GONE
          visibility = if (isEmpty) View.GONE else View.VISIBLE
      }
    }
  }

  override fun setAdapter(adapter: RecyclerView.Adapter<*>?) {
    super.setAdapter(adapter)

    adapter?.registerAdapterDataObserver(emptyObserver)
    emptyObserver.onChanged()
  }
}

RecyclerViewの状態が変わった時に描画を変更するよう、Observerを登録。
このへんのstackoverflow参考にした。
RecyclerViewの要素数に応じて表示を切り替える。
visibilityかよ〜と思ったけどListViewに生えてるsetEmptyViewの内部実装もそうらしい。
setAdapterをoverrideしているので、ActivityやFragmentでsetAdapterをするだけで設定される。

emptyView用のXMLの作成

view_empty.xml
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    >
  <LinearLayout
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:gravity="center"
      android:orientation="vertical"
      >
    <ImageView
        android:id="@+id/empty_image"
        android:layout_width="@dimen/empty_image_size"
        android:layout_height="@dimen/empty_image_size"
        android:layout_gravity="center"
        android:layout_margin="@dimen/spacing"
        android:src="@drawable/no_item"
        />

    <TextView
        android:id="@+id/empty_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="表示する内容がありません"
        android:textColor="@color/black_alpha_54"
        android:textSize="@dimen/text_large"
        />
  </LinearLayout>
</layout>

RecyclerViewを乗せたいレイアウトファイルに追加する

sample_fragment.xml

〜略〜

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >
      <me.rm_rf.view.widget.SupportEmptyRecyclerView
          android:id="@+id/recycler_view"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:padding="@dimen/spacing_xsmall"
          />
      <include
          layout="@layout/view_empty"
          android:id="@+id/included_empty_view"
          />
    </RelativeLayout>

ここでポイントなのはEmptyViewをRecyclerViewと同じファイルに書くこと。
RecylerViewとEmptyViewをRelativeLayoutで括ること。
同じ階層に置いていないと、RecyclerViewの範囲内でEmptyViewを
android:gravity="center"
したいのに効かなかったり、なんかうまいこと表示されなかったりする。

setAdaptersするだけでitem=0のときにEmptyViewが表示されるの便利だな〜