保存NestedScrollView的滚动状态

时间:2017-06-07 08:12:28

标签: android savestate android-nestedscrollview

我的应用程序围绕一个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 onethat one),他们的解决方案要么在我的方案中不起作用,要么在实践中不切实际有4-12个不同的片段,我需要修改才能使其正常工作。

让嵌套滚动视图在片段更改时保持其滚动位置的最佳方法是什么?

4 个答案:

答案 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_NAMESPACESaveScrollNestedScrollViewer类的名称空间):

<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)

由于现在所有答案都已弃用,我将为大家提供一个新选项。

  1. 创建一个变量来保存视图模型上的嵌套滚动视图:
class DummyViewModel : ViewModel() {
var estadoNestedSV:Int?=null
}
  1. 在您的 Fragment 上覆盖 onStop 以在嵌套滚动视图被销毁之前保存状态:
override fun onStop() {
        try {
            super.onStop()
            viewModel.estadoNestedSV = binding.nestedSV.scrollY
        } catch (e: Exception) {
            Log.i((activity as MainActivity).constantes.TAG_GENERAL, e.message!!)
        }
    }
  1. 通过覆盖 onViewCreated 在片段上创建视图后恢复状态:
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()
}

这只是一个简单的例子,但这个概念是正确的。