既然我们都已经有了上传资料,当然我们也要有可以看我们所有上架内容的地方,还有下架资料的地方啦!! 且我们要让只有当前登入的人才可以删除! 不然随便一个人都可以去删除,那着实有点监介
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)
}
这样就大功告成啦!!
成果如下
Q: 终於要讲效能了! A: 以Loading为范例讲黑~ Animation Loading 直...
React 事件处理 React 和 HTML 事件处理的语法略有不同: HTML 的事件语法: &...
认识一些软件开发的专业术语 在做软件专案的时候,常常会看到一些英文简写,像是 Day 01 讲到的 ...
1. 如何让panic,包含一个值 在呼叫panic函数时,把某个值做为参数传给该函数就可以了。pa...
已经先有测试资料了 来试试看删除文件的方法 doc_info/views.py 一样使用修饰器来验证...