RecyclerView直到被刷过才更新

问题描述 投票:0回答:1

我正在电影应用程序上工作,我想在其中加载电影并将其另存为本地数据库中的缓存。我遵循了Udacity的“使用Kotlin构建Andoid应用程序”课程“火星房地产”的示例。我的应用程序正在使用实时数据,MVVM,数据绑定,房间和改造。每次加载新数据时,它们都会保存在db中,并且是“真相”的唯一来源。

这是主要布局:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable
            name="viewModel"
            type="com.example.moviesapp.presentation.main.MainViewModel"/>
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.example.moviesapp.presentation.MainActivity">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/movies_grid"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:padding="6dp"
            android:clipToPadding="false"
            android:background="@android:color/black"
            app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:listData="@{viewModel.movies}"
            app:spanCount="3"
            tools:itemCount="16"
            tools:listitem="@layout/grid_view_item"/>

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

片段:

class MainFragment : DaggerFragment(), SharedPreferences.OnSharedPreferenceChangeListener {

    /**
    //     * Lazily initialize [MainViewModel].
    //     */
    private val viewModel: MainViewModel by lazy {
        ViewModelProviders.of(this, providerFactory).get(MainViewModel::class.java)
    }

    @Inject
    lateinit var providerFactory: ViewModelProviderFactory

    @Inject
    lateinit var sharedPreferences: SharedPreferences

    override fun onResume() {
        super.onResume()
        sharedPreferences.registerOnSharedPreferenceChangeListener(this)
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View {

        val binding = FragmentMainBinding.inflate(inflater)
        binding.lifecycleOwner = this
        binding.viewModel = viewModel
        binding.moviesGrid.adapter = MainAdapter(MainAdapter.OnClickListener {
            viewModel.displayMovieDetails(it)
        })

        viewModel.navigateToSelectedMovie.observe(this, Observer {
            if (null != it) {
                this.findNavController().navigate(MainFragmentDirections.actionShowDetail(it.id))
                viewModel.displayMovieDetailsComplete()
            }
        })

        setHasOptionsMenu(true)
        return binding.root
    }

    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
        inflater.inflate(R.menu.main, menu)
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        return when (item.itemId) {
            R.id.action_settings -> {
                this.findNavController().navigate(
                    MainFragmentDirections.actionShowSettings()
                )
                true
            }
            R.id.action_search -> {
                this.findNavController().navigate(
                    MainFragmentDirections.actionMainFragmentToSearchFragment()
                )
                true
            }
            R.id.action_back -> {
                viewModel.paginateBack()
                true
            }
            R.id.action_forward -> {
                viewModel.paginateForward()
                true
            }
            else -> super.onOptionsItemSelected(item)
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        sharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
    }

    override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
        viewModel.onPreferencesChanged()
    }
}

查看模型:

class MainViewModel @Inject constructor(application: Application, private val getMoviesUseCase: GetMoviesUseCase) :
    AndroidViewModel(application) {

    private var currentPage = 1

    private val totalPages: Int by lazy {
        with (PreferenceManager.getDefaultSharedPreferences(application)) {
            getInt(TOTAL_PAGES, 0)
        }
    }

    // get movies saved in local db
    val movies = getMoviesUseCase.allMovies()

    init {
        loadMovies()
    }

    private fun loadMovies() {
        if (isInternetAvailable(getApplication()))
            viewModelScope.launch {
                getMoviesUseCase.refreshMovies(currentPage)
            }
    }

    private val _navigateToSelectedMovie = MutableLiveData<Movie>()
    val navigateToSelectedMovie: LiveData<Movie>
        get() = _navigateToSelectedMovie

    fun displayMovieDetails(movie: Movie?) {
        if (movie != null) _navigateToSelectedMovie.value = movie
    }

    fun displayMovieDetailsComplete() {
        _navigateToSelectedMovie.value = null
    }

适配器:

class MainAdapter(private val onClickListener: OnClickListener) :
    ListAdapter<Movie, MainAdapter.MovieViewHolder>(DiffCallback) {

    class MovieViewHolder(private var binding: GridViewItemBinding):
            RecyclerView.ViewHolder(binding.root ) {
        fun bind(movie: Movie) {
            binding.movie = movie
            binding.executePendingBindings()
        }
    }

    companion object DiffCallback : DiffUtil.ItemCallback<Movie>() {
        override fun areItemsTheSame(oldItem: Movie, newItem: Movie): Boolean {
            return oldItem === newItem
        }

        override fun areContentsTheSame(oldItem: Movie, newItem: Movie): Boolean {
            return oldItem.id == newItem.id
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MovieViewHolder {
        return MovieViewHolder(
            GridViewItemBinding.inflate(
                LayoutInflater.from(parent.context)
            )
        )
    }

    override fun onBindViewHolder(holder: MovieViewHolder, position: Int) {
        val movie = getItem(position)
        holder.itemView.setOnClickListener {
            onClickListener.onClick(movie)
        }
        holder.bind(movie)
    }

    class OnClickListener(val clickListener: (movie: Movie) -> Unit) {
        fun onClick(movie: Movie) = clickListener(movie)
    }
}

数据绑定适配器:

@BindingAdapter("listData")
fun bindRecyclerView(recyclerView: RecyclerView, data: List<Movie>?) {
    val adapter = recyclerView.adapter as MainAdapter
    adapter.submitList(data)
}

@BindingAdapter("posterImageUrl")
fun bindPosterImage(imgView: ImageView, imgUrl: String?) {
    imgUrl?.let {
        val imgUri = (IMAGE_BASE_URL + POSTER_SIZE + imgUrl).toUri().buildUpon().build()
        Glide.with(imgView.context)
            .load(imgUri)
            .apply(RequestOptions().placeholder(R.drawable.loading_animation).error(R.drawable.ic_broken_image))
            .into(imgView)
    }
}

我现在在View模型中显示新的实时数据,但是Recyclerview直到刷新后才会更新。 viewmodel实例通过数据绑定传递到recyclerview。我现在在片段中称实时数据为Observ方法,但在此示例中不知道它是如何完成的。在火星房地产示例中,它似乎正在工作。我找不到代码的区别。

谢谢,护甲

android android-recyclerview android-room android-databinding android-livedata
1个回答
0
投票
// in view model, is the type of movies LiveData<List<Movie>> ?
val movies = getMoviesUseCase.allMovies()

如果从getMoviesUseCase.allMovies()返回的电影类型为LivaData<List<Movie>>类型,则观察者(Recyclerview)可以接收更改。

如果电影的类型是List<Movie>,则观察者无法接收更改,因为没有人通知观察者。

如果电影的类型为List<Movie>,请将其更改为LiveData<List<Movie>>

private val _movies = MutableLiveData<List<Movie>>().apply { value = emptyList() }
val movies: LiveData<List<Movie>> = _movies

并尝试像这样初始化_movies:

viewModelScope.launch {
            _movies.value = getMoviesUseCase.allMovies()
        }
© www.soinside.com 2019 - 2024. All rights reserved.