如何使用kotlin滑动删除存储在firestore中的recyclerview项目?
Posted
技术标签:
【中文标题】如何使用kotlin滑动删除存储在firestore中的recyclerview项目?【英文标题】:How to swipe delete recyclerview items stored in firestore using kotlin? 【发布时间】:2021-08-25 04:21:25 【问题描述】:下面是我的 app_bar_main 文件,其中包含一个工具栏和用于创建板的工厂。 这个布局文件包含在 main_content 中,它有一个回收器视图。
app_bar_main
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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"
android:layout_
android:layout_
tools:context=".activities.MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_
android:layout_
android:theme="@style/Theme.Manageio.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar_main_activity"
android:layout_
android:layout_
android:background="?attr/colorPrimary"
app:popupTheme="@style/Theme.Manageio.PopUpOverlay" />
</com.google.android.material.appbar.AppBarLayout>
<include layout="@layout/main_content" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab_createBoard"
android:layout_
android:layout_
android:layout_gravity="bottom|end"
android:layout_margin="24dp"
app:srcCompat="@drawable/ic_vector_add"
android:background="@color/primaryColor" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
main_content
<LinearLayout 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"
android:layout_
android:layout_
android:gravity="center"
android:background="@color/lightPrimaryColor"
android:orientation="vertical"
android:padding="16dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context=".activities.MainActivity">
<androidx.cardview.widget.CardView
android:layout_
android:layout_
android:background="@color/white"
android:elevation="5dp"
app:cardCornerRadius="10dp">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_boardList"
android:layout_
android:layout_
app:item_layout="@layout/item_board"
android:visibility="gone"
/>
<TextView
android:layout_
android:layout_
android:id="@+id/tv_noBoardsAvailable"
android:gravity="center"
android:textSize="32sp"
android:fontFamily="@font/boogaloo"
android:textColor="@color/secondaryText"
android:text="No Boards Available!"/>
</androidx.cardview.widget.CardView>
</LinearLayout>
现在,每当我点击 fab 时,它都会将我带到 CreateBoardActivity,它会创建板并将其存储在 firestore 中
CreateBoardActivity
import android.Manifest
import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.bumptech.glide.Glide
import com.example.manageio.R
import com.example.manageio.firebase.FirestoreClass
import com.example.manageio.models.Board
import com.example.manageio.utils.Constants
import com.google.firebase.storage.FirebaseStorage
import com.google.firebase.storage.StorageReference
import kotlinx.android.synthetic.main.activity_create_board.*
import kotlinx.android.synthetic.main.activity_profile.*
import java.io.IOException
class CreateBoardActivity : BaseActivity()
private var mSelectedImageFileUri : Uri?= null
private lateinit var mUserName : String
private var mBoardImageURL : String = ""
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_create_board)
if(intent.hasExtra(Constants.NAME))
mUserName = intent.getStringExtra(Constants.NAME).toString()
iv_boardImage.setOnClickListener
if (ContextCompat.checkSelfPermission(
this,
Manifest.permission.READ_EXTERNAL_STORAGE
) == PackageManager.PERMISSION_GRANTED
)
Constants.showImageChooser(this)
else
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
Constants.READ_STORAGE_PERMISSION_CODE
)
createBoard_createBtn.setOnClickListener
if(mSelectedImageFileUri!=null)
uploadBoardImage()
else
showProgressDialog(resources.getString(R.string.please_wait))
createBoard()
private fun createBoard()
val assignedUserArrayList : ArrayList<String> = ArrayList()
assignedUserArrayList.add(getCurrentUserId())
var board = Board(
createBoard_et_boardName.text.toString(),
mBoardImageURL,
mUserName,
assignedUserArrayList
)
FirestoreClass().createBoard(this,board)
private fun uploadBoardImage()
showProgressDialog(resources.getString(R.string.please_wait))
val sRef: StorageReference = FirebaseStorage.getInstance()
.reference.child(
"BOARD_IMAGE" + System.currentTimeMillis() + "." +
Constants.getFileExtension(this,mSelectedImageFileUri!!)
)
sRef.putFile(mSelectedImageFileUri!!).addOnSuccessListener taskSnapshot ->
Log.i(
"FirebaseBoardImageUrl",
taskSnapshot.metadata!!.reference!!.downloadUrl.toString()
)
taskSnapshot.metadata!!.reference!!.downloadUrl.addOnSuccessListener uri ->
Log.i("DownloadBoardImageUrl", uri.toString())
mBoardImageURL = uri.toString()
createBoard()
.addOnFailureListener exception ->
Toast.makeText(this@CreateBoardActivity, exception.message, Toast.LENGTH_LONG).show()
hideProgressDialog()
fun boardCreatedSuccessfully()
hideProgressDialog()
setResult(Activity.RESULT_OK)
finish()
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
)
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == Constants.READ_STORAGE_PERMISSION_CODE)
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)
Constants.showImageChooser(this)
else
Toast.makeText(
this,
"You denied the permission for storage.You can allow it from settings",
Toast.LENGTH_LONG
).show()
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?)
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK && requestCode == Constants.PICK_IMAGE_REQUEST_CODE && data!!.data != null)
mSelectedImageFileUri = data.data
try
Glide
.with(this)
.load(mSelectedImageFileUri)
.centerCrop()
.placeholder(R.color.lightPrimaryColor)
.into(iv_boardImage)
catch (e: IOException)
e.printStackTrace()
FireStoreClass
import android.app.Activity
import android.util.Log
import android.widget.Toast
import com.example.manageio.activities.*
import com.example.manageio.models.Board
import com.example.manageio.models.User
import com.example.manageio.utils.Constants
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.firestore.FirebaseFirestore
import com.google.firebase.firestore.SetOptions
class FirestoreClass
private val mFireStore = FirebaseFirestore.getInstance()
fun getBoardDetails(activity: TaskListActivity, documentId: String)
mFireStore.collection(Constants.BOARDS)
.document(documentId)
.get()
.addOnSuccessListener document ->
val board = document.toObject(Board::class.java)!!
board.documentId = document.id
activity.boardDetails(board)
.addOnFailureListener e ->
activity.hideProgressDialog()
Log.e("FirestoreClass", "Error with exception ", e)
fun createBoard(activity: CreateBoardActivity, board: Board)
mFireStore.collection(Constants.BOARDS)
.document()
.set(board, SetOptions.merge())
.addOnSuccessListener
Toast.makeText(activity, "Board Created Successfully", Toast.LENGTH_SHORT).show()
activity.boardCreatedSuccessfully()
.addOnFailureListener e ->
activity.hideProgressDialog()
Log.e("FirestoreClass", "Error with exception ", e)
fun getBoardsList(activity: MainActivity)
mFireStore.collection(Constants.BOARDS)
.whereArrayContains(Constants.ASSIGNED_TO, getCurrentUserId())
.get()
.addOnSuccessListener document ->
val boardlist: ArrayList<Board> = ArrayList()
for (i in document.documents)
val board = i.toObject(Board::class.java)!!
board.documentId = i.id
boardlist.add(board)
activity.populateBoardsListToUI(boardlist)
.addOnFailureListener e ->
activity.hideProgressDialog()
Log.e("FireStoreClass", "Error while creating board.", e)
fun updateUserProfileData(activity: Activity, userHashMap: HashMap<String, Any>)
mFireStore.collection(Constants.USERS)
.document(getCurrentUserId())
.update(userHashMap)
.addOnSuccessListener
Log.i("FirestoreClass", "Profile Data Updated Successfully")
Toast.makeText(
activity,
"You profile has been updated successfully",
Toast.LENGTH_SHORT
).show()
when(activity)
is MainActivity ->
activity.tokenUpdateSuccess()
is ProfileActivity ->
activity.profileUpdateSuccess()
.addOnFailureListener e ->
when(activity)
is MainActivity ->
activity.hideProgressDialog()
is ProfileActivity ->
activity.hideProgressDialog()
Log.e("FirestoreClass", "Error while updating profile", e)
Toast.makeText(activity, "Error while updating profile", Toast.LENGTH_SHORT).show()
fun loadUserData(activity: Activity, readBoardsList: Boolean = false)
mFireStore.collection(Constants.USERS)
.document(getCurrentUserId())
.get()
.addOnSuccessListener document ->
val loggedInUser = document.toObject(User::class.java)!!
when (activity)
is LoginActivity ->
activity.loginSuccess(loggedInUser)
is MainActivity ->
activity.updateNavigationUserDetails(loggedInUser, readBoardsList)
is ProfileActivity ->
activity.setUserDataInUi(loggedInUser)
.addOnFailureListener e ->
when (activity)
is LoginActivity ->
activity.hideProgressDialog()
is MainActivity ->
activity.hideProgressDialog()
Log.e("FirestoreClass", "Error with exception $e")
fun getCurrentUserId(): String
val currentUser = FirebaseAuth.getInstance().currentUser
var currentUserId = ""
if (currentUser != null)
currentUserId = currentUser.uid
return currentUserId
fun assignMemberToBoard(activity: MembersActivity, board : Board, user : User)
val assignedToHashmap = HashMap<String,Any>()
assignedToHashmap[Constants.ASSIGNED_TO] = board.assignedTo
mFireStore.collection(Constants.BOARDS)
.document(board.documentId)
.update(assignedToHashmap)
.addOnSuccessListener
activity.memberAssignSuccess(user)
.addOnFailureListener e ->
activity.hideProgressDialog()
Log.e("FirestoreClass", "Error while creating board",e)
主要活动
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import android.util.Log
import android.view.MenuItem
import android.view.View
import androidx.core.view.GravityCompat
import androidx.recyclerview.widget.LinearLayoutManager
import com.bumptech.glide.Glide
import com.example.manageio.R
import com.example.manageio.adapters.BoardItemsAdapter
import com.example.manageio.firebase.FirestoreClass
import com.example.manageio.models.Board
import com.example.manageio.models.User
import com.example.manageio.utils.Constants
import com.google.android.material.navigation.NavigationView
import com.google.firebase.FirebaseApp.getInstance
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.installations.FirebaseInstallations
import com.google.firebase.messaging.FirebaseMessaging
import com.google.firebase.messaging.FirebaseMessaging.getInstance
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.app_bar_main.*
import kotlinx.android.synthetic.main.main_content.*
import kotlinx.android.synthetic.main.nav_header_main.*
class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener
companion object
const val MY_PROFILE_REQUEST_CODE : Int = 99
const val CREATE_BOARD_REQUEST_CODE : Int =98
private lateinit var mUserName : String
private lateinit var mSharedPreferences : SharedPreferences
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setUpActionBar()
nav_view.setNavigationItemSelectedListener(this)
mSharedPreferences = this.getSharedPreferences(Constants.MANAGEIO_PREFERENCES, Context.MODE_PRIVATE)
val tokenUpdated = mSharedPreferences.getBoolean(Constants.FCM_TOKEN_UPDATED,false)
if(tokenUpdated)
showProgressDialog(resources.getString(R.string.please_wait))
FirestoreClass().loadUserData(this@MainActivity,true)
else
FirebaseInstallations.getInstance().getToken(tokenUpdated).addOnSuccessListener(this@MainActivity)
instanceIdResult ->
updateFCMTOKEN(instanceIdResult.token)
// TODO Check 1
// FirestoreClass().loadUserData(this , true)
fab_createBoard.setOnClickListener
val intent = Intent(this@MainActivity,CreateBoardActivity::class.java)
intent.putExtra(Constants.NAME,mUserName)
startActivityForResult(intent, CREATE_BOARD_REQUEST_CODE)
fun populateBoardsListToUI(boardsList : ArrayList<Board>)
hideProgressDialog()
if(boardsList.size > 0)
rv_boardList.visibility = View.VISIBLE
tv_noBoardsAvailable.visibility = View.GONE
rv_boardList.layoutManager = LinearLayoutManager(this)
rv_boardList.setHasFixedSize(true)
val adapter = BoardItemsAdapter(this,boardsList)
rv_boardList.adapter = adapter
adapter.setOnClickListener(object :BoardItemsAdapter.OnClickListener
override fun onClick(position: Int, model: Board)
val intent = Intent(this@MainActivity,TaskListActivity::class.java)
intent.putExtra(Constants.DOCUMENT_ID,model.documentId)
startActivity(intent)
)
else
rv_boardList.visibility = View.GONE
tv_noBoardsAvailable.visibility = View.VISIBLE
private fun setUpActionBar()
setSupportActionBar(toolbar_main_activity)
toolbar_main_activity.setNavigationIcon(R.drawable.ic_nav_menu)
toolbar_main_activity.setNavigationOnClickListener
toggleDrawer()
private fun toggleDrawer()
if (drawer_layout.isDrawerOpen(GravityCompat.START))
drawer_layout.closeDrawer(GravityCompat.START)
else
drawer_layout.openDrawer(GravityCompat.START)
fun updateNavigationUserDetails(user: User, readBoardsList : Boolean)
hideProgressDialog()
mUserName = user.name
Glide
.with(this)
.load(user.image)
.centerCrop()
.placeholder(R.drawable.ic_nav_user)
.into(iv_userImage);
tv_username.text = user.name
if(readBoardsList)
showProgressDialog(resources.getString(R.string.please_wait))
FirestoreClass().getBoardsList(this)
override fun onBackPressed()
if (drawer_layout.isDrawerOpen(GravityCompat.START))
drawer_layout.closeDrawer(GravityCompat.START)
else
doubleBackToExit()
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?)
super.onActivityResult(requestCode, resultCode, data)
if(resultCode == Activity.RESULT_OK && requestCode == MY_PROFILE_REQUEST_CODE)
FirestoreClass().loadUserData(this)
else if(resultCode == Activity.RESULT_OK && requestCode == CREATE_BOARD_REQUEST_CODE)
FirestoreClass().getBoardsList(this)
else
Log.e("MainActivity", "Cancelled")
override fun onNavigationItemSelected(item: MenuItem): Boolean
when(item.itemId)
R.id.nav_my_profile ->
startActivityForResult(Intent(this@MainActivity,ProfileActivity::class.java),
MY_PROFILE_REQUEST_CODE)
R.id.nav_logout ->
FirebaseAuth.getInstance().signOut()
mSharedPreferences.edit().clear().apply()
val intent = Intent(this,WelcomeActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
finish()
drawer_layout.closeDrawer(GravityCompat.START)
return true
**BoardItemsAdapter
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.example.manageio.R
import com.example.manageio.firebase.FirestoreClass
import com.example.manageio.models.Board
import com.example.manageio.utils.Constants
import kotlinx.android.synthetic.main.item_board.view.*
open class BoardItemsAdapter(private val context: Context,private var list: ArrayList<Board>) : RecyclerView.Adapter<RecyclerView.ViewHolder>()
private var onClickListener : OnClickListener ?= null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder
return MyViewHolder(LayoutInflater.from(context).inflate(R.layout.item_board,parent,false))
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int)
val model = list[position]
if(holder is MyViewHolder)
Glide
.with(context)
.load(model.image)
.centerCrop()
.placeholder(R.color.lightPrimaryColor)
.into(holder.itemView.iv_itemBoardImage)
holder.itemView.tv_name.text = model.name
holder.itemView.tv_createdBy.text = "Created By: $model.createdBy"
holder.itemView.setOnClickListener
if(onClickListener!=null)
onClickListener!!.onClick(position,model)
interface OnClickListener
fun onClick(position: Int,model : Board)
fun setOnClickListener(onClickListener: OnClickListener)
this.onClickListener = onClickListener
override fun getItemCount(): Int
return list.size
private class MyViewHolder(view : View) : RecyclerView.ViewHolder(view)
板子(模型)
包 com.example.manageio.models
导入 android.os.Parcel 导入android.os.Parcelable
data class Board(
val name : String = "",
val image : String = "",
val createdBy : String = "",
val assignedTo : ArrayList<String> = ArrayList(),
var documentId : String = "",
var taskList : ArrayList<Task> = ArrayList()
) : Parcelable
constructor(parcel: Parcel) : this(
parcel.readString()!!,
parcel.readString()!!,
parcel.readString()!!,
parcel.createStringArrayList()!!,
parcel.readString()!!,
parcel.createTypedArrayList(Task.CREATOR)!!
)
override fun writeToParcel(parcel: Parcel, flags: Int) = with(parcel)
parcel.writeString(name)
parcel.writeString(image)
parcel.writeString(createdBy)
parcel.writeStringList(assignedTo)
parcel.writeString(documentId)
parcel.writeTypedList(taskList)
override fun describeContents(): Int
return 0
companion object CREATOR : Parcelable.Creator<Board>
override fun createFromParcel(parcel: Parcel): Board
return Board(parcel)
override fun newArray(size: Int): Array<Board?>
return arrayOfNulls(size)
然后创建的板显示在主要活动中 请参考这些图片
Firestore Image
Main activity image
不是我想要的,每当我滑动一个回收站视图列表时,它就应该被删除。 如何实现它以使其也从 Firestore 中删除?
请不要介意其他不重要的功能。其实这是我第一次问所以我把它都贴在那里了
【问题讨论】:
检查文档以从 Firestore 中删除 firebase.google.com/docs/firestore/manage-data/… 如果您遇到问题,最好在发布问题时创建MCVE。您为此问题发布了近 700(七百)行行代码。人们需要解析和尝试在线调试的内容很多。请编辑您的问题并隔离问题,这样可以增加获得帮助的机会。 但是,我认为这篇文章 How to delete a record from Firestore on a RecylerView left/right swipe? 可能会有所帮助。 首先:欢迎来到 ***,我们很高兴你在这里。但我只能为 @AlexMamo 的评论 +1。 我已经简化了我的问题,请查看此***.com/questions/67938465/… 【参考方案1】:您需要一个 ItemTouchHelper 回调方法才能在 Android Recycler 视图中实现滑动删除。下面是这样的
abstract class SwipeToDeleteCallback internal constructor(internal var mContext: Context) : ItemTouchHelper.Callback()
private val mClearPaint: Paint
private val mBackground: ColorDrawable
private val backgroundColor: Int
private val deleteDrawable: Drawable?
private val intrinsicWidth: Int
private val intrinsicHeight: Int
init
mBackground = ColorDrawable()
backgroundColor = Color.parseColor("#b80f0a")
mClearPaint = Paint()
mClearPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
deleteDrawable = ContextCompat.getDrawable(mContext, R.drawable.ic_delete)
intrinsicWidth = deleteDrawable!!.intrinsicWidth
intrinsicHeight = deleteDrawable.intrinsicHeight
override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int
return ItemTouchHelper.Callback.makeMovementFlags(0, ItemTouchHelper.LEFT)
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, viewHolder1: RecyclerView.ViewHolder): Boolean
return false
override fun onChildDraw(
c: Canvas, recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, dX: Float,
dY: Float,
actionState: Int,
isCurrentlyActive: Boolean
)
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
val itemView = viewHolder.itemView
val itemHeight = itemView.height
val isCancelled = dX == 0f && !isCurrentlyActive
if (isCancelled)
clearCanvas(
c,
itemView.right + dX,
itemView.top + 0f,
itemView.right + 0f,
itemView.bottom + 0f
)
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
return
mBackground.color = backgroundColor
mBackground.setBounds(itemView.right + dX.toInt(), itemView.top, itemView.right, itemView.bottom)
mBackground.draw(c)
val deleteIconTop = itemView.top + (itemHeight - intrinsicHeight) / 2
val deleteIconMargin = (itemHeight - intrinsicHeight) / 2
val deleteIconLeft = itemView.right - deleteIconMargin - intrinsicWidth
val deleteIconRight = itemView.right - deleteIconMargin
val deleteIconBottom = deleteIconTop + intrinsicHeight
deleteDrawable!!.setBounds(deleteIconLeft, deleteIconTop, deleteIconRight, deleteIconBottom)
deleteDrawable.draw(c)
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
private fun clearCanvas(c: Canvas, left: Float, top: Float, right: Float, bottom: Float)
c.drawRect(left, top, right, bottom, mClearPaint)
override fun getSwipeThreshold(viewHolder: RecyclerView.ViewHolder): Float
return 0.7f
那你就可以这样使用了
recyclerView..addOnItemTouchListener(YourItemTouchHere)
实现 onMove 方法。在firestore上删除只是将firestore中对象路径的值设置为FieldValue.delete(),它将被删除
【讨论】:
以上是关于如何使用kotlin滑动删除存储在firestore中的recyclerview项目?的主要内容,如果未能解决你的问题,请参考以下文章
Firestore - 如何在 Kotlin 中排除数据类对象的字段
如何从firestore中删除文档时实时更新Recyclerview?
如何在 Kotlin 中将 Firestore 日期/时间戳转换为日期?
如何使用 kotlin 数据类获取 Firestore 文档的文档 ID