notifyDataSetChanged()强制在FragmentStatePagerAdapter

时间:2016-08-12 10:53:13

标签: android android-viewpager fragmentpageradapter notifydatasetchanged fragmentstatepageradapter

我有一个FragmentStatePagerAdapter,每秒刷新一次,为他的某些页面添加新数据。

问题是某些页面有很多内容和垂直滚动,所以每当调用notifyDataSetChanged()时,滚动都会被压缩到他的上层。这是一种非常不正常和烦人的行为。

我在stackoverflow上找到了这个:notifyDataSetChanged() makes the list refresh and scroll jumps back to the top

问题是这些解决方案是针对普通的ViewPager或普通适配器而设计的,并且不能使用FragmentStatePageAdapter或其他东西,因为在尝试它们之后我仍然遇到同样的问题。

这是我的适配器:

public class CollectionPagerAdapter extends FragmentStatePagerAdapter {
    public CollectionPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int i) {
        Fragment fragment = new ObjectFragment();
        Bundle args = new Bundle();
        args.putString(ObjectFragment.ARG_TEXT, children[i]);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public int getCount() {
        return infoTitlesArray.length;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return infoTitlesArray[position];
    }

    @Override
    public int getItemPosition(Object object) {
        return POSITION_NONE;
    }
}

ViewPager有问题:

<android.support.v4.view.ViewPager
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v4.view.PagerTitleStrip
            android:id="@+id/pager_title_strip"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="top"
            android:paddingTop="4dp"
            android:paddingBottom="4dp"
            style="@style/CustomPagerTitleStrip"/>
    </android.support.v4.view.ViewPager>

片段的布局:

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scrollbarSize="5dip"
    style="@style/CustomScrollBar">
    <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="left"
        android:textSize="16sp"
        android:padding="5dp"
        style="@style/CustomTextView"/>
</ScrollView>

片段的java代码:

public static class ObjectFragment extends Fragment {
    public static final String ARG_TEXT = "object";

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_collection_object, container, false);
        Bundle args = getArguments();
        TextView tv = ((TextView) rootView.findViewById(R.id.text));
        tv.setText(Html.fromHtml(args.getString(ARG_TEXT)));
        return rootView;
    }
}

1 个答案:

答案 0 :(得分:1)

编辑:

我创建了一个演示项目。这是一些重要的部分。

  1. 使用FragmentStatePagerAdapter子类。

    我们需要一个FragmentStatePagerAdapter基类来保存片段的状态。

  2. ScrollView的滚动位置保存在onSaveInstanceState()中,并在(重新)创建片段视图时将滚动位置设置为保存的值。

    现在我们正在保存/恢复片段状态,我们将滚动位置置于该状态:

        @Override
        public void onSaveInstanceState(Bundle outState) {
            int scrollY = scrollView.getScrollY();
            outState.putInt("scrollY", scrollY);
            super.onSaveInstanceState(outState);
        }
    

    并在onCreateView()中恢复

            if (savedInstanceState != null) {
                final int scrollY = savedInstanceState.getInt("scrollY");
                scrollView.post(new Runnable() {
                    @Override
                    public void run() {
                        scrollView.setScrollY(scrollY);
                    }
                });
            }
    
  3. 为更新设置侦听器通知系统。

    我们有一个名为DataUpdateListener的接口,由片段实现。该活动提供了注册/取消注册方法:

    public void addDataUpdateListener(DataUpdateListener listener) {
        mListenerMap.put(listener.getPage(), listener);
    }
    
    public void removeDataUpdateListener(DataUpdateListener listener) {
        mListenerMap.remove(listener.getPage());
    }
    

    ...和片段寄存器&amp;取消注册活动:

    onCreateView()中的

        ((MainActivity) getActivity()).addDataUpdateListener(this);
    

        @Override
        public void onDestroyView() {
            ((MainActivity) getActivity()).removeDataUpdateListener(this);
            super.onDestroyView();
        }
    

    然后,只要数据发生变化,片段都会收到更新通知:

            for (int i = 0; i < children.length; i++) {
                notifyUpdateListener(i, children[i]);
            }
    
  4. 请注意,代码中的任何地方都没有在视图寻呼机适配器上调用onNotifyDataSetChanged()

    该演示位于https://github.com/klarson2/View-Pager-Data-Update

    的GitHub上

    这是导致滚动的原因:

        @Override
        public int getItemPosition(Object object) {
            return POSITION_NONE;
        }
    

    当您致电notifyDataSetChanged()时,ViewPager所做的就是询问您如何处理已有的网页。

    因此,它会调用getItemPosition来查明:此页面应该放在哪里?您有三种选择:

    1. 返回索引。因此,如果您为第0页返回2,则ViewPager会将页面从0移动到2。

    2. 返回POSITION_UNCHANGED。该页面将保持现在的位置。

    3. 返回POSITION_NONE。这意味着不再显示该页面。

    4. 一旦ViewPager知道页面移动到的位置,它就会调用getItem()来查找页面中的任何空白。

      因此,如果您不希望滚动受到干扰,请告诉ViewPager放置页面的位置,而不是告诉它去除它并创建一个新页面。