web-dev-qa-db-ja.com

ページングライブラリを使用してリサイクラービューで日付区切り記号を追加する方法

多くの検索の結果、通常のアダプターでそれが可能であることがわかりましたが、ページングライブラリを使用してそれを行う方法がわかりません。手がかりだけのコードは必要ありません。


11
someguy234

セパレータを追加するには、基本的に2つのオプションがあります。

  1. ビューベースでは、セパレータをリストの「アイテム」として明示的に含め、それらのセパレータの新しいビュータイプを定義します。リストがセパレータービューを再利用できるようにしますが、データを定義するときにセパレーターを考慮する必要があることを意味します。
  2. データベースの各アイテムには実際にはセパレータビューがありますが、特定のアイテムにのみ表示されます。いくつかの基準に基づいて、ビューホルダーのバインド中に表示または非表示にします。

ページングライブラリの場合、オプション2のみが実行可能です。これは、データを部分的にしかロードせず、セパレーターの挿入がはるかに複雑になるためです。アイテムxがアイテムx-1と異なる日であるかどうかを確認する方法を見つけ、結果に応じてビューの日付セクションを表示/非表示にするだけです。

9
Kiskae

私はあなたと同じ場所にいて、この解決策を思いつきました。

ただし、これを実装するには、日付コンバータをデータベースに変更する必要がありました。

これらは私のコンバータです

class DateConverter {
    companion object {
        @JvmStatic
        val formatter = SimpleDateFormat("yyyyMMddHHmmss", Locale.ENGLISH)

        @TypeConverter
        @JvmStatic
        fun toDate(text: String): Date = formatter.parse(text)

        @TypeConverter
        @JvmStatic
        fun toText(date: Date): String = formatter.format(date)
    }
}

いくつかの開始情報ですが、表示したいレポートヘッダーのリストがあり、ページを移動してフィルタリングできます

それらはこのオブジェクトによって表されます:

data class ReportHeaderEntity(
@ColumnInfo(name = "id") override val id: UUID
, @ColumnInfo(name = "name") override val name: String
, @ColumnInfo(name = "description") override val description: String
, @ColumnInfo(name = "created") override val date: Date)

また、リスト内の項目の間にセパレータを追加して、日付順に表示したいと思いました

私はこれを次のようにして達成しました:

このような部屋で新しいクエリを作成しました

 @Query(
    "SELECT id, name, description,created " +
            "FROM   (SELECT id, name, description, created, created AS sort " +
            "        FROM   reports " +
            "        WHERE  :filter = '' " +
            "                OR name LIKE '%' || :filter || '%' " +
            "                OR description LIKE '%' || :filter || '%' " +
            "        UNION " +
            "        SELECT '00000000-0000-0000-0000-000000000000' as id, Substr(created, 0, 9) as name, '' as description, Substr(created, 0, 9) || '000000' AS created, Substr(created, 0, 9) || '256060' AS sort " +
            "        FROM   reports " +
            "        WHERE  :filter = '' " +
            "                OR name LIKE '%' || :filter || '%' " +
            "                OR description LIKE '%' || :filter || '%' " +
            "        GROUP  BY Substr(created, 0, 9)) " +
            "ORDER  BY sort DESC ")

fun loadReportHeaders(filter: String = ""): DataSource.Factory<Int, ReportHeaderEntity>

これは基本的に、フィルター処理したすべてのアイテムの区切り線を作成します

また、並べ替え用のダミーの日付を作成します(時刻が25:60:60であるため、常に他のレポートの前に表示されます)。

次に、ユニオンを使用してこれを私のリストと組み合わせ、ダミーの日付で並べ替えます

長い文字列から文字列に変更する必要があった理由は、SQLで文字列を使用してダミーの日付を作成し、日付部分全体を日付時刻から分離する方がはるかに簡単だからです。

上記はこのようなリストを作成します:

