1

I've made a list which gets items from a Room-database using LiveData. This liveData<List> is then bound to a recyclerView, using a BindingAdapter. The lists adapter is listAdapter, not `RecyclerView.Adapter.

I need help holding onto the scroll-state or somehow returning to the scroll-index I was at before the recyclerView reloaded:

//In ViewModel
    val movieList = moviesRepository.movies

..

//in Repo
  val movies: LiveData<List<Movie>> =
    Transformations.map(database.movieDao.getMovies()) {
        it.asDomainModel()
    }

Every time the DB updates, the recyclerView shoots back up to the top.

And here's the bindingAdapter for the RecyclerView and the list.

@BindingAdapter("listData")
fun bindRecyclerView(recyclerView: RecyclerView, data: List<Movie>?) {
    val adapter = recyclerView.adapter as MovieListAdapter
    //Log.d("listData binding", "${data}")
    adapter.submitList(data)
}

I think I need to use something like recyclerView.smoothScrollToPosition(la.getItemCount()); after the update has occured, but I don't know how to automatically call it when the update has occured

Project Repo

Tomk07
  • 113
  • 2
  • 12

2 Answers2

1
  1. First of all don't use ListAdapter RecyclerView has a more optimized adapter here

  2. in your adapter provide a function that overrides the item list and there is where you notify the data change

  3. Use smoothScrollToPosition(lastVisiblePosition) to scroll to the last visible position where lastVisiblePosition = layoutManager.findFirstVisibleItemPosition()

  4. Update lastVisiblePosition before you push new items to the adapter notifyDatasetChanged()

Step 2

fun updateList(newItems:List<Movie>) {
    moviesList.addAll(newItems)
    lastVisiblePosition = layoutManager.findFirstVisibleItemPosition()
    notifyDataSetChanged()
}

from your view when you call adapter.updateList(newItems) just call recyclerView.smoothScrollToPosition(adapter.lastVisiblePosition)

AouledIssa
  • 2,528
  • 2
  • 22
  • 39
  • thanks for the reply, any chance you could go into a bit more depth on step 2? Not sure what you mean overriding the 'item list'. perhaps an example if you have the time – Tomk07 May 31 '20 at 22:16
  • Thanks, I may implement this solution in the future, but I managed to find a solution which doesn't require me to completely re-jig my Adapter, which would be a bit of a diversion. Thanks again :) – Tomk07 Jun 04 '20 at 15:59
  • OP is using the newer JetPack ListAdapter that is a sublass of RecycleView.Adapter https://developer.android.com/reference/androidx/recyclerview/widget/ListAdapter There is no need to replace it. It is the newer recommended approach. – Paul Hiles Jun 18 '20 at 15:53
0

figured it out. All of the examples I could find were using Activities, not fragments. So all I had to do was pop this into the onCreateView() function in the relevant fragment.kt file.

    binding.movieList.layoutManager =
        object : LinearLayoutManager(getActivity(), VERTICAL, false) {
            override fun onLayoutCompleted(state: RecyclerView.State) {
                super.onLayoutCompleted(state)
                val lastVisibleItemPosition = findLastVisibleItemPosition()
                val count = (binding.movieList.adapter as MovieListAdapter).itemCount

                    //speed the scroll up a bit, but make it look smooth at the end
                    binding.movieList.scrollToPosition(count - 5)
                    binding.movieList.smoothScrollToPosition(count)

            }
        }

here, the binding.movieList is referring to this xml element

 <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/movie_list"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        app:layout_constraintBottom_toTopOf="@+id/load_more_button"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:listData="@{viewModel.movieList}"
        tools:listitem="@layout/movie_list_item" />
Tomk07
  • 113
  • 2
  • 12
  • 1
    Your solution will work but it won't be scalable. Better to use the Apis when they exist and don't re-invent the wheel! Happy coding – AouledIssa Jun 05 '20 at 07:54