什么与RecyclerView.Adapter一起使用的SortedList <T>是什么?


71

Android支持库22.1已于昨天发布。v4支持库和v7中添加了许多新功能,其中android.support.v7.util.SortedList<T>引起了我的注意。

据说,这SortedList是一种新的数据结构,可与一起使用RecyclerView.Adapter,维护所提供的项目的添加/删除/移动/更改的动画RecyclerView。听起来很像a List<T>ListView但看起来更先进,更强大。

那么,SortedList<T>和之间有什么区别List<T>?如何有效使用它?如果是这样,SortedList<T>over的执行List<T>是什么?有人可以张贴一些样品吗?

任何提示或代码将不胜感激。提前致谢。


2
它必须是基于通用类型T的Comparable实现排序的列表-如果不是,则google在命名约定部门中已经失去了一些要点。
好人

它是一个根据比较回调对元素进行排序的列表。因此,您可以添加元素,它们将显示在“正确”位置(而不是最后)。看看Javadoc。developer.android.com/reference/android/support/v7/util/…–
Thilo

1
最重要的是,如何与它一起使用RecyclerView.Adapter?有人为此写样本吗?
SilentKnight 2015年

Answers:


95

SortedList通过处理与回收站适配器的通信Callback

在以下示例的辅助方法中可以看到SortedList和之间的区别。ListaddAll

public void addAll(List<Page> items) {
        mPages.beginBatchedUpdates();
        for (Page item : items) {
            mPages.add(item);
        }
        mPages.endBatchedUpdates();
    }
  1. 保留最后添加的项目

假设我的回收商列表被填充后,我有10个缓存项目要立即加载。同时,我在网络中查询相同的10个项目,因为自从缓存它们以来它们可能已经更改。我可以调用相同的addAll方法,并SortedList在后台使用fetchedItems替换cachedItems(始终保留最后添加的项目)。

// After creating adapter
myAdapter.addAll(cachedItems)
// Network callback
myAdapter.addAll(fetchedItems)

在常规中List,我将拥有所有项目的副本(列表大小为20)。使用SortedList其替换项,使用Callback的项相同areItemsTheSame

  1. 何时更新视图很聪明

添加fetchedItems时,onChange仅当一个或多个Page标题更改时才会调用。您可以自定义SortedList在Callback中查找的内容areContentsTheSame

  1. 其表现

如果要将多个项目添加到SortedList,则将项目添加到连续索引中时,BatchedCallback调用会将单个onInserted(index,1)调用转换为一个onInserted(index,N)。此更改可以帮助RecyclerView更轻松地解决更改。

样品

您可以在您的适配器上使用getter SortedList,但是我只是决定向我的适配器添加辅助方法。

适配器类别:

  public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private SortedList<Page> mPages;

    public MyAdapter() {
        mPages = new SortedList<Page>(Page.class, new SortedList.Callback<Page>() {
            @Override
            public int compare(Page o1, Page o2) {
                return o1.getTitle().compareTo(o2.getTitle());
            }

            @Override
            public void onInserted(int position, int count) {
                notifyItemRangeInserted(position, count);
            }

            @Override
            public void onRemoved(int position, int count) {
                notifyItemRangeRemoved(position, count);
            }

            @Override
            public void onMoved(int fromPosition, int toPosition) {
                notifyItemMoved(fromPosition, toPosition);
            }

            @Override
            public void onChanged(int position, int count) {
                notifyItemRangeChanged(position, count);
            }

            @Override
            public boolean areContentsTheSame(Page oldItem, Page newItem) {
                // return whether the items' visual representations are the same or not.
                return oldItem.getTitle().equals(newItem.getTitle());
            }

            @Override
            public boolean areItemsTheSame(Page item1, Page item2) {
                return item1.getId() == item2.getId();
            }
        });

    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.viewholder_page, parent, false);
        return new PageViewHolder(view);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        PageViewHolder pageViewHolder = (PageViewHolder) holder;
        Page page = mPages.get(position);
        pageViewHolder.textView.setText(page.getTitle());
    }

    @Override
    public int getItemCount() {
        return mPages.size();
    }

    // region PageList Helpers
    public Page get(int position) {
        return mPages.get(position);
    }

    public int add(Page item) {
        return mPages.add(item);
    }

    public int indexOf(Page item) {
        return mPages.indexOf(item);
    }

    public void updateItemAt(int index, Page item) {
        mPages.updateItemAt(index, item);
    }

    public void addAll(List<Page> items) {
        mPages.beginBatchedUpdates();
        for (Page item : items) {
            mPages.add(item);
        }
        mPages.endBatchedUpdates();
    }

    public void addAll(Page[] items) {
        addAll(Arrays.asList(items));
    }

    public boolean remove(Page item) {
        return mPages.remove(item);
    }

    public Page removeItemAt(int index) {
        return mPages.removeItemAt(index);
    }

    public void clear() {
       mPages.beginBatchedUpdates();
       //remove items at end, to avoid unnecessary array shifting
       while (mPages.size() > 0) {
          mPages.removeItemAt(mPages.size() - 1);
       }
       mPages.endBatchedUpdates();
    }
}

