Android添加数据后RecyclerView无法实时刷新问题求助
影视收藏清单应用RecyclerView实时刷新问题
我在Android Studio开发一款影视收藏应用,包含两个标签页:
- Movies标签页:搜索影视并添加到数据库
- Home标签页:展示数据库中的收藏清单
当前问题:添加影视到数据库后,Home标签页的RecyclerView无法实时刷新,必须重启应用才能看到新增内容。我已在updateDataHome()中实现notifyDataSetChanged(),但无效。
相关代码片段
负责数据库操作的MovieManager类
class MovieManager(private val context: Context) { private val dbHelper = DataBase.DatabaseHelper(context) fun addMovie(movie: MovieDB, homeAdapter: HomeAdapter) { val db = dbHelper.writableDatabase println("CONTEXT:$context") val values = ContentValues().apply { put(DataBase.CollectionEntry.COLUMN_TITLE, movie.title) put(DataBase.CollectionEntry.COLUMN_ID, movie.id) put(DataBase.CollectionEntry.COLUMN_ADDED, movie.addedDate) put(DataBase.CollectionEntry.COLUMN_ORIGIN, movie.origin) put(DataBase.CollectionEntry.COLUMN_TYPE, movie.type) put(DataBase.CollectionEntry.COLUMN_USER, movie.user) put(DataBase.CollectionEntry.COLUMN_POSTER_PATH, movie.posterPath) } db.insert(DataBase.CollectionEntry.TABLE_NAME, null, values) dbHelper.close() // Mettez à jour l'adaptateur Home après l'ajout du film //val updatedMovies = getMovies() //homeAdapter.updateDataHome(updatedMovies) //println("Updated movies:$updatedMovies") } fun clearDatabase() { val db = dbHelper.writableDatabase db.execSQL("DELETE FROM ${DataBase.CollectionEntry.TABLE_NAME}") dbHelper.close() } }
含添加按钮逻辑的MovieAdapter类
override fun onBindViewHolder(holder: ViewHolder, position: Int) { val result = results[position] val dimX = 250 val dimY = 350 // Utilisez Picasso pour charger l'image à partir de l'URL du poster path Picasso.get().load("https://image.tmdb.org/t/p/original/${result.poster_path}") //.placeholder(R.drawable.sad_face) .error(R.drawable.not_found6) .resize(dimX, dimY) .centerCrop() .into(holder.posterImageView) runOnUiThread{ holder.addButton.setOnClickListener { val id: Int = result.id.toInt() val title: String = when { result.title != null -> result.title result.name != null -> result.name else -> "Unknown title" } // Date d'ajout, origine, type et utilisateur (à adapter selon vos besoins) val addedDate: String = getCurrentDate() val origin: String = "API Source" // Remplacez par la source réelle de l'API val type: String = result.media_type ?: "Unknown type" val user: String = "Current User" // Remplacez par la logique pour obtenir l'utilisateur actuel val posterPath: String? = result.poster_path val movie = MovieDB(title, id, addedDate, origin, type, user, posterPath) // Ajoutez le film à la base de données movieManager.addMovie(movie, homeAdapter) //homeAdapter.updateDataHome(movieManager.getMovies()) //notifyDataSetChanged() } } } fun runOnUiThread(action: () -> Unit) { val handler = Handler(Looper.getMainLooper()) handler.post { action.invoke() } }
含updateDataHome()方法的HomeAdapter类
package fr.cccq.mymultimedialist.adapter import android.os.Handler import android.os.Looper import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView import androidx.recyclerview.widget.RecyclerView import com.squareup.picasso.Picasso import fr.cccq.mymultimedialist.R import fr.cccq.mymultimedialist.database.MovieManager import fr.cccq.mymultimedialist.model.MovieDB class HomeAdapter(private val movieManager: MovieManager) : RecyclerView.Adapter<HomeAdapter.ViewHolder>() { private var movies: List<MovieDB> = emptyList() class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { // Initialisez les vues ici val posterImageView: ImageView = itemView.findViewById(R.id.posterImageViewHome) val titleTextView: TextView = itemView.findViewById(R.id.titleTextViewHome) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { // Créez et retournez le ViewHolder ici val view = LayoutInflater.from(parent.context).inflate(R.layout.database_result, parent, false) return ViewHolder(view) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { // Associez les données aux vues ici val movie = movies[position] // Utilisez 'holder' pour accéder aux vues et associer les données du film Picasso.get().load("https://image.tmdb.org/t/p/original/${movie.posterPath}") .resize(250, 360) .centerCrop() .into(holder.posterImageView) holder.titleTextView.text = movie.title } override fun getItemCount(): Int { return movies.size } fun updateDataHome(newMovies: List<MovieDB>) { runOnUiThread { movies = newMovies notifyDataSetChanged() } } fun runOnUiThread(action: () -> Unit) { val handler = Handler(Looper.getMainLooper()) handler.post { action.invoke() } } }
包含RecyclerView的HomeFragment类
package fr.cccq.mymultimedialist.fragments import android.os.Bundle import android.os.Handler import android.os.Looper import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import fr.cccq.mymultimedialist.R import fr.cccq.mymultimedialist.adapter.HomeAdapter import fr.cccq.mymultimedialist.database.MovieManager class HomeFragment : Fragment() { private lateinit var recyclerViewHome: RecyclerView private lateinit var adapterHome: HomeAdapter private lateinit var movieManager: MovieManager override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val view = inflater.inflate(R.layout.fragment_home, container, false) recyclerViewHome = view.findViewById(R.id.recyclerViewHome) return view } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // Initialisez le gestionnaire de base de données movieManager = MovieManager(requireContext()) // Initialisez l'adaptateur pour l'onglet Home adapterHome = HomeAdapter(movieManager) // Initialisez le RecyclerView recyclerViewHome.layoutManager = LinearLayoutManager(requireContext()) recyclerViewHome.adapter = adapterHome // Chargez les films à partir de la base de données val moviesFromDatabase = movieManager.getMovies() // Mettez à jour l'adaptateur avec les films de la base de données runOnUiThread { adapterHome.updateDataHome(moviesFromDatabase) adapterHome.notifyDataSetChanged() } // Affichez les films dans la console displayMoviesFromDatabase() //movieManager.clearDatabase() } fun runOnUiThread(action: () -> Unit) { val handler = Handler(Looper.getMainLooper()) handler.post { action.invoke() } } private fun displayMoviesFromDatabase() { val movies = movieManager.getMovies() for (movie in movies) { println("Movie from Database: ${movie.title}, ID: ${movie.id}, Added Date: ${movie.addedDate}, Origin: ${movie.origin}, Type: ${movie.type}, User: ${movie.user}") } } }
问题分析与解决方法
核心问题
- MovieManager中更新代码被注释:
addMovie()里更新HomeAdapter的逻辑被注释,导致添加数据后未通知适配器刷新。 - 适配器实例可能不一致:MovieAdapter持有的
homeAdapter如果不是HomeFragment正在使用的实例,调用更新方法也无效。 - 未重新获取最新数据库数据:即使调用更新,若未从数据库重新查询最新列表,适配器仍使用旧数据。
具体修复步骤
1. 恢复MovieManager的addMovie更新逻辑
取消注释addMovie()中的适配器更新代码,确保添加数据后立即查询最新数据并通知:
fun addMovie(movie: MovieDB, homeAdapter: HomeAdapter) { val db = dbHelper.writableDatabase println("CONTEXT:$context") val values = ContentValues().apply { put(DataBase.CollectionEntry.COLUMN_TITLE, movie.title) put(DataBase.CollectionEntry.COLUMN_ID, movie.id) put(DataBase.CollectionEntry.COLUMN_ADDED, movie.addedDate) put(DataBase.CollectionEntry.COLUMN_ORIGIN, movie.origin) put(DataBase.CollectionEntry.COLUMN_TYPE, movie.type) put(DataBase.CollectionEntry.COLUMN_USER, movie.user) put(DataBase.CollectionEntry.COLUMN_POSTER_PATH, movie.posterPath) } db.insert(DataBase.CollectionEntry.TABLE_NAME, null, values) dbHelper.close() // 恢复更新适配器的代码 val updatedMovies = getMovies() homeAdapter.updateDataHome(updatedMovies) println("Updated movies:$updatedMovies") }
注意:确保getMovies()方法能正确查询数据库所有数据
2. 优化HomeAdapter的updateDataHome方法
当前方法已在主线程执行,可简化逻辑(若调用方确保在主线程,可去掉内部的runOnUiThread):
fun updateDataHome(newMovies: List<MovieDB>) { movies = newMovies notifyDataSetChanged() }
3. 确保适配器实例一致(推荐用接口解耦)
直接传递适配器容易出现实例不一致问题,建议用接口回调让HomeFragment自行更新:
// 定义回调接口 interface OnMovieAddedListener { fun onMovieAdded() } // HomeFragment实现接口 class HomeFragment : Fragment(), OnMovieAddedListener { // ... 原有代码 ... override fun onMovieAdded() { val updatedMovies = movieManager.getMovies() adapterHome.updateDataHome(updatedMovies) } } // MovieAdapter持有接口实例 class MovieAdapter(private val listener: OnMovieAddedListener) : RecyclerView.Adapter<...>() { // ... 原有代码 ... holder.addButton.setOnClickListener { // ... 创建movie对象 ... movieManager.addMovie(movie) listener.onMovieAdded() } }
4. 检查getMovies()方法正确性
确保MovieManager的getMovies()能正确读取所有数据,示例实现:
fun getMovies(): List<MovieDB> { val movies = mutableListOf<MovieDB>() val db = dbHelper.readableDatabase val projection = arrayOf( DataBase.CollectionEntry.COLUMN_TITLE, DataBase.CollectionEntry.COLUMN_ID, DataBase.CollectionEntry.COLUMN_ADDED, DataBase.CollectionEntry.COLUMN_ORIGIN, DataBase.CollectionEntry.COLUMN_TYPE, DataBase.CollectionEntry.COLUMN_USER, DataBase.CollectionEntry.COLUMN_POSTER_PATH ) val cursor = db.query( DataBase.CollectionEntry.TABLE_NAME, projection, null, null, null, null, null ) with(cursor) { while (moveToNext()) { val title = getString(getColumnIndexOrThrow(DataBase.CollectionEntry.COLUMN_TITLE)) val id = getInt(getColumnIndexOrThrow(DataBase.CollectionEntry.COLUMN_ID)) val addedDate = getString(getColumnIndexOrThrow(DataBase.CollectionEntry.COLUMN_ADDED)) val origin = getString(getColumnIndexOrThrow(DataBase.CollectionEntry.COLUMN_ORIGIN)) val type = getString(getColumnIndexOrThrow(DataBase.CollectionEntry.COLUMN_TYPE)) val user = getString(getColumnIndexOrThrow(DataBase.CollectionEntry.COLUMN_USER)) val posterPath = getString(getColumnIndexOrThrow(DataBase.CollectionEntry.COLUMN_POSTER_PATH)) movies.add(MovieDB(title, id, addedDate, origin, type, user, posterPath)) } } cursor.close() dbHelper.close() return movies }
额外优化建议
- 避免频繁关闭数据库连接:用
try-finally统一管理连接,减少性能损耗。 - 改用Room数据库+LiveData:自动监听数据变化,无需手动调用刷新,实现更优雅。
- 分层架构:数据库操作放在Repository层,适配器仅负责UI展示和事件传递。
内容的提问来源于stack exchange,提问作者Marino33
相关产品推荐
相关产品推荐

