簡體   English   中英

如何使用分頁庫在回收站視圖中添加日期分隔符?

[英]How to add date separators in recycler view using Paging Library?

經過大量搜索,我知道使用常規適配器是可能的,但我不知道如何使用分頁庫來做到這一點。 我不需要代碼只是一個線索。

例子

要添加分隔符,您基本上有兩個選項:

  1. 基於視圖,您明確地將分隔符作為“項目”包含在列表中,並為這些分隔符定義新的視圖類型。 允許列表重復使用分隔符視圖,但這意味着您需要在定義數據時考慮分隔符。
  2. 基於數據,每個項目實際上都有一個分隔符視圖,但它只顯示在特定項目上。 根據某些標准,您在綁定視圖持有者時顯示或隱藏它。

對於分頁庫,只有選項 2 是可行的,因為它僅部分加載數據並且插入分隔符變得更加復雜。 您只需要找出一種方法來檢查項目x是否與項目x-1並根據結果在視圖中顯示/隱藏日期部分。

我和你在同一個地方,我想出了這個解決方案。

一個重要的注意事項,為了實現這一點,我不得不將我的日期轉換器更改為數據庫,從 long 到 string 以存儲時間戳

這些是我的轉換器

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,以便它始終出現在其他報告的前面)

然后我使用 union 將它與我的列表結合起來,並按虛擬日期對它們進行排序

我必須從 long 更改為 string 的原因是因為在 sql 中使用 string 創建虛擬日期並將日期部分與整個日期時間分開要容易得多

上面創建了一個這樣的列表:

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> (不是特定的viewholder)的實現

添加到伴生對象:

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

並像這樣覆蓋 get 視圖類型:

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

然后我創建了兩個單獨的視圖持有者:

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()
            }
        }
    }
}

我希望這有助於並激勵人們找到更好的解決方案

您可以使用Paging 3庫中的insertSeparators獲得相同的結果。 確保您的物品sorted日期sorted

在內部或viewmodel檢索類似這樣的Pager

private val communicationResult: Flow<PagingData<CommunicationHistoryItem>> = Pager(
    PagingConfig(
        pageSize = 50,
        enablePlaceholders = false,
        maxSize = 400,
        initialLoadSize = 50
    )
) {
    CommunicationPagingSource(repository)
}.flow.cachedIn(viewModelScope)

畢竟像標題一樣insert separators

val groupedCommunicationResult = communicationResult
        .map { pagingData -> pagingData.map { CommunicationHistoryModel.Body(it) } }
        .map {
            it.insertSeparators{ after, before ->
                if (before == null) {
                    //the end of the list
                    return@insertSeparators null
                }

                val afterDateStr = after?.createdDate
                val beforeDateStr = before.createdDate

                if (afterDateStr == null || beforeDateStr == null)
                    return@insertSeparators null

                val afterDate = DateUtil.parseAsCalendar(afterDateStr)?.cleanTime()?.time ?: 0
                val beforeDate = DateUtil.parseAsCalendar(beforeDateStr)?.cleanTime()?.time ?: 0

                if (afterDate > beforeDate) {
                    CommunicationHistoryModel.Header( DateUtil.format(Date(beforeDate))) // dd.MM.yyyy
                } else {
                    // no separator
                    null
                }
            }
        }

cleanTime需要grouping通過dd.MM.yyyy忽略時間

fun Calendar.cleanTime(): Date {
    set(Calendar.HOUR_OF_DAY, 0)
    set(Calendar.MINUTE, 0)
    set(Calendar.SECOND, 0)
    set(Calendar.MILLISECOND, 0)
    return this.time
}

綁定數據時也傳入前一項

  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

如前所述這里 ,尋呼圖書館工作與PagedListAdapter PagedListAdapter從RecyclerView.Adapter擴展,因此您可以像在Recyclerview( 示例 )中那樣完成此操作。 只用兩個字 - 您需要為日期標題和內容項使用不同的視圖類型。

Kiskae 的回答非常好,對於您的情況,選項 2 可能效果很好。

在我的情況下,我想要一個不在數據庫中的附加項目,如下所示:

  • 顯示所有
  • 第 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();

這意味着數據源實際上在開始時返回了另一個項目,無需調整位置。 在您的適配器中,您可以檢查該項目並以不同方式處理它。

在您的情況下,查詢必須有所不同,但我認為這是可能的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM