Android——SortedList

一、作用

保持内部数据有序,可自动去重,在对 SortedList 数据进行增删改查时,能够自动通知 RecyclerView 数据集中的更改

二、概念

1. 构造函数

2. 方法

(1)List 接口中常用方法
虽然 SortedList 和 List 没有直接的继承实现等关系,但这两个里面提供的一些方法是类似的。

(2)特别的方法
void beginBatchUpdates()
void endBatchUpdates()

3. 相关类

(1)abstract class SortedList.Callback

public static abstract class Callback < T2 > implements Comparator < T2 > , ListUpdateCallback {

    /**
     * Similar to {@link java.util.Comparator#compare(Object, Object)}, 比较两个对象并返回它们的顺序.
     *
     * @param o1 The first object to compare.
     * @param o2 The second object to compare.
     *
     * @return 负值(o1<o2),0(o1==02),正值(o1>o2)
     */
    @Override
    abstract public int compare(T2 o1, T2 o2);

    /**
     * Called by the SortedList when the item at the given position is updated.
     * 当指定 position 位置的 Item 更新时,由 SortedList 调用
     * @param position The position of the item which has been updated.
     * @param count    The number of items which has changed.
     */
    abstract public void onChanged(int position, int count);

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

    /**
     * 由 SortedList 调用,用于检查两个 items 是否有相同的数据。根据该方法的返回值,决定是否要调用{@link #onChanged(int, int)} 。
     * <p>
     * SortedList 使用该方法检查一致性,而不是 {@link Object#equals(Object)}
     * 所以
     * 你可根据你的 UI 自己决定如何书写该方法
     * <p>
     * @param oldItem The previous representation of the object.
     * @param newItem The new object that replaces the previous one.
     *
     * @return True :两个 Item 有相同的内容;false:内容不相同
     */
    abstract public boolean areContentsTheSame(T2 oldItem, T2 newItem);


    /**
     * 由 SortedList 调用,用于检查是否是两个相同的 item,如果你的 item 有唯一的 ID,那么可以使用 id 检查两个 item 是否相同。
     * <p>
     *
     * @param item1 The first item to check.
     * @param item2 The second item to check.
     *
     * @return True :两个 Item 相同;false:不相同
     */
    abstract public boolean areItemsTheSame(T2 item1, T2 item2);

    /**
     * 当 {@link #areItemsTheSame(T2, T2)} returns {@code true} for two items and
     * {@link #areContentsTheSame(T2, T2)} returns false for them, {@link Callback} 调用该方法得到 payload 封装 Item 的变化。
     * <p>
     * For example, if you are using {@link Callback} with
     * {@link RecyclerView}, you can return the particular field that
     * changed in the item and your
     * {@link RecyclerView.ItemAnimator ItemAnimator} can use that
     * information to run the correct animation.
     * <p>
     * 默认实现返回 {@code null}.
     *
     * @param item1 The first item to check.
     * @param item2 The second item to check.
     * @return A payload object 代表两个 item 之间的不同
     */
    @Nullable
    public Object getChangePayload(T2 item1, T2 item2) {
        return null;
    }
}

(2)static class SortedList.BatchedCallback

/**
     * A callback implementation that can batch notify events dispatched by the SortedList.
     extends Callback ,可以批量通知 SortedList 
     * <p>
     * This class can be useful if you want to do multiple operations on a SortedList but don't
     * want to dispatch each event one by one, which may result in a performance issue.
     * <p>
     如果你想向 SortedList 中添加多个 item,如果 items 是被连续添加进来的,BatchedCallback 可以将多个<code>onInserted(index, 1)</code> 
     调用转化成一个 <code>onInserted(index, N)</code> 调用。
     * For example, if you are going to add multiple items to a SortedList, BatchedCallback call
     * convert individual <code>onInserted(index, 1)</code> calls into one
     * <code>onInserted(index, N)</code> if items are added into consecutive indices. This change
     * can help RecyclerView resolve changes much more easily.
     * <p>
     如果连续的改变不适合 batching,BatchedCallback 会立即发送通知。在你编辑完 SortedList 后,要调用
     {@link BatchedCallback#dispatchLastEvent()} 去刷新变化,告诉 Callback。
     * If consecutive changes in the SortedList are not suitable for batching, BatchingCallback
     * dispatches them as soon as such case is detected. After your edits on the SortedList is
     * complete, you <b>must</b> always call {@link BatchedCallback#dispatchLastEvent()} to flush
     * all changes to the Callback.
     */
public static class BatchedCallback < T2 > extends Callback < T2 > {

    final Callback < T2 > mWrappedCallback;
    private final BatchingListUpdateCallback mBatchingListUpdateCallback;
    /**
     * Creates a new BatchedCallback that wraps the provided Callback.
     *
     * @param wrappedCallback The Callback which should received the data change callbacks.
     *                        Other method calls (e.g. {@link #compare(Object, Object)} from
     *                        the SortedList are directly forwarded to this Callback.
     */
    public BatchedCallback(Callback < T2 > wrappedCallback) {
        mWrappedCallback = wrappedCallback;
        mBatchingListUpdateCallback = new BatchingListUpdateCallback(mWrappedCallback);
    }

    @Override
    public int compare(T2 o1, T2 o2) {
        return mWrappedCallback.compare(o1, o2);
    }

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

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

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

    @Override
    public void onChanged(int position, int count) {
        mBatchingListUpdateCallback.onChanged(position, count, null);
    }

    @Override
    public void onChanged(int position, int count, Object payload) {
        mBatchingListUpdateCallback.onChanged(position, count, payload);
    }

    @Override
    public boolean areContentsTheSame(T2 oldItem, T2 newItem) {
        return mWrappedCallback.areContentsTheSame(oldItem, newItem);
    }

    @Override
    public boolean areItemsTheSame(T2 item1, T2 item2) {
        return mWrappedCallback.areItemsTheSame(item1, item2);
    }

    @Nullable
    @Override
    public Object getChangePayload(T2 item1, T2 item2) {
        return mWrappedCallback.getChangePayload(item1, item2);
    }

    /**
     * This method dispatches any pending event notifications to the wrapped Callback.
     * You <b>must</b> always call this method after you are done with editing the SortedList.
     */
    public void dispatchLastEvent() {
        mBatchingListUpdateCallback.dispatchLastEvent();
    }
}

(3)SortedListAdapterCallback

它是 SortedList.Callback 抽象类的一个实现,可以将一个 SortedList 和 RecyclerView.Adapter 绑定。

它的唯一一个构造函数是

SortedListAdapterCallback(Adapter adapter)

有4个抽象方法需具体的使用者实现。


  • areItemsTheSame(T2 item1,T2 item2)

该方法由 SortedList 调用,以决定两个 Object 是否是相同的 item。

  • areContentTheSame(T2 oldItem,T2 newItem)

由 SortedList 调用,以决定两个 item 是否有相同的数据。

  • compare(T2 o1,T2 o2)

得到 o1、o2 的排列规则。

  • onChange(int position,int count)

由 SortedList 调用,当给定 position 的 item 更新时。

三、使用

1. 创建 Adapter

和常规 Adapter 的写法一样,只是把 Adapter 的数据源替换成SortedList<>类型

public class MyAdapter extends RecyclerView.Adapter < MyAdapter.UserViewHolder > {

    private Context mContext;
    private SortedList < User > mData;
    private LayoutInflater mLayoutInflater;

    public MyAdapter(Context context, SortedList < User > data) {
        this.mContext = context;
        this.mData = data;
        this.mLayoutInflater = LayoutInflater.from(context);
    }

    public SortedList < User > getData() {
        return mData;
    }

    public void setData(SortedList < User > data) {
        mData = data;
    }