00000000-0000-0000-0000-000000000000    20190522        20190522000000
e3b8fbe5-b8ce-4353-b85d-8a1160f51bac    name 16769  description 93396   20190522141926
6779fbea-f840-4859-a9a1-b34b7e6520be    name 86082  description 21138   20190522141925
00000000-0000-0000-0000-000000000000    20190521        20190521000000
6efa201f-d618-4819-bae1-5a0e907ddcfb    name 9702   description 84139   20190521103247

私のPagedListAdapterでPagedListAdapter<ReportHeader, RecyclerView.ViewHolder>の実装になるように変更しました(特定のビューホルダーではありません)

コンパニオンオブジェクトに追加:

companion object {
    private val EMPTY_ID = UUID(0L,0L)
    private const val LABEL = 0
    private const val HEADER = 1
}

そして、次のようにビュータイプを取得します:

override fun getItemViewType(position: Int): Int = if (getItem(position)?.id ?: EMPTY_ID == EMPTY_ID) LABEL else HEADER

次に、2つの個別のビューホルダーを作成しました。

class ReportHeaderViewHolder(val binding: ListItemReportBinding) : RecyclerView.ViewHolder(binding.root) 

class ReportLabelViewHolder(val binding: ListItemReportLabelBinding) : RecyclerView.ViewHolder(binding.root)

他のオーバーライドされたメソッドを次のように実装しました:

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
    val inflater = LayoutInflater.from(parent.context)
    return when (viewType) {
        HEADER -> ReportHeaderViewHolder(DataBindingUtil.inflate(inflater, R.layout.list_item_report, parent, false))
        else -> ReportLabelViewHolder(DataBindingUtil.inflate(inflater, R.layout.list_item_report_label, parent, false))
    }
}

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
    val reportItem = getItem(position)
    when (getItemViewType(position)) {
        HEADER -> {
            (holder as ReportHeaderViewHolder).binding.apply {
                report = reportItem
                executePendingBindings()
            }
        }
        LABEL -> {
            (holder as ReportLabelViewHolder).binding.apply {
                date = reportItem?.name
                executePendingBindings()
            }
        }
    }
}

これが人々にさらに良い解決策を見つける助けとなることを願っています

5
Cruces

ここ で述べたように、ページングライブラリは PagedListAdapter で動作します。また、PagedListAdapterはRecyclerView.Adapterから拡張されているため、Recyclerview( example )のように簡単に実行できます。簡単に言えば、日付ヘッダーとコンテンツアイテムには異なるビュータイプを使用する必要があります。

0
Kiryl Tkach

キスケの答え は優れており、あなたのケースではオプション2がおそらくうまく機能します。

私の場合、次のように、データベースにない項目を1つ追加したいと思いました。

  • すべて表示する
  • アイテム1
  • アイテム2

それもクリック可能である必要がありました。 getItemCountをオーバーライドして+1を返す通常の方法と、他のメソッドの位置をオフセットする方法があります。

しかし、まだ文書化されていない別の方法に出くわしました。これは、場合によっては役立つかもしれません。 unionを使用して、クエリに追加の要素を組み込むことができる場合があります。

@Query("select '' as name, 0 as id " +
        "union " +
        "select name, id from user " +
        "order by 1 asc")
DataSource.Factory<Integer, User> getAllDataSource();

つまり、データソースは実際には最初に別のアイテムを返し、位置を調整する必要はありません。アダプターでは、その項目を確認して、別の方法で処理できます。

あなたの場合、クエリは異なる必要がありますが、それは可能だと思います。

0
robinst

前のアイテムでもデータパスをバインドするとき

  override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    val item = getItem(position)
    val previousItem = if (position == 0) null else getItem(position - 1)
    holder.bind(item, previousItem)
  }

次に、すべてのビューがヘッダーを設定します。ヘッダーは、前のアイテムに同じヘッダーがない場合にのみ表示されます。

    val previousHeader =  previousItem?.name?.capitalize().first()
    val header = item?.name?.capitalize()?.first()
    view.cachedContactHeader.text = header
    view.cachedContactHeader.isVisible  = previousHeader != header
0
Matthew Shearer