Kotlin中如何用Retrofit实现RecyclerView加载更多且保留旧数据?
Hey there! I see exactly what's going wrong with your infinite scroll setup—let's get that fixed so your new movie items stack after the old ones instead of replacing them.
The Root Problem
Right now, every time you call getPopularMovies() for a new page, you're creating a brand new MovieAdapter instance and passing only the latest page's results to it. Then you reassign this new adapter to your RecyclerView, which wipes out all the old data and replaces it with the new page. That's why your old items disappear!
Step-by-Step Solution
1. Initialize Your Adapter Once
First, set up your MovieAdapter as a member variable in your Fragment, and initialize it once when the view is created (instead of every API call). This keeps your adapter instance consistent:
private lateinit var moviesAdapter: MovieAdapter override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // Initialize adapter with an empty list first moviesAdapter = MovieAdapter(movies = emptyList()) moviesList.adapter = moviesAdapter // Load the first page to kick things off getPopularMovies(1) }
2. Add a Data Append Method to Your Adapter
Modify your MovieAdapter to include a method that adds new movies to the existing list, instead of replacing it. This lets you update the data without creating a new adapter:
class MovieAdapter(private var movies: List<Movie>) : RecyclerView.Adapter<MovieAdapter.MovieViewHolder>() { // ... Keep your existing ViewHolder, onCreateViewHolder, and onBindViewHolder code ... // Method to append new movies to the list fun addNewMovies(newMovies: List<Movie>) { // Combine existing list with new data val updatedList = movies + newMovies movies = updatedList // Notify RecyclerView only about the new items (more efficient than notifyDataSetChanged()) notifyItemRangeInserted( startPosition = movies.size - newMovies.size, itemCount = newMovies.size ) } // Optional: Add a method to replace the list (for the first page) fun setInitialMovies(initialMovies: List<Movie>) { movies = initialMovies notifyDataSetChanged() } }
3. Update Your API Call to Append Data
Now adjust your getPopularMovies() function to either set the initial data (for page 1) or append new data (for subsequent pages), instead of creating a new adapter every time:
private fun getPopularMovies(page: Int){ val repository = MoviesRepositoryProvider.provideMovieRepository() repository.getPopularMovies(page) .observeOn(AndroidSchedulers.mainThread()) .subscribeOn(Schedulers.io()) .subscribe ({ result -> if (page == 1) { // First page: Replace the empty list with initial data moviesAdapter.setInitialMovies(result.results) } else { // Subsequent pages: Append new movies to the existing list moviesAdapter.addNewMovies(result.results) } }, { error -> error.printStackTrace() }) }
Bonus Tip: Prevent Duplicate Requests
To avoid accidentally loading the same page multiple times (e.g., if the user scrolls quickly), add a boolean flag to track if a request is already in progress:
private var isLoading = false private fun getPopularMovies(page: Int){ if (isLoading) return // Exit if a request is already running isLoading = true val repository = MoviesRepositoryProvider.provideMovieRepository() repository.getPopularMovies(page) .observeOn(AndroidSchedulers.mainThread()) .subscribeOn(Schedulers.io()) .subscribe ({ result -> // ... Your existing data update code ... isLoading = false // Reset flag after successful load }, { error -> error.printStackTrace() isLoading = false // Reset flag even if request fails }) }
That should do it! Now when you scroll to the bottom, new movie items will be added to the end of your list instead of replacing the old ones.
内容的提问来源于stack exchange,提问作者Endyrmion

