既然我們都已經有了上傳資料,當然我們也要有可以看我們所有上架內容的地方,還有下架資料的地方啦!! 且我們要讓只有當前登入的人才可以刪除! 不然隨便一個人都可以去刪除,那著實有點監介
RecyclerView也就是所謂的動態視圖列表,我們透過它來呈現我們每一筆的約散! 還不太了解RecyclerView的夥伴們,可以參考官方文檔喔! https://developer.android.com/guide/topics/ui/layout/recyclerview?hl=zh-cn
主要是給我們Recyclerview的item
我們裡面用到 dataBinding,用法會是
<?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="invitation"
type="com.example.petsmatchingapp.model.Invitation" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp">
<LinearLayout
android:id="@+id/ll_home_invitation_item_list_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="2dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/iv_home_invitation_item_list_image"
android:layout_width="80dp"
android:layout_height="80dp"
android:scaleType="center">
</ImageView>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_home_invitation_item_list_description"
android:layout_width="200dp"
android:orientation="vertical"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:gravity="center"
android:layout_marginEnd="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/iv_home_invitation_item_list_delete"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@id/ll_home_invitation_item_list_image">
<!-- 在你想要指定的view塞入資料-->
<com.example.petsmatchingapp.utils.JFTextView
android:id="@+id/tv_home_invitation_item_list_pet_type"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{@string/home_fragment_pet_type(invitation.pet_type,invitation.pet_type_description)}"
tools:text="博美犬">
</com.example.petsmatchingapp.utils.JFTextView>
<com.example.petsmatchingapp.utils.JFTextView
android:id="@+id/tv_home_invitation_item_list_date_place"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{@string/home_fragment_pet_place(invitation.area,invitation.date_place)}"
tools:text="新竹縣客家園區">
</com.example.petsmatchingapp.utils.JFTextView>
<com.example.petsmatchingapp.utils.JFTextView
android:id="@+id/tv_home_invitation_item_list_date_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{@string/home_fragment_pet_time(invitation.date_time)}"
tools:text="2021/01/05">
</com.example.petsmatchingapp.utils.JFTextView>
</LinearLayout>
<ImageView
android:id="@+id/iv_home_invitation_item_list_delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:src="@drawable/ic_baseline_delete_forever_24"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@id/ll_home_invitation_item_list_description">
</ImageView>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
由於我們希望我們可以在每個string前面都新增說明,譬如說這個資料是時間,那我們就在前面新增約散時間:XXX,這樣使用者才不會誤會這個資料是代表什麼意思! 所以我們可以到string新增以下
<string name="home_fragment_pet_type">寵物種類: %1$s-%2$s</string>
<string name="home_fragment_pet_place">地點: %1$s-%2$s</string>
<string name="home_fragment_pet_time">時間: %1$s</string>
android:text="@{@string/home_fragment_pet_place(invitation.area,invitation.date_place)}"
我們要去新增一個ListAdapter,好讓我們可以在裡面綁定資料,而ListAdapter跟我們以往的Adapter有什麼不一樣呢? 簡單來說就是新增了DiffCallback,讓我們當今天的list有改變時,我們不用自己去notifyDataSetChanged()參考文章:https://codertw.com/程式語言/665013/
package com.example.petsmatchingapp.ui.adapter
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.example.petsmatchingapp.databinding.FragmentAddInvitationBinding
import com.example.petsmatchingapp.databinding.HomeInvitationItemListBinding
import com.example.petsmatchingapp.model.Invitation
import com.example.petsmatchingapp.ui.fragment.HomeFragment
import com.example.petsmatchingapp.utils.Constant
//我們傳入Fragment,好讓我們好呼叫HomeFragment的funtion,並且繼承ListAdapter,第一個塞入想要丟進的資料,第二個則是ViewHoder,再過來也要丟入 DiffCallback
class HomeAdapter(val fragment: HomeFragment): ListAdapter<Invitation, HomeAdapter.HomeViewHolder>(DiffCallback) {
//DiffCallback會幫我們比對新舊list是否有差異,讓我們不用去呼叫notifyDataSetChanged()
companion object DiffCallback: DiffUtil.ItemCallback<Invitation>(){
override fun areItemsTheSame(oldItem: Invitation, newItem: Invitation): Boolean {
return oldItem === newItem
}
override fun areContentsTheSame(oldItem: Invitation, newItem: Invitation): Boolean {
return oldItem.id == oldItem.id
}
}
class HomeViewHolder(val binding: HomeInvitationItemListBinding): RecyclerView.ViewHolder(binding.root){
fun bind(item: Invitation){
//我們透過databinding來綁定我們home_invitation_item_list的data的variable的name
binding.invitation = item
//透過Glide來顯示Pet圖片
Constant.loadPetImage(item.pet_image,binding.ivHomeInvitationItemListImage)
//要加入這個,才可以即時更新UI
binding.executePendingBindings()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeViewHolder {
return HomeViewHolder(HomeInvitationItemListBinding.inflate(LayoutInflater.from(parent.context)))
}
override fun onBindViewHolder(holder: HomeViewHolder, position: Int) {
val model = getItem(position)
holder.bind(model)
//在這邊設定delete的按鈕Listener
holder.binding.ivHomeInvitationItemListDelete.setOnClickListener{
fragment.setAndShowDeleteDialog(model.id)
}
}
}
這時候我們會看到setAndShowDeleteDialog這邊是紅字,沒關係,我們晚點再回來解決! 我們先初始化我們的adapter
在ConstraintLayout新增recyclerview
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_home_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.recyclerview.widget.RecyclerView>
我們回到HomeFragment新增以下,並且在onCreateView呼叫它
private fun setAdapter() {
//LinearLayoutManager負責RecyclerView是水平還是垂直,預設是垂直
binding.rvHomeFragment.layoutManager = LinearLayoutManager(requireContext())
homeAdapter = HomeAdapter(this)
//綁定adapter
binding.rvHomeFragment.adapter = homeAdapter
}
好的!! 我們的Adapter就完成啦!! 接下來則是要開始顯示我們的Invitation囉!
private val _homeInvitationList = MutableLiveData<List<Invitation>>()
val homeInvitationList: LiveData<List<Invitation>>
get() = _homeInvitationList
fun getCurrentUserInvitation(fragment:HomeFragment,id: String){
//直接找到 Invitaion的集合
Firebase.firestore.collection(Constant.INVITATION)
//用get去拿資料
.get()
.addOnSuccessListener {
//創立一個空的mutableList
val currentInvitationList = mutableListOf<Invitation>()
//因為get會拿到所有的資料,所以我們要透過for迴圈來找出資料欄位裡面的user_id是符合我們當前登入的使用者Id
for (i in it.documents){
val model = i.toObject(Invitation::class.java)
if (model?.user_id == id){
//若符合則加入list
currentInvitationList.add(model)
}
}
//最後再把list加入到livedata
_homeInvitationList.postValue(currentInvitationList)
}
.addOnFailureListener {
fragment.getCurrentUserInvitationListFail(it.toString())
}
}
並且在onResume()來呼叫我們的funtion
override fun onResume() {
accountViewModel.userDetail.value?.id?.let { matchingViewModel.getCurrentUserInvitation(this,it) }
super.onResume()
}
fun getCurrentUserInvitationListFail(e: String){
showSnackBar(e,true)
}
回到HomeFragment,並在onCreateView新增
//觀察livedata變化時,新增資料到adapter
matchingViewModel.homeInvitationList.observe(viewLifecycleOwner, Observer {
//把list丟進去
homeAdapter.submitList(it)
})
這時候就可以看到我們的Invitation的列表囉!!
我們現在需要處理的就是setAndShowDeleteDialog(),我們希望當今天user按下在紅色垃圾桶的時候,會顯示對話框AlertDialog,並且根據使用者的選擇來刪除或關掉對話框。
<string name="alertDialog_title_do_you_want_delete">刪除約散</string>
<string name="alertDialog_message_delete_description">刪除約散後,其他會員無法看到您的邀約。</string>
<string name="alertDialog_delete_positive_yes">刪除</string>
<string name="alertDialog_delete_negative_no">再考慮一下</string>
<string name="delete_successful">成功刪除!</string>
fun setAndShowDeleteDialog(id: String){
//建立builder來設定一些參數
val builder = AlertDialog.Builder(requireContext())
builder.apply {
//設定Title
setTitle(resources.getString(R.string.alertDialog_title_do_you_want_delete))
//設定Message
setMessage(resources.getString(R.string.alertDialog_message_delete_description))
//設定肯定的字跟ClickListener
setPositiveButton(resources.getString(R.string.alertDialog_delete_positive_yes),object :DialogInterface.OnClickListener{
override fun onClick(dialog: DialogInterface?, which: Int) {
//使用者按肯定,就傳入Invitation的id,並刪除
deleteInvitation(id)
//關掉對話框
dialog?.dismiss()
}
})
//否定的字跟ClickListener
setNegativeButton(resources.getString(R.string.alertDialog_delete_negative_no),object :DialogInterface.OnClickListener{
override fun onClick(dialog: DialogInterface?, which: Int) {
//關掉對話框
dialog?.dismiss()
}
})
}
val alertDialog = builder.create()
//設定不能點擊其它地方關掉
alertDialog.setCancelable(false)
alertDialog.show()
}
接下來則是要到MatchingViewModel來新增刪除的funtion
fun deleteInvitation(id: String,fragment: HomeFragment){
Firebase.firestore.collection(Constant.INVITATION)
.document(id)
//直接用delete來刪除我們特定的資料
.delete()
.addOnSuccessListener {
fragment.deleteInvitationSuccess()
}
.addOnFailureListener {
fragment.deleteInvitationFail(it.toString())
}
}
這時候發現有紅字! 一樣來HomeFragment新增
fun deleteInvitationSuccess() {
showSnackBar(resources.getString(R.string.delete_successful), false)
matchingViewModel.getCurrentUserInvitation(this,accountViewModel.getCurrentUID()!!)
}
fun deleteInvitationFail(e: String) {
showSnackBar(e, true)
}
這樣就大功告成啦!!
成果如下