Android 向下滚动的RecyclerView跳回顶部当LiveData更新其内容时,它应该保持其当前的Y偏移量
我有一个可使用复选框选择的语言列表的RecyclerView。它使用LiveData,因此当用户从另一个客户端选择或取消选择语言时,更改会自动反映在所有客户端中 每次单击该复选框时,该语言的记录都会在文件室数据库中以及服务器上被选中和取消选中。用户登录的其他设备也会自动更新,因此我使用的是LiveData。我与RecyclerView bug发生冲突,该bug在每次更新列表时都会将列表推回顶部,而不管当前滚动位置如何 因此,假设我有两部打开语言屏幕的手机,我将它们都向下滚动到底部,然后选择或取消选择最后一种语言。两台设备上的RecyclerView都会滚动回顶部,但我希望它保持静止 我已经看到了很多解决方法来修复这种行为,但我对Android和Kotlin相对较新,而且建议的所有补丁都没有解释清楚,我无法实现它们。我需要做什么,我应该在哪里做 RecyclerView位于一个片段中:Android 向下滚动的RecyclerView跳回顶部当LiveData更新其内容时,它应该保持其当前的Y偏移量,android,kotlin,android-recyclerview,android-livedata,Android,Kotlin,Android Recyclerview,Android Livedata,我有一个可使用复选框选择的语言列表的RecyclerView。它使用LiveData,因此当用户从另一个客户端选择或取消选择语言时,更改会自动反映在所有客户端中 每次单击该复选框时,该语言的记录都会在文件室数据库中以及服务器上被选中和取消选中。用户登录的其他设备也会自动更新,因此我使用的是LiveData。我与RecyclerView bug发生冲突,该bug在每次更新列表时都会将列表推回顶部,而不管当前滚动位置如何 因此,假设我有两部打开语言屏幕的手机,我将它们都向下滚动到底部,然后选择或取消
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/languages_container"
tools:context=".ui.members.languages.LanguagesFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/languages_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:paddingBottom="15dp"
app:layoutManager="LinearLayoutManager"
app:layout_constraintTop_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:context="com.example.flashcardmallard.ui.members.languages.LanguagesFragment"
tools:listitem="@layout/fragment_languages_cards" />
</FrameLayout>
这是适配器:
class LanguagesRecyclerViewAdapter ( private val languages: List < LanguagesEntity > ) : RecyclerView.Adapter < LanguagesRecyclerViewAdapter.ViewHolder > ()
{
override fun onCreateViewHolder ( viewGroup: ViewGroup, i: Int ): ViewHolder
{
return ViewHolder ( LayoutInflater.from ( viewGroup.context ).inflate ( R.layout.fragment_languages_cards, viewGroup, false ) )
}
override fun onBindViewHolder ( viewHolder: ViewHolder, index: Int )
{
val language = languages [ index ]
viewHolder.languageCard.text = language.name
viewHolder.languageCard.isChecked = language.spoken
}
override fun getItemCount() = languages.size
inner class ViewHolder ( itemView: View ) : RecyclerView.ViewHolder ( itemView )
{
val languageCard: CheckBox = itemView.findViewById ( R.id.language_selection )
init
{
languageCard.setOnClickListener {
LANGUAGE_RECYCLER_VIEW_STATE = LANGUAGES_SCREEN_VIEWS.languagesRecyclerView.layoutManager!!.onSaveInstanceState()!!
GlobalScope.launch {
// Save to local database
LanguageRepository ().updateLanguage ( languageCard )
// Delete on server, pop up if not possible
var dataSync = DataSyncBean ( languages = arrayListOf ( LanguageBean ( addedOrDeleted = if (
languageCard.isChecked ) ADDED else DELETED, name = languageCard.text.toString() ) ) )
try
{
dataSync = Retrofit.Builder().baseUrl ( ConstantsUtil.BASE_URL ).addConverterFactory ( GsonConverterFactory.create () ).client (
OkHttpClient.Builder().cookieJar ( CookieUtil () ).build() ).build().create (
RestfulCalls::class.java ).updateUserLanguageMapping ( dataSync )
}
catch ( e : Exception )
{
dataSync.syncOutcome = ConstantsUtil.SERVER_UNREACHABLE
withContext ( Dispatchers.Main ) {
val alertDialog : AlertDialog.Builder = AlertDialog.Builder ( LANGUAGES_FRAGMENT.context )
alertDialog.setTitle ( R.string.no_server_connection )
alertDialog.setMessage ( R.string.changes_local_only )
alertDialog.setPositiveButton ( R.string.ok ) { _, _ -> }
val alert: AlertDialog = alertDialog.create()
alert.setCanceledOnTouchOutside ( true )
alert.show()
}
}
}
}
}
}
}
另一个解决方案说,这个错误取决于布局的高度是“包装内容”,并建议一个固定的高度,但尽管有一个愚蠢的高度数字,RecyclerView根本没有滚动
建议包括一个解决方法
本页提供了一些解决方案:
一人
// Save state
private Parcelable recyclerViewState;
recyclerViewState = recyclerView.getLayoutManager().onSaveInstanceState();
// Restore state
recyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState);
但是我完全不知道把这些行放在我的文件里的什么地方
我想我应该截取数据更新之前的时刻(不能点击,因为数据库可以通过其他设备的同步更新)和更新回收器视图的时刻。我不明白这是怎么回事。Teo在这个更具体的问题中给出了解决方案: 以下是他的解决方案: //步骤1-创建一个函数来通知变量
internal fun setLanguage(lang: List<LanguagesEntity>) {
languages = lang
notifyDataSetChanged()
}
为什么每次观察者更改时都要调用
setAdapter()
,而不是更新现有适配器?滚动位置与特定的适配器实例有关。@ianhanniballake诚实的问题是,我更改了一个示例,该示例正是这样做的,我认为效果很好,直到我意识到它会导致该问题。所以我还没有完全理解它的功能,以及如何修复它。。。我理解你所说的概念,但不知道如何实际做到。@ianhanniballake嘿,谢谢你之前的评论!它确实为我指明了正确的方向,并让我创建了一个更具体的帖子,帮助我找到了答案:-)很高兴你能找到解决方案!
val linearLayoutManager = LinearLayoutManager ( context, RecyclerView.VERTICAL, false )
LANGUAGES_SCREEN_VIEWS.languagesRecyclerView.setLayoutManager(linearLayoutManager)
// Save state
private Parcelable recyclerViewState;
recyclerViewState = recyclerView.getLayoutManager().onSaveInstanceState();
// Restore state
recyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState);
internal fun setLanguage(lang: List<LanguagesEntity>) {
languages = lang
notifyDataSetChanged()
}
val languages = mutableListOf < LanguagesEntity > ()
var languagesAdapter = LanguagesRecyclerViewAdapter(languages)
LANGUAGES_SCREEN_VIEWS.languagesRecyclerView.adapter = languagesAdapter
languagesViewModel.getLanguagesForUser().observe( requireActivity(), { languages ->
languagesAdapter.setLanguage(language)
} )