当用户滚动时如何显示或隐藏日期文本视图就像whatsapp聊天android Kotlin
Posted
技术标签:
【中文标题】当用户滚动时如何显示或隐藏日期文本视图就像whatsapp聊天android Kotlin【英文标题】:How to show or hide date textview when user scroll just like whatsapp chat android Kotlin 【发布时间】:2021-11-29 18:58:58 【问题描述】:嘿,我正在开发聊天应用程序。我完成了来自How to use different layouts for incoming & outgoing chat messages 的传入和传出消息。我也试过这个How to show date in between conversation in recyclerview or in listview。我添加了输出我的所有外观。
ConversationAdapter.kt
class ConversationAdapter : ListAdapter<Message, RecyclerView.ViewHolder>(MESSAGE_COMPARATOR)
companion object
private val MESSAGE_COMPARATOR = object : DiffUtil.ItemCallback<Message>()
override fun areItemsTheSame(oldItem: Message, newItem: Message): Boolean
return oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: Message, newItem: Message): Boolean
return ((oldItem.name == newItem.name) && (oldItem.text == oldItem.text)
&& (oldItem.time == newItem.time) && (oldItem.type == newItem.type))
private const val INCOMING_MESSAGE = 1
private const val OUTGOING_MESSAGE = 2
private const val TIMING = 3
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder
return when (viewType)
INCOMING_MESSAGE ->
IncomingViewHolder(
IncomingLayoutBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
OUTGOING_MESSAGE ->
OutGoingViewHolder(
OutgoingLayoutBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
else ->
TimingViewHolder(
TimingLayoutBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int)
when (holder)
is IncomingViewHolder ->
holder.bindItem(getItem(position))
is OutGoingViewHolder ->
holder.bindItem(getItem(position))
is TimingViewHolder ->
holder.bindItem(getItem(position))
override fun getItemViewType(position: Int): Int
return when
getItem(position).type.equals("incoming") ->
INCOMING_MESSAGE
getItem(position).type.equals("outgoing") ->
OUTGOING_MESSAGE
getItem(position).type.equals("added") ->
TIMING
else ->
super.getItemViewType(position)
inner class IncomingViewHolder(val binding: IncomingLayoutBinding) : RecyclerView.ViewHolder(binding.root)
fun bindItem(item: Message)
binding.incomingMessage.text = item.text
inner class OutGoingViewHolder(val binding: OutgoingLayoutBinding) : RecyclerView.ViewHolder(binding.root)
fun bindItem(item: Message)
binding.outGoingMessage.text = item.text
inner class TimingViewHolder(val binding: TimingLayoutBinding) : RecyclerView.ViewHolder(binding.root)
fun bindItem(item: Message)
binding.timing.text = item.time
MM.kt
data class Message(
val id: String? = null,
val text: String? = null,
val time: String? = null,
val type: String? = null,
val name: String? = null
)
MainActivity.kt
class MainActivity : BaseActivity()
lateinit var binding: MainLayoutBinding
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
binding = MainLayoutBinding.inflate(layoutInflater)
setContentView(binding.root)
setupAdapter()
private fun setupAdapter()
val list = listOf<Message>(
Message(id = "1", text = "avc", time = "2021-10-08T15:34:00", type = "outgoing"),
Message(id = "2", text = "dsds", time = "2021-10-08T15:36:00", type = "incoming"),
Message(id = "3", type = "added", time = "2021-10-08T00:00:00", name = "ABC"),
Message(id = "4", type = "added", time = "2021-10-07T15:50:00", name = "XYZ"),
Message(id = "5", text = "asvc", time = "2021-10-07T15:46:00", type = "outgoing"),
Message(id = "6", text = "asvc", time = "2021-10-07T15:46:00", type = "incoming"),
Message(id = "6", text = "asvc", time = "2021-10-06T12:34:00", type = "outgoing"),
Message(id = "8", text = "asvc", time = "2021-10-06T12:50:00", type = "incoming"),
Message(id = "9", type = "added", time = "2021-10-06T12:46:00", name = "DEF")
)
val adapter = ConversationAdapter()
binding.conversationRecyclerView.adapter = adapter
adapter.submitList(list)
timing_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_
android:background="@color/aqua"
android:layout_>
<TextView
android:id="@+id/timing"
android:layout_
android:layout_
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
incoming_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_
android:background="@color/red"
android:layout_>
<TextView
android:id="@+id/incomingMessage"
android:layout_
android:layout_
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
outgoing_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_
android:background="@color/yellow"
android:layout_>
<TextView
android:id="@+id/outGoingMessage"
android:layout_
android:layout_
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
输出
如何实现其他功能。
1.由于我的列表太大,如何对我的日期进行分类以有效地显示每天对话之间的日期。我刚刚添加了示例
2. 用户滚动聊天时如何显示日期的滚动效果,就像whatsapp的昨天,今天和其他日期格式一样。
3. 在上面的列表示例中,有一个属性added,这意味着有人在群聊中添加了。在whatsapp XYZ 使用群组邀请链接加入。
我正在添加whatsapp的屏幕截图
【问题讨论】:
您可以使用getItemViewType。 Example in java @Kaushik 我已经使用 getitemViewType 但它不起作用。请参阅我更新的问题。谢谢 【参考方案1】:即使不清楚“这在我的解决方案中不起作用”是什么意思
而且我不知道问题是崩溃还是空列表还是...但是
在方法areItemsTheSame
和areContentsTheSame
中检查项目的逻辑是错误的:
方法areItemsTheSame
用于检查旧项目的身份是否等于新项目的身份。
并使用areContentsTheSame
方法来检查两个项目的值是否相等
所以在areContentsTheSame
中,您只需检查id
的项目,因为其他内容是项目的值,而您在areContentsTheSame
中编写的内容没有任何意义,因为您只是在检查项目的参考。所以把它改成这样:
private val MESSAGE_COMPARATOR = object : DiffUtil.ItemCallback<MM>()
override fun areItemsTheSame(oldItem: MM, newItem: MM): Boolean
return oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: MM, newItem: MM): Boolean
return ((oldItem.added == newItem.added)
&& (oldItem.addedTime == newItem.addedTime)
&& (oldItem.sentAt == newItem.sentAt)
&& (oldItem.text == newItem.text))
【讨论】:
感谢您回复我。我用完整的解释更新了我的问题。请再看看【参考方案2】:TimingViewHolder
的持有者在哪里?
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int)
if (holder is IncomingViewHolder)
holder.bindItem(getItem(position))
else if (holder is OutGoingViewHolder)
holder.bindItem(getItem(position))
[更新]
由于我的列表太大,如何对我的日期进行分类以显示每天对话之间的日期效率低下。
您想制作这样的对话界面吗?
如果是,你可以试试这个方法,为了解释清楚,我需要修改你的代码。
data class MessageEntry
// 它来自服务器,是原始数据
数据类Message
// 来自MessageEntry
data class MessageEntry(
val id: String? = null,
val text: String? = null,
val time: String? = null,
val type: String? = null,
val name: String? = null
)
确保 Message 类中没有可为空的字段
data class Message(
val id: String,
val text: String,
val time: Date, // SDK's class
val type: String, // It's better to use enum
val name: String
)
//Raw data from Server
val mutableRawList = mutableListOf<MessageEntry>(
MessageEntry(id = "1", text = "avc", time = "2021-10-08T15:34:00", type = "outgoing"),
MessageEntry(id = "2", text = "dsds", time = "2021-10-08T15:36:00", type = "incoming"),
MessageEntry(id = "3", type = "added", time = "2021-10-08T00:00:00", name = "ABC"),
MessageEntry(id = "4", type = "added", time = "2021-10-07T15:50:00", name = "XYZ"),
MessageEntry(id = "5", text = "asvc", time = "2021-10-07T15:46:00", type = "outgoing"),
MessageEntry(id = "6", text = "asvc", time = "2021-10-07T15:46:00", type = "incoming"),
MessageEntry(id = "6", text = "asvc", time = "2021-10-06T12:34:00", type = "outgoing"),
MessageEntry(id = "8", text = "asvc", time = "2021-10-06T12:50:00", type = "incoming"),
MessageEntry(id = "9", type = "added", time = "2021-10-06T12:46:00", name = "DEF")
)
//I ignore the changing, MessageEntry.toMessage(), for an instance.
val mutableMessageList = mutableListOf<Message>(
Message(id = "1", text = "avc", time = Date(2021-10-08T15:34:00), type = "outgoing"),
Message(id = "2", text = "dsds", time = Date(2021-10-08T15:36:00), type = "incoming"),
Message(id = "3", type = "added", time = Date(2021-10-08T00:00:00), name = "ABC"),
Message(id = "4", type = "added", time = Date(2021-10-07T15:50:00), name = "XYZ"),
Message(id = "5", text = "asvc", time = Date(2021-10-07T15:46:00), type = "outgoing"),
Message(id = "6", text = "asvc", time = Date(2021-10-07T15:46:00), type = "incoming"),
Message(id = "6", text = "asvc", time = Date(2021-10-06T12:34:00), type = "outgoing"),
Message(id = "8", text = "asvc", time = Date(2021-10-06T12:50:00), type = "incoming"),
Message(id = "9", type = "added", time = Date(2021-10-06T12:46:00), name = "DEF")
)
private var lastMessageDate: Date? = null
...
val adapter = ConversationAdapter()
binding.conversationRecyclerView.adapter = adapter
// Need to change raw list MessageEntry to Message
// TODO, maybe you should change the related code of xxxAdapter, xxxViewHolder...
adapter.submitList(mutableMessageList.toFlatMessageItems)
toFlatMessageItems()
的方法是什么
//Rebuild the message list from TreeMap<Date, List<Message>>
private fun MutableList<Message>.toFlatMessageItems() =
mutableListOf<Message>().apply
val messageByDate = this@toFlatMessageItems.splitByDate()
for (time in messageByDate.keys)
if (time != lastMessageDate)
val dateHeaderItem = time.toMessage()
add(dateHeaderItem)
for (message in messageByDate.getValue(date))
val messageItem = message
add(messageItem)
lastMessageDate = messageByDate.keys.lastOrNull()
splitByDate
的方法是什么:
//Split the data by Date
private fun List<Message>.splitByDate() =
TreeMap<Date, List<Message>>(Collections.reverseOrder()).apply
for (message in this@splitByDate)
val date = Message.time.noTime() // need you implement noTime() extension function, only the (year, month, day)
var value = this[date] as MutableList<Message>?
if (value == null)
value = mutableListOf()
this[date] = value
value.add(message)
Kotlin 扩展函数
-
将 MessageEntry 更改为 Message
MessageEntry.toMessage() = Message(
id = id ?: "", // or add an extension fuction to set empty string
text = text ?: "",
time = time.toDate(), // extension function from time string to Date, for spliting date
type = type ?: "",
name = name ?: ""
)
-
时间(字符串或长)到日期
String/Long.toDate(): Date
//TODO change string/long time format to Date
-
日期.noTime()
Date.toDate(): Date
//TODO, only year, month, day
-
Date.toMessage()
Date.toMessage() = Message(
id = "",
text = "",
time = time, // the type of time is Date
type = "added",
name = ""
)
注意:
-
我没有测试,只是给你一个想法。
使用 MutableList 或 List,由您决定。
有些扩展功能需要你完成。
【讨论】:
感谢您回复我。我用完整的解释更新了我的问题。请再看看 更新代码和输出后,我认为您已经解决了您的问题。 UI 显示消息列表是什么。 我想对我的日期进行分类。如何做到这一点? @vivekmodi,我更新了我的答案,请检查一下。 很好的答案,它对我有很大帮助。你能帮我解决这个当用户滚动聊天时如何显示日期的滚动效果,就像whatsapp有昨天、今天和其他日期格式一样。以上是关于当用户滚动时如何显示或隐藏日期文本视图就像whatsapp聊天android Kotlin的主要内容,如果未能解决你的问题,请参考以下文章
当softKeyBoard隐藏按钮和其他元素时如何使布局可滚动[ANDROID]
Swift:仅当键盘隐藏 TextField 或 Button 时滚动视图
如何在 UIView 中设置滚动视图以向上滑动被键盘隐藏的文本字段?