页面类:

public class Page {
    private String title;
    private long id;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }
}

观看者xml:

<?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="wrap_content">

    <TextView
        android:id="@+id/text_view"
        style="@style/TextStyle.Primary.SingleLine"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

观看者类别:

public class PageViewHolder extends RecyclerView.ViewHolder {
    public TextView textView;


    public PageViewHolder(View itemView) {
        super(itemView);
        textView = (TextView)item.findViewById(R.id.text_view);
    }
}

1
这是使用SortedList领域的最佳方法RecyclerView.Adapter吗?
SilentKnight 2015年

我要由程序员决定哪种设计最适合他们。就我而言,让适配器处理所有数据并SortedList在内部表示数据更有意义。
Amozoss 2015年

15
请注意,有SortedListAdapterCallback,它使用aRecyclerView.Adapter作为构造函数参数,onInserted()并为您处理和kin方法。
CommonsWare,2015年

3
实现是错误的。它破坏了equals()和compareTo()之间的约定。您必须检查compareTo()元素是否相等,并在比较标题之前返回0。否则,由于依赖合同,您最终将遇到重复项作为SortedList的情况。
Paul Woitaschek

3
在更新项目时,如果我有新的已更新的和已删除的项目,您将如何处理?.addAll不会有帮助,因为它不会删除现在已删除的项目,并且.clear()会“重置”列表(我需要检查)。.beginBatchUpdate通过封装.remove()后跟一个循环可以帮助.addAll()吗?
David Corsalini 2015年

14

SortedListv7 support library

一种SortedList实现,它可以使项目保持顺序,并通知列表中的更改,以便可以将其绑定到 RecyclerView.Adapter

它使用该compare(Object, Object)方法使项目保持有序,并使用二进制搜索来检索项目。如果项目的排序标准可能会更改,请确保在编辑它们时调用适当的方法,以避免数据不一致。

您可以控制参数的顺序并通过SortedList.Callback参数更改通知 。

以下是的使用示例SortedList,我认为这是您想要的,看看并享受吧!

