我的应用程序围绕一个HomeActivity,底部包含4个选项卡。这些选项卡中的每一个都是一个片段,所有这些片段都是从头开始添加(不替换),并在点击相应的选项卡时隐藏/显示它们。
我的问题是每当我更改标签时,我的滚动状态都会丢失。展示该问题的每个片段都使用android.support.v4.widget.NestedScrollView
(参见下面的示例)。
注意:使用RecyclerView或ListView的片段由于某种原因保持其滚动状态。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/include_appbar_title" />
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Content -->
</android.support.v4.widget.NestedScrollView>
</LinearLayout>
我阅读了几篇关于保存实例状态的帖子(例如this one,that one),他们的解决方案要么在我的方案中不起作用,要么在实践中不切实际有4-12个不同的片段,我需要修改才能使其正常工作。
让嵌套滚动视图在片段更改时保持其滚动位置的最佳方法是什么?
答案 0 :(得分:11)
我在inthecheesefactory上找到的一个解决方案是,默认情况下,片段的状态已保存(从EditText中的输入到滚动位置),但仅限于为xml元素提供ID。
就我而言,只需在我的NestedScrollView中添加一个ID就可以解决问题:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/include_appbar_title" />
<android.support.v4.widget.NestedScrollView
android:id="@+id/NestedScrollView"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Content -->
</android.support.v4.widget.NestedScrollView>
</LinearLayout>
答案 1 :(得分:0)
您可以通过首先公开相应的方法来自行管理实例状态(包括滚动状态):
class SaveScrollNestedScrollViewer : NestedScrollView {
constructor(context: Context) : super(context)
constructor(context: Context, attributes: AttributeSet) : super(context, attributes)
constructor(context: Context, attributes: AttributeSet, defStyleAttr: Int) : super(context, attributes, defStyleAttr)
public override fun onSaveInstanceState(): Parcelable? {
return super.onSaveInstanceState()
}
public override fun onRestoreInstanceState(state: Parcelable?) {
super.onRestoreInstanceState(state)
}
}
然后在您的视图中使用它(YOUR_NAMESPACE
是SaveScrollNestedScrollViewer
类的名称空间):
<YOUR_NAMESPACE.SaveScrollNestedScrollViewer
android:id="@+id/my_scroll_viewer"
android:layout_width="match_parent"
android:layout_height="match_parent">
</YOUR_NAMESPACE.SaveScrollNestedScrollViewer>
,然后在显示它的活动中,根据需要保存/恢复状态。例如,如果要在导航后恢复滚动位置,请使用以下命令:
class MyActivity : AppCompatActivity() {
companion object {
var myScrollViewerInstanceState: Parcelable? = null
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.my_activity)
if (myScrollViewerInstanceState != null) {
my_scroll_viewer.onRestoreInstanceState(myScrollViewerInstanceState)
}
}
public override fun onPause() {
super.onPause()
myScrollViewerInstanceState = my_scroll_viewer.onSaveInstanceState()
}
}
答案 2 :(得分:0)
由于现在所有答案都已弃用,我将为大家提供一个新选项。
class DummyViewModel : ViewModel() {
var estadoNestedSV:Int?=null
}
override fun onStop() {
try {
super.onStop()
viewModel.estadoNestedSV = binding.nestedSV.scrollY
} catch (e: Exception) {
Log.i((activity as MainActivity).constantes.TAG_GENERAL, e.message!!)
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
try {
//Check first if data exists to know if this load is a first time or if the device was rotated.
if(viewModel.data.value != null)
binding.nestedSVPelisDetalles.scrollY = viewModel.estadoNestedSV!!
} catch (e: Exception) {
Log.i((activity as MainActivity).constantes.TAG_GENERAL, e.message!!)
}
}
快乐编码!
答案 3 :(得分:0)
查看 NestedScrollView 的实现,我们看到 NestedScrollView 的 scrollY 属性存储在其 SavedState 中作为其保存的滚动位置。
// Source: NestedScrollView.java
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.scrollPosition = getScrollY();
return ss;
}
因此,在跨配置更改保留滚动位置的想法方面,我确实同意 Ramiro G.M.。在这种情况下,我认为不需要 NestedScrollView 的子类。
如果您使用的是 Fragment 和 MVVM,那么我会在 Fragment onViewDestroyed 方法中将 NestedScrollView 的滚动位置保存到我的 ViewModel。创建片段视图后,您可以稍后通过 LiveData 对象观察状态。
override fun onViewCreated(...) {
mViewModel.scrollState.observe(viewLifecycleOwner, { scrollState ->
binding.myNestedScrollView.scrollY = scrollState
})
}
override fun onDestroyView() {
val scrollState = binding.myNestedScrollView.scrollY
mViewModel.setScrollState(scrollState)
super.onDestroyView()
}
这只是一个简单的例子,但这个概念是正确的。