You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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}")
        }
    }

}

问题分析与解决方法

核心问题

  1. MovieManager中更新代码被注释:addMovie()里更新HomeAdapter的逻辑被注释,导致添加数据后未通知适配器刷新。
  2. 适配器实例可能不一致:MovieAdapter持有的homeAdapter如果不是HomeFragment正在使用的实例,调用更新方法也无效。
  3. 未重新获取最新数据库数据:即使调用更新,若未从数据库重新查询最新列表,适配器仍使用旧数据。

具体修复步骤

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 10:15:54