public class SortedListActivity extends ActionBarActivity {
    private RecyclerView mRecyclerView;
    private LinearLayoutManager mLinearLayoutManager;
    private SortedListAdapter mAdapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.sorted_list_activity);
        mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        mRecyclerView.setHasFixedSize(true);
        mLinearLayoutManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(mLinearLayoutManager);
        mAdapter = new SortedListAdapter(getLayoutInflater(),
                new Item("buy milk"), new Item("wash the car"),
                new Item("wash the dishes"));
        mRecyclerView.setAdapter(mAdapter);
        mRecyclerView.setHasFixedSize(true);
        final EditText newItemTextView = (EditText) findViewById(R.id.new_item_text_view);
        newItemTextView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
                if (id == EditorInfo.IME_ACTION_DONE &&
                        (keyEvent == null || keyEvent.getAction() == KeyEvent.ACTION_DOWN)) {
                    final String text = textView.getText().toString().trim();
                    if (text.length() > 0) {
                        mAdapter.addItem(new Item(text));
                    }
                    textView.setText("");
                    return true;
                }
                return false;
            }
        });
    }

    private static class SortedListAdapter extends RecyclerView.Adapter<TodoViewHolder> {
        SortedList<Item> mData;
        final LayoutInflater mLayoutInflater;
        public SortedListAdapter(LayoutInflater layoutInflater, Item... items) {
            mLayoutInflater = layoutInflater;
            mData = new SortedList<Item>(Item.class, new SortedListAdapterCallback<Item>(this) {
                @Override
                public int compare(Item t0, Item t1) {
                    if (t0.mIsDone != t1.mIsDone) {
                        return t0.mIsDone ? 1 : -1;
                    }
                    int txtComp = t0.mText.compareTo(t1.mText);
                    if (txtComp != 0) {
                        return txtComp;
                    }
                    if (t0.id < t1.id) {
                        return -1;
                    } else if (t0.id > t1.id) {
                        return 1;
                    }
                    return 0;
                }

                @Override
                public boolean areContentsTheSame(Item oldItem,
                        Item newItem) {
                    return oldItem.mText.equals(newItem.mText);
                }

                @Override
                public boolean areItemsTheSame(Item item1, Item item2) {
                    return item1.id == item2.id;
                }
            });
            for (Item item : items) {
                mData.add(item);
            }
        }

        public void addItem(Item item) {
            mData.add(item);
        }

        @Override
        public TodoViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) {
            return new TodoViewHolder (
                    mLayoutInflater.inflate(R.layout.sorted_list_item_view, parent, false)) {
                @Override
                void onDoneChanged(boolean isDone) {
                    int adapterPosition = getAdapterPosition();
                    if (adapterPosition == RecyclerView.NO_POSITION) {
                        return;
                    }
                    mBoundItem.mIsDone = isDone;
                    mData.recalculatePositionOfItemAt(adapterPosition);
                }
            };
        }

        @Override
        public void onBindViewHolder(TodoViewHolder holder, int position) {
            holder.bindTo(mData.get(position));
        }

        @Override
        public int getItemCount() {
            return mData.size();
        }
    }

    abstract private static class TodoViewHolder extends RecyclerView.ViewHolder {
        final CheckBox mCheckBox;
        Item mBoundItem;
        public TodoViewHolder(View itemView) {
            super(itemView);
            mCheckBox = (CheckBox) itemView;
            mCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    if (mBoundItem != null && isChecked != mBoundItem.mIsDone) {
                        onDoneChanged(isChecked);
                    }
                }
            });
        }

        public void bindTo(Item item) {
            mBoundItem = item;
            mCheckBox.setText(item.mText);
            mCheckBox.setChecked(item.mIsDone);
        }

        abstract void onDoneChanged(boolean isChecked);
    }

    private static class Item {
        String mText;
        boolean mIsDone = false;
        final public int id;
        private static int idCounter = 0;

        public Item(String text) {
            id = idCounter ++;
            this.mText = text;
        }
    }
}

很抱歉几个月后对此发表评论,但是,当添加一些导致回收的物品时,我是唯一遇到一些严重问题的人吗?如果我添加了12个条目,它就会发疯并开始触发一些动作,而且它会删除所有先前输入的数据...
fiipi

抱歉,我的意思是示例代码,请允许我详细说明一下。也许最好问一个单独的问题。
fiipi 2015年

TL; DR:SortedListAdapterCallback在构建您的SortedList<>
lionello时,

5

支持库源存储库中有一个示例SortedListActivity,该示例演示了如何在RecyclerView.Adapter中使用SortedList和SortedListAdapterCallback。从SDK的根目录开始,在安装了支持库的情况下,它应该位于 extras/android/support/samples/Support7Demos/src/com/example/android/supportv7/util/SortedListActivity.java(也位于github上)。

这些特殊示例的存在在Google文档的处理不同主题的页面的底部被提及一次,因此,我不怪您找不到它。


2
无效链接。你知道现在在哪里吗?
MidasLefko '16


2

关于SortedList实现,它由<T>默认最小容量为10项的阵列支持。阵列装满后,将其大小调整为size() + 10

源代码在这里

文档

Sorted list实现,可以使项目保持顺序,并通知列表中的更改,以便可以将其绑定到RecyclerView.Adapter。

它使用compare(Object,Object)方法保持项目排序,并使用二进制搜索来检索项目。如果项目的排序标准可能会更改,请确保在编辑它们时调用适当的方法,以避免数据不一致。

您可以通过SortedList.Callback参数控制项目的顺序并更改通知。

关于性能,他们还添加了SortedList.BatchedCallback一次执行多个操作,而不是一次执行

可以批量通知SortedList调度的事件的回调实现。

如果您要对SortedList进行多个操作,但又不想一个一个地分派每个事件,则可能会导致性能问题,该类很有用。

例如,如果要将多个项目添加到SortedList,则将项目添加到连续索引中时,BatchedCallback调用将单个onInserted(index,1)调用转换为一个onInserted(index,N)。此更改可以帮助RecyclerView更轻松地解决更改。

如果SortedList中的连续更改不适合批处理,则只要检测到这种情况,BatchingCallback就会分派它们。完成对SortedList的编辑后,必须始终调用dispatchLastEvent()刷新对回调的所有更改。


它很好地解释了。Samples会更好。
SilentKnight 2015年

目前,我没有时间拿出样本,阅读文档并深入研究。
Axxiss
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.