    @NonNull
    @Override
    public UserViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = mLayoutInflater.inflate(R.layout.user_item, parent, false);
        return new UserViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull UserViewHolder holder, int position, @NonNull List < Object > payloads) {
        super.onBindViewHolder(holder, position, payloads);
    }

    @Override
    public int getItemCount() {
        return mData == null ? 0 : mData.size();
    }

    @Override
    public void onBindViewHolder(@NonNull UserViewHolder holder, int position) {
        User user = mData.get(position);
        holder.idTv.setText(user.getId() + "");
        holder.nameTv.setText(user.getName());
        holder.ageTv.setText(user.getAge() + "");
        holder.profileTv.setText(user.getProfile());
    }

    class UserViewHolder extends RecyclerView.ViewHolder {
        private TextView nameTv;
        private TextView ageTv;
        private TextView profileTv;
        private TextView idTv;

        public UserViewHolder(View view) {
            super(view);
            idTv = view.findViewById(R.id.id_tv);
            nameTv = view.findViewById(R.id.name_tv);
            ageTv = view.findViewById(R.id.age_tv);
            profileTv = view.findViewById(R.id.profile_tv);
        }

    }

}

2. 准备 SortedListAdapterCallback 的实现类

public class SortedListCallback extends SortedListAdapterCallback < User > {
    public SortedListCallback(RecyclerView.Adapter adapter) {
        super(adapter);
    }

    @Override
    public int compare(User o1, User o2) {
        return o1.getId() - o2.getId();
    }

    @Override
    public boolean areContentsTheSame(User oldItem, User newItem) {
        if (oldItem.getId() != newItem.getId()) {
            return false;
        }
        if (!oldItem.getName().equals(newItem.getName())) {
            return false;
        }
        if (oldItem.getAge() != newItem.getAge()) {
            return false;
        }
        if (!oldItem.getProfile().equals(newItem.getProfile())) {
            return false;
        }
        return true;
    }

    @Override
    public boolean areItemsTheSame(User oldItem, User newItem) {
        return oldItem.getId() == newItem.getId();
    }
}
  • areItemTheSame()

该方法中,如果 Item 有可唯一标志的 Id 时,一般用此 id 检查 item 是否相同。

  • areContentsTheSame()

逐项比较 item 的数据,默认相同,只要有一项不同则返回 false。

3. 创建 SortedList 数据集,并设置给 Adapter

SortedList 的构造函数中需要传入 Callback 实例,

Callback 实例创建时需要传入 Adapter 实例,

所以倒过来就是,先创建 Adapter,然后创建 Callback 实例,最后创建 SortedList 实例再设置给 Adapter,所以 Adapter 创建时传不传入数据源都无所谓,因为后面注定要设置新的 SortedList 的。

private void initViews() {
    mRecyclerView = findViewById(R.id.user_rv);

    mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
    // 1,创建Adapter
    mMyAdapter = new MyAdapter(this, null);
    mRecyclerView.setAdapter(mMyAdapter);
    // 2, 创建Callback
    SortedListCallback callback = new SortedListCallback(mMyAdapter);
    // 3,创建SortedList
    mData = new SortedList < > (User.class, callback);

    mData.add(new User(1, "xiaohong1", 10, "adfada"));
    mData.add(new User(2, "xiaohong2", 10, "adfada"));
    mData.add(new User(1, "xiaohong3", 10, "adfada"));
    mData.add(new User(4, "xiaohong4", 10, "adfada"));
    mData.add(new User(5, "xiaohong5", 10, "adfada"));
    // 4,为Adapter 设置新的数据源
    mMyAdapter.setData(mData);
}

4. 实践定向刷新

有了 SortedList,我们刷新数据时不需要再调用notifyXX()相关方法通知 Adapter 更新视图,直接操作数据源 SortedList,对其进行增删改后,数据的修改即可直接反应到视图上。

界面上有一按钮,点击按钮向数据源添加数据项,添加后视图立即更新。

  • 向数据源中增加数据
private void initListener() {
    mRefreshBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            mData.add(new User(10, "dafh", 10, "dafafa"));
            mData.add(new User(11, "dafh", 10, "dafafa"));
            mData.add(new User(12, "dafh", 10, "dafafa"));
        }
    });
}
  • 删除数据

  • 修改数据

四、注意

五、评价

SortedLit 的亮点是维护数据集的有序&去重,它不支持部分绑定

参考文献

【Android】你可能不知道的Support(一) 0步自动定向刷新SortedList
Android-developer:SortedListAdapterCallback

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352

推荐阅读更多精彩内容