带ListView的FragmentStatePagerAdapter ...如何在不向上滚动的情况下更新listview的内容?

时间:2016-09-26 18:18:30

标签: android listview android-viewpager fragmentpageradapter fragmentstatepageradapter

欢迎所有人。

我有一个FragmentStatePagerAdapter,每个页面都有一个Listview。 FragmentStatePagerAdapter每秒刷新一次,为他的一些页面添加新数据(记住每个页面都是列表视图)。

我使用setAdapter设置listview的内容:

StableArrayAdapter adapter = new StableArrayAdapter((MainActivity) getActivity(), R.layout.list_view_item, ((MainActivity) getActivity()).getData(mPage));
listView.setAdapter(adapter);

问题在于某些页面有很多内容和垂直滚动,所以当调用listView.setAdapter(adapter);时,每一秒都会强制列表视图的滚动。这是一种非常不正常和烦人的行为。

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

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

这是我对FragmentStatePagerAdapter的适配器:

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

    @Override
    public Fragment getItem(int position) {
        return ObjectFragment.newInstance(position);
    }

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

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

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>

片段的布局:

<ListView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/listview"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">
</ListView>

片段的java代码:

public static class ObjectFragment extends Fragment implements DataUpdateListener {
    private int mPage;
    private ListView listView;

    public static ObjectFragment newInstance(int page) {
        ObjectFragment objectFragment = new ObjectFragment();
        Bundle args = new Bundle();
        args.putInt("page", page);
        objectFragment.setArguments(args);
        return objectFragment;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mPage = getArguments().getInt("page");
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_collection_object, container, false);
        ((MainActivity) getActivity()).addDataUpdateListener(this);
        listView = (ListView) rootView;

        StableArrayAdapter adapter = new StableArrayAdapter((MainActivity) getActivity(), R.layout.list_view_item, ((MainActivity) getActivity()).getData(mPage));
        listView.setAdapter(adapter);

        return rootView;
    }

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

    //DataUpdateListener interface methods
    @Override
    public int getPage() {
        return mPage;
    }

    @Override
    public void onDataUpdated(ArrayList<String> data) {
        StableArrayAdapter adapter = new StableArrayAdapter((MainActivity) getActivity(), R.layout.list_view_item, data);
        listView.setAdapter(adapter);
    }
}

当需要更新片段的内容(列表视图)时,将使用参数中接收的新内容调用onDataUpdated方法。

感谢您的帮助

编辑:

使用Budius中的代码示例添加生成崩溃的适配器:

private static class StableArrayAdapter extends ArrayAdapter<String> {
        HashMap<String, Integer> mIdMap = new HashMap<String, Integer>();

        public StableArrayAdapter(Context context, int textViewResourceId, List<String> objects) {
            super(context, textViewResourceId, objects);
            for (int i = 0; i < objects.size(); ++i) {
                mIdMap.put(objects.get(i), i);
            }
        }

        public void setData(List<String> objects){
            mIdMap.clear();
            for (int i = 0; i < objects.size(); ++i) {
                mIdMap.put(objects.get(i), i);
            }
        }

        @Override
        public long getItemId(int position) {
            String item = getItem(position);
            return mIdMap.get(item);
        }

        @Override
        public boolean hasStableIds() {
            return true;
        }
    }

编辑2:

尝试使用Budius解决方案的新适配器。但它不起作用。内容没有刷新。

    private static class StableArrayAdapter extends ArrayAdapter<String> {
    List<String> objects;

    public StableArrayAdapter(Context context, int textViewResourceId, List<String> objects) {
        super(context, textViewResourceId, objects);
        this.objects = new ArrayList<>();
        this.objects.addAll(objects);
    }

    public void setData(List<String> objects){
        this.objects.clear();
        this.objects.addAll(objects);
    }

    @Override
    public long getItemId(int position) {
        return Math.abs(objects.get(position).hashCode());
    }

    @Override
    public boolean hasStableIds() {
        return true;
    }
}

1 个答案:

答案 0 :(得分:1)

调用setAdapter(adapter)并返回顶部的列表是正常的并且是“预期的”行为。毕竟,它是一个新的适配器,框架不应该假设新适配器与旧适配器有任何关系。

说,您的解决方案应该仍然在notifyDataSetChanged()左右,并且stableIds的适当用法。考虑到这一点,你应该做的只是切换ArrayList<String> data而不是创建一个新的@Override public void onDataUpdated(ArrayList<String> data) { adapter.setData(data); // internally replaces the old data with this new one adapter.notifyDataSetChanged(); } 。像这样:

hasStableIds

然后在实施StableArrayAdapter时,您必须覆盖getItemId才能返回true并覆盖Math.abs(value.hashCode)以返回有意义的值。考虑到你的数据只是字符串,这有点棘手,但像RecyclerView这样的东西就足够了。

PS:我强烈建议使用ListView代替public class StableArrayAdapter extends /* whatever you're extending */ { ... your code as usual @Override public boolean hasStableIds() { return true; } @Override public long getItemId(int position) { return Math.abs(data.get(position).hashCode()); } } 。就个人而言,我不明白为什么Google还没有弃用ListView。

修改

根据评论,有点像这样:

ID

表示您告诉系统ArrayAdapter与您获得的数据直接相关,并且您返回与您的数据相关联的ID(即使连接丢失)。这意味着,它将是一个数据的一个ID和不同数据的不同ID。

通知ListView的方式知道根据ID保持te位置。

编辑2:

我发现了你的错误。您的适配器从List<String>延伸。这内部已经有getItem(position);,当您致电List<String> objects;时,数据来自该内部列表。

修复很简单。

  • 删除add并使用内部列表。

要更新您从ArrayAdapter(https://developer.android.com/reference/android/widget/ArrayAdapter.html)调用addAllclear@Override public void onDataUpdated(ArrayList<String> data) { adapter.clear(); adapter.addAll(data); adapter.notifyDataSetChanged(); } 方法的数据:

类似的东西:

tell application "Finder"
    set srcPath to POSIX path of ((parent of (path to me) as text) & "M Templates")
    set dstPath to POSIX path of (((path to movies folder) as text) & "M Templates")


    duplicate entire contents of srcPath to dstPath
end tell