整理使用RecyclerView控件在长按删除事件中的用法(使用Kotlin)
一、前言:
在最近的学习中,使用Recyclerview控件时,遇到需要长按删除的场景,在测试过程中,遇到各种崩溃,在这里总结一下:
在本博客汇中,使用了一个统计学生信息的demo,并且用到了SQLite,这样保证长按删除的同时数据库也能同步数据,先上一下长按删除的效果图:
二、代码分析:
长按删除item最重要的是要确保数据库的同步进行。
1.Student类代码:
class Student(val name: String, val gender: String, val age: Int) {
}
2.数据库代码:
package com.example.recyclerlongclickremove
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import android.widget.Toast
class StudentBaseHelper(val context: Context, val name: String, val versin: Int) :
SQLiteOpenHelper(context, name, null, versin) {
private val createStockData = "create table $name (" +
"id integer primary key autoincrement, " +
"studentName text," +
"gender text," +
"age INTEGER)"
/* 创建数据库 */
override fun onCreate(db: SQLiteDatabase) {
db.execSQL(createStockData)
Toast.makeText(context, "Create $name succeeded", Toast.LENGTH_LONG).show()
}
/* 升级数据库 */
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
Toast.makeText(context, "upgrade $name succeeded", Toast.LENGTH_LONG).show()
db.execSQL("drop table if exists $name")
onCreate(db)
}
}
这里面只实现了两个必须要覆写的函数;
3.数据库操作的封装代码:
package com.example.recyclerlongclickremove
import android.content.ContentValues
import android.content.Context
class StudentBaseControl(val context: Context, val name: String, val version: Int) {
private var dbHelper = StudentBaseHelper(context, name, version)
/* 创建数据库 */
fun create(){
dbHelper.writableDatabase
}
/* 添加数据 */
fun addData(student: Student){
val db = dbHelper.writableDatabase
val value = ContentValues().apply {
put("studentName", student.name)
put("gender", student.gender)
put("age", student.age)
}
db.insert(name, null, value)
}
/*
* 遍历全局数据库
*/
fun queryAllData(name: String) : ArrayList<Student>{
val db = dbHelper.writableDatabase
val dataList = ArrayList<Student>()
/* 全局搜索 */
val cursor = db.query(name, null,
null, null, null,
null, null, null)
/* 遍历数据库 */
if (cursor.moveToFirst()){
do {
val name = cursor.getString(cursor.getColumnIndex("studentName"))
val gender = cursor.getString(cursor.getColumnIndex("gender"))
val age = cursor.getString(cursor.getColumnIndex("age")).toInt()
dataList.add(Student(name, gender, age))
} while (cursor.moveToNext())
cursor.close()
return dataList
}
cursor.close()
/* 如果未遍历到目标数据,则返回null */
return dataList
}
/* 删除数据:根据学生姓名进行删除 */
fun deleteData(studentName: String){
val db = dbHelper.writableDatabase
db.delete(name, "studentName = ?", arrayOf(studentName))
}
}
create方法,创建数据库,直接操作SQLite;
addData方法,往数据库中添加item;
queryAllData方法,遍历数据库,这个方法的作用主要是用在Activity启动的时候检测是否创建了数据库;
deleteData方法,删除数据库中的数据;
4.RecyclerView的适配器:
package com.example.recyclerlongclickremove
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView
class StudentsAdapter(list: ArrayList<Student>):
RecyclerView.Adapter <StudentsAdapter.ViewHolder>(){
private var studentList = ArrayList<Student>()
init {
studentList = list
}
/* 用于获取最外层布局的及控件的实例 */
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view){
val name : TextView = view.findViewById(R.id.name)
val gender : TextView = view.findViewById(R.id.gender)
val age : TextView = view.findViewById(R.id.age)
}
/* 加载student布局 */
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.student, parent, false)
val viewHolder = ViewHolder(view)
/* 最外层布局点击事件 */
viewHolder.itemView.setOnClickListener {
Toast.makeText(parent.context, "you click outer item!",
Toast.LENGTH_SHORT).show()
}
/**
* 长按监听:删除item
*/
viewHolder.itemView.setOnLongClickListener {
val position = viewHolder.adapterPosition
/* 将长按item对应的学生姓名发送至MainActivity */
Log.d("jiyi", "adapter remove:${studentList[position].name}")
MainActivity.mainActivityTodo(
MainActivity.HANDLELONGCLIECK,
studentList[position].name)
/* 在ArrayList中移除此股 */
studentList.remove(studentList[position])
/* 通知移除该item */
notifyItemRemoved(position)
/* 通知调制ArrayList顺序(此句删除也无影响) */
notifyItemRangeChanged(position, studentList.size)
false
}
return viewHolder //注意这里要返回viewHolder,因为有各种点击事件
}
/* 对RecyclerView滚入屏幕的子项数据赋值 */
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val student = studentList[position]
holder.name.text = student.name
holder.gender.text = student.gender
holder.age.text = student.age.toString()
}
/* 返回数据源长度 */
override fun getItemCount() = studentList.size
}
adapter的基本操作就不说了,我们主要看其中的长按监听删除item事件:
我们能将数据库中的数据显示到控件上,是使用了ArrayList的,而数据又是从数据库中来的,因此,在删除事件的处理上,这两个地方都要进行各自数据的删除,Arraylist的删除操作就由adapter来完成,数据库的删除操作由MainActivity来完成,所以在代码中可以看到,这里借助了MainActivity提供的静态方法,将需要删除的学生名字给传递过去,当然,这里是为了方便,学生名字都没有重复,在实际开发中,你肯定有自己的数据删选准则,删除ArrayList就只需要调用remove就好了,notifyItemRemoved方法则是为了通知adapter,进行UI上的刷新,代码中还有一句notifyItemRangeChanged,我实测中发现这句有没有并没有什么区别,但网上有人说需要这句,所以我还是保留了,总的来说,adapter的主要操作是删除ArrayList中的数据,剩下的就交给MainActivity了;
5.MainAcrtivity的实现:
package com.example.recyclerlongclickremove
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.android.synthetic.main.activity_main.*
import kotlin.concurrent.thread
/** 长按删除Flag */
internal var onLongClickFlag = false
/** 长按删除的股票代码 */
internal var removeStudentName = ""
class MainActivity : AppCompatActivity() {
private var threadRun = true
private var studentList = ArrayList<Student>()
val databaseStudent = StudentBaseControl(this, "Student", 1)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
/*
* 将当前活动赋值给静态对象
*/
mainActivity = this
/*
* 如果数据库不存在:创建 + 添加初始数据
*/
if (databaseStudent.queryAllData("Student").size == 0){
/* 创建数据库 */
databaseStudent.create()
/*
* 初始化student数据
*/
initStudent()
/* 添加数据 */
for (i in 0 until (studentList.size)){
databaseStudent.addData(studentList.get(index = i))
}
}
}
override fun onResume() {
super.onResume()
/*
* 重新填充studentList数据,避免从其他activity回来之后数据有改变
*/
studentList.clear()
studentList = databaseStudent.queryAllData("Student")
/* 获取layoutManager */
val layoutManager = LinearLayoutManager(this)
recyclerView.layoutManager = layoutManager
/* 获取适配器 */
val adapter = StudentsAdapter(studentList)
recyclerView.adapter = adapter
thread {
while (threadRun){
if (onLongClickFlag){
if (removeStudentName != ""){
/* 删除数据库中的对应数据,注意,这里不需要删除ArrayList里面的了,因为adapter中已经删除了 */
databaseStudent.deleteData(removeStudentName)
}
onLongClickFlag = false
removeStudentName = ""
Thread.sleep(200)
}
}
}
}
/* 初始化StudentList */
private fun initStudent(){
repeat(1){
studentList.add(Student("张三", "男", 27))
studentList.add(Student("李四", "女", 28))
studentList.add(Student("王五", "男", 29))
studentList.add(Student("赵六", "女", 30))
studentList.add(Student("郭七", "男", 31))
}
}
/**
* MainActivity的单实例,用于供外部类调用的static方法等
*/
companion object{
const val HANDLELONGCLIECK = "handlelongclick"
lateinit var mainActivity : AppCompatActivity //静态对象,用于适配器调用activity的相关操作
/**
* mainActivityTodo由外部类回调MainActivity操作
* event:需要执行的操作
* stockCode:响应控件对应的股票代码
*/
@JvmStatic
fun mainActivityTodo(event: String, studentName: String){
when (event){
/* 处理长按删除item事件 */
HANDLELONGCLIECK -> {
onLongClickFlag = true
removeStudentName = studentName
Log.d("MainActivity", "remove student:$studentName")
}
}
}
}
}
为了方面监测用户是否是删除了item,在MainActivity中我跑了一个线程,如果确认有长按删除事件触发时,onLongClickFlag这个布尔值会置为true,那么,只需要这时调用deleteData删除数据库中的数据就可以了;
三、总结:
RecyclerView的长按删除事件调用setOnLongClickListener就可以了,只不过在实际中,一定要区别处理ArrayList和SQLite中的操作,不然很容易出现各种崩溃的问题。
上一篇: 排序
下一篇: mongodb多层嵌套数组查询