我正在構建一個聊天平臺,在那里我正在閱讀來自本地房間資料庫(ChatModel)的訊息串列。我需要在這些訊息之間添加日期分隔符。我必須使用多個視圖持有者,因此創建了一個密封類來區分專案
sealed class ChatUiModel {
data class ChatItem(val message: ChatModel) : ChatUiModel()
data class DateSeparatorItem(val time: String) : ChatUiModel()
}
我需要在串列的 2 個模型之間轉換帶有日期單獨專案的串列,我不精通 kotlin 中的集合函式,并且在 map/flatmap 等之間混淆。
.observe(viewLifecycleOwner) { messages ->
messages.map {
// if item prev.date < item next.date
ChatUiModel.DateSeparatorItem(it.date.toReadableTime())
ChatUiModel.ChatItem(it)
}
chatAdapter.submitList(messages)
}
達到這個
val items = mutableListOf<ChatUiModel>()
val data = messages.listIterator()
for (item in data) {
if (data.hasPrevious())
if (data.previous().time < item.time)
items.add(ChatUiModel.DateSeparatorItem(item.time))
items.add(ChatUiModel.ChatItem(item))
}
Timber.i("CHAT = $items")
uj5u.com熱心網友回復:
(這結束了很長時間,但我想你想要解釋發生了什么 - 如果你愿意,你可以跳到解決方案的最后)
好的,所以如果您不熟悉所有實用程式函式和一般函式操作,這有點棘手 - 您基本上要做的是將傳入的messages 轉換為ChatItems,但您還想將每條訊息與前一條訊息進行比較,并DateSeparatorItem在必要時輸出第一個,對嗎?
直線map是行不通的 - 只是將每個專案轉換為另一個專案(它將一個值映射到另一個值),有時您想將一個專案轉換為兩個(日期專案和聊天專案)。
您可以將map每個訊息專案放入一個串列中,并使其包含聊天專案或日期 聊天。這樣就可以為您提供一個串列串列,然后flatten您就可以將所有這些專案按順序排列在一個串列中。基本上就是flatmap這么干的!
所以現在您需要能夠比較多條訊息,以便您可以檢查日期。Kotlin 有這個windowed功能,就像在你的集合中滑動視圖一樣,所以它可以轉換[1, 2, 3, 4]為[[1, 2], [2, 3], [3, 4]],然后你可以處理這些組。有一個更方便的zipWithNext函式,它只生成Pairs 而不是任意大小的Lists - 即[(1, 2), (2, 3), (3, 4)],但windowed有一個有用的選項 -partialWindows允許該視窗繼續移動到串列的末尾,即使它用完專案來填充整個視窗:
listOf(1, 2, 3, 4).windowed(size=3, partialWindows=true).run(::println)
>> [[1, 2, 3], [2, 3, 4], [3, 4], [4]]
如果我們對大小為 2 的視窗執行此操作,我們將獲得每條原始訊息,如果有的話,還會獲得緊隨其后的訊息(zipWithNext當它用完完整對時將停止):
listOf(1, 2, 3, 4).windowed(size=2, partialWindows=true).run(::println)
>> [[1, 2], [2, 3], [3, 4], [4]]
我們可以用這個!
您現在的邏輯是接收一條訊息并將其與前一條進行比較,以查看是否需要在聊天專案之前插入日期 - 我建議將其翻轉,并通過檢查下一項的時間戳在當前專案之后插入日期。那是因為將每個專案與下一個專案一起提供給您,因此您無法查看前一個專案。windowed
我們在這里處理一個串列,我們需要將第一個專案與第二個專案進行比較(檢查是否有一個),但我們可以有點厚顏無恥,只是list.first()與list.last(). 我們知道將有一個或兩個專案 - 如果串列中只有一個專案(即它是最后一條訊息),那么我們將它與自身進行比較,因為如果時間戳是,我們只添加日期專案不同......好吧,如果是同一個專案,他們就不會!所以最后不會添加任何流氓日期專案。如果您這樣做,可能值得記錄代碼,因為它可能不清楚 - 如果需要,您可以撰寫一些更明確的邏輯。
以下是完成最后一件事的幾種方法: Kotlin Playground 示例
data class Message(val text: String, val time: Int)
val messages = listOf(
Message("hey", 1),
Message("u up", 1),
Message("lol", 3),
Message("wow", 10)
)
fun withMutableList() {
messages.windowed(size=2, partialWindows=true)
// or map followed by flatten()
.flatMap { items ->
val current = items.first()
val next = items.last()
// creating a mutable list with the chat item, optionally adding a date
mutableListOf<ChatUiModel>(ChatItem(current)).apply {
if (next.time > current.time) add(DateItem(next.time))
}
}
.forEach(::println)
}
fun withNulls() {
messages.windowed(size=2, partialWindows=true)
.flatMap { items ->
val current = items.first()
val next = items.last()
// either adding a date or a null, nulls get removed later
listOf(
ChatItem(current),
if (next.time > current.time) DateItem(next.time) else null
)
}
.filterNotNull()
.forEach(::println)
}
fun withSequence() {
sequence {
messages.windowed(size=2, partialWindows=true)
.forEach { items ->
val current = items.first()
val next = items.last()
// just yielding a stream of items, nice and neat!
yield(ChatItem(current))
if (next.time > current.time) yield(DateItem(next.time))
}
}.forEach(::println)
}
都給出了這個輸出:
ChatItem(message=Message(text=hey, time=1))
ChatItem(message=Message(text=u up, time=1))
DateItem(time=3)
ChatItem(message=Message(text=lol, time=3))
DateItem(time=10)
ChatItem(message=Message(text=wow, time=10))
uj5u.com熱心網友回復:
準備串列的一種簡單方法是:
messages
.groupBy { it.date }
.map { (date, chatModels) ->
listOf(DateSeparatorItem(date)) chatModels.map { ChatItem(it) }
}
.flatten()
自己試試
在這里,我們首先將所有訊息按其資料分組以獲得一個Map<Long, List<ChatModel>. 然后我們將地圖的每個條目映射到一個包含該日期的DateSeparator和ChatItems的新串列。最后,我們將整個串列展平以獲得所需的List<ChatUiModel>.
- 在我鏈接的代碼中,我使用
Long了日期。如果您有一個,String您可以使用 java.time API 輕松地將它們相互轉換。 - 如果您的訊息串列最初沒有排序,
sortedBy請先添加一個函式groupBy以對其進行排序。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/367475.html
標籤:安卓 列表 科特林 android-recyclerview 收藏
