好的,那接下来我们就要来显示我们的资料啦!! 由於我们的资料会有一个是对方传过来的,一个是我们自己发送过去的,而时间的先後顺序Realtime那边会按照先後顺序帮我们排序起来,所以我们不用去管它。
既然提到了recyclerview,我们当然要有item_view_list阿,而且因为我们这次会有一个是对方传过来的,一个是我们传过去的(简单分法就是一个在左,一个在右),所以我们会建立两个item_view_list。
简单明了,带点line的风格
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="message"
type="com.example.petsmatchingapp.model.Message" />
</data>
<RelativeLayout
android:padding="5dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.example.petsmatchingapp.utils.JFTextView
android:id="@+id/tv_item_message_me_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toStartOf="@id/tv_item_message_me_message"
android:layout_alignBottom="@id/tv_item_message_me_message"
android:layout_marginEnd="5dp"
android:textSize="12sp"
tools:text = "13:00">
</com.example.petsmatchingapp.utils.JFTextView>
<com.example.petsmatchingapp.utils.JFTextView
android:id="@+id/tv_item_message_me_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{message.message}"
android:layout_alignParentEnd="true"
android:padding="5dp"
android:textSize="16sp"
android:maxWidth="250dp"
android:background="@drawable/message_background_me"
tools:text = "你今天吃饱了吗?">
</com.example.petsmatchingapp.utils.JFTextView>
</RelativeLayout>
</layout>
这次我们用RelativeLayout的原因是因为,我原本用constraintlayout,就是不能从右边排版,所以我们这次用RelativeLayout的android:layout_alignParentEnd="true"
也要新增我们message的background喔
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid
android:color="@color/message_me_color">
</solid>
<corners
android:radius="20dp">
</corners>
</shape>
绿色颜色代码:#78e37a
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="message"
type="com.example.petsmatchingapp.model.Message" />
</data>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp">
<ImageView
android:id="@+id/iv_item_message_other_image"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentStart="true"
tools:src = "@drawable/icon_dog"/>
<com.example.petsmatchingapp.utils.JFTextView
android:id="@+id/item_message_other_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:text="@{message.message}"
android:padding="5dp"
android:maxWidth="250dp"
android:textSize="16sp"
android:layout_toEndOf="@id/iv_item_message_other_image"
android:layout_alignTop="@id/iv_item_message_other_image"
android:background="@drawable/message_backgorund_other"
tools:text = "今天我吃饱了喔,我想应该也还没吃饱啦"/>
<com.example.petsmatchingapp.utils.JFTextView
android:id="@+id/item_message_other_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toEndOf="@id/item_message_other_message"
android:layout_alignBottom="@id/item_message_other_message"
android:layout_marginStart="5dp"
tools:text="03:05 pm"
android:textSize="12sp"
app:layout_constraintStart_toEndOf="@id/item_message_other_message">
</com.example.petsmatchingapp.utils.JFTextView>
</RelativeLayout>
</layout>
background只是把刚刚的绿色改成白色
跟以往的adapter不一样,我们这次因为有两个item_view_list,所以我们这次会在adapter来判断,而判断的依据就是这个message的资料的发送者是不是现在的使用者
直接在Adapter的class新增以下,让我们可以判断要用哪一个viewHolder
private val MESSAGE_TYPE_LEFT = 0
private val MESSAGE_TYPE_RIGHT = 1
private var firebaseUser: FirebaseUser? = null
再过来就是override以下,来判断是否是现在登入的user
override fun getItemViewType(position: Int): Int {
firebaseUser = FirebaseAuth.getInstance().currentUser
if (firebaseUser?.uid == getItem(position).send_user_id){
return MESSAGE_TYPE_RIGHT
}else{
return MESSAGE_TYPE_LEFT
}
}
再过来要新增两个viewHolder,一个是我们传过去的,一个是别人传过来的
首先先做我们传出去的
class MyMessageViewHolder(val binding: MessageItemListMeBinding):RecyclerView.ViewHolder(binding.root){
//这个funtion是我们要转换时间的
fun getDate(timestamp: Any): String{
//我们只要小时跟分钟就好
val mFormat = "HH:mm"
val sdf = SimpleDateFormat(mFormat, Locale.getDefault())
return sdf.format(timestamp)
}
//一样绑定资料,只有时间因为需要转换,所以我们写在adapter
fun bind(item:Message){
binding.message = item
item.time?.let {
binding.tvItemMessageMeTime.text = getDate(it)
}
binding.executePendingBindings()
}
}
别人传过来的一样
class OtherMessageViewHolder(val binding: MessageListItemOtherBinding): RecyclerView.ViewHolder(binding.root){
fun getDate(timestamp: Any): String{
val mFormat = "HH:mm"
val sdf = SimpleDateFormat(mFormat, Locale.getDefault())
return sdf.format(timestamp)
}
fun bind(item: Message){
binding.message = item
binding.itemMessageOtherTime.setText(getDate(item.time!!))
item.send_user_image?.let { Constant.loadUserImage(it,binding.ivItemMessageOtherImage) }
binding.executePendingBindings()
}
}
再过来就是要在onCreateViewHolder来回传不同的viewHolder
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
if(viewType == MESSAGE_TYPE_RIGHT){
return MyMessageViewHolder(MessageItemListMeBinding.inflate(LayoutInflater.from(parent.context)))
}else{
return OtherMessageViewHolder(MessageListItemOtherBinding.inflate(LayoutInflater.from(parent.context)))
}
}
然後绑定viewHolder
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val model = getItem(position)
when(holder){
is MyMessageViewHolder ->{
holder.bind(model)
}
is OtherMessageViewHolder -> {
holder.bind(model)
}
}
}
回到ChatRoomFragment,来setAdapter
private fun setAdapter(){
chatAdapter = ChatRoomAdapter()
binding.rvChatRoom.adapter = chatAdapter
val linerLayout = LinearLayoutManager(requireContext())
//让我们的list从尾开始生成
linerLayout.reverseLayout = true
binding.rvChatRoom.layoutManager = linerLayout
}
接下来我们会读取数据,我们可以透过设定addValueEventListener来帮助我们当数据有更改的时候,我们就拿出所有数据
首先我们先去ChatViewModle新增
private val _messageList = MutableLiveData<List<Message>>()
val messageList: LiveData<List<Message>>
get() = _messageList
fun messageValueListener(fragment: ChatRoomFragment, currentUID: String, invitationUID: String){
val ref = FirebaseDatabase.getInstance().reference.child(currentUID).child(invitationUID)
ref.addValueEventListener(object : ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
val list = mutableListOf<Message>()
for (i in snapshot.children){
val message = i.getValue(Message::class.java)
if (message != null) {
list.add(message)
}
}
_messageList.postValue(list.reversed())
}
override fun onCancelled(error: DatabaseError) {
TODO("Not yet implemented")
}
})
}
我们要加入我们当前使用者的userId,以及要聊天的user_id,因为我们的所有聊天内容,不管是我传给别人,或是别人传给我,都会同时储存在A→B,B→A的资料。
而onDataChagne,它的DataSnapshot是会回传所有的资料,而不是只有新的资料,所以我们要把它全部加入到livedata,并且显示到recyclerview。
而如果只是想要观察child的资料呢,如你想要某个新增的资料,而不是全部都回传给你,addChildEventListener是你的好选择,里面可以override以下的funtion,你可以看是要观察新增的还是删除的..
ref.addChildEventListener(object : ChildEventListener{
override fun onChildAdded(snapshot: DataSnapshot, previousChildName: String?) {
val model = snapshot.getValue(Message::class.java)
Timber.d("child model : $model")
}
override fun onChildChanged(snapshot: DataSnapshot, previousChildName: String?) {
val model = snapshot.getValue(Message::class.java)
Timber.d("child model chagne: $model")
}
override fun onChildRemoved(snapshot: DataSnapshot) {
}
override fun onChildMoved(snapshot: DataSnapshot, previousChildName: String?) {
}
override fun onCancelled(error: DatabaseError) {
}
})
而因为我们这次是要丢入到livedata,所以我们要所有资料,所以我们就选择addValueEventListener
再过来就是在Fragment呼叫它罗。
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
chatViewModel.messageValueListener(this,accountViewModel.userDetail.value!!.id,matchingViewModel.selectedInvitation.value!!.user_id)
super.onViewCreated(view, savedInstanceState)
}
如果我们希望使用者在没有网路状态的时候,依旧可以看到聊天讯息呢? 总是怕如果再没有网路的情况,竟然也忘记邀约的时间?
我们直接在 MyApp.kt的onCreated新增以下,就好啦!
Firebase.database.setPersistenceEnabled(true)
它会缓存聊天纪录,而且也可以实现当你今天离线写入的时候,别怕,当你恢复网路的时候就会自动帮你写入~
我们希望我们的chatRoom上面显示我们的聊天对象名称,所以我们要把name放入Invitation
data class Invitation(
val id: String = "",
val user_id: String = "",
//新增这个
val user_name: String? = "",
val pet_image: String = "",
val pet_type: String = "",
val pet_type_description: String = "",
val area: String = "",
val date_place: String = "",
val date_time: Timestamp? = null,
val note: String = "",
val update_time: Timestamp? = null ,
)
并且回到AddInvitationFragment的onClick里面新增
user_name = accountViewModel.userDetail.value?.name,
最後回到ChatRoomFragment来新增以下,把它显示出来就好啦
matchingViewModel.selectedInvitation.value?.user_name?.let {
binding.tvChatRoomAcceptUserName.text = it
}
<<: D19 - 今晚我想来点 唯独派 getter 唯写派 setter
前言 在上一章节中,我们介绍了基本使用者管理,在这一章节中,介绍档案之目录与档案系统权限。 Linu...
使用 fd-find 代替 find,效率更好 下载方式 : sudo apt-get instal...
前言 到了这个章节大家可能会开始回想,刚开始听到K8S时很多人都说Kubernetes的AutoSc...
现在开发者写程序,最方便的一点,就是不会的地方,可以问 Google 在 Google 中输入 Sw...
卡多利亚良食故事馆 地点:台南市後壁区42-27号 时间:9:00~17:00 对於一个研替来说 最...