1
2/*
3 * Copyright (C) 2016 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package com.example.android.supportv7.widget;
19
20import android.support.v4.util.Pair;
21import android.support.v7.widget.DividerItemDecoration;
22import android.support.v7.widget.LinearLayoutManager;
23import android.support.v7.widget.RecyclerView;
24import android.view.LayoutInflater;
25import android.view.View;
26import android.view.ViewGroup;
27import android.widget.TextView;
28
29import com.example.android.supportv7.Cheeses;
30import com.example.android.supportv7.R;
31
32import java.util.ArrayList;
33import java.util.List;
34
35/**
36 * A sample Stable Id RecyclerView activity.
37 *
38 * Stable Ids guarantee that items that remain onscreen due to a data set change are rebound to the
39 * same views. This sample visually identifies views (independent from their data) to demonstrate
40 * how RecyclerView does this rebinding. In addition, you can observe how RecyclerView recycles
41 * items in response to a scroll or fling.
42 *
43 * Tapping an item will send it's data to the top of the list. RecyclerView will detect that the
44 * data with that stable ID has moved position, and move the View accordingly, even though the only
45 * signal sent to the RecyclerView was notifyDataSetChanged().
46 *
47 * Compared to DiffUtil or SortedList, stable Ids are often easier to use, but are less efficient,
48 * and can only look at attached views to try and form reasonable animations for an update. Note
49 * that notifyDataSetChanged() will still always cause every visible item to be rebound, since
50 * RecyclerView isn't told what *inside the item* may have changed.
51 *
52 * It is suggested instead to use DiffUtil or SortedList to dispatch minimal updates from your
53 * data set to the RecyclerView. SortedList allows you to express operations like moving or removing
54 * an item very efficiently, without RecyclerView needing to find and compare the state of each
55 * child before and after an operation. DiffUtil can compute minimal alterations (such as inserts
56 * and moves) from lists that you pass it - useful if your data source or server doesn't provide
57 * delta updates. Both DiffUtil and SortedList allow you to avoid rebinding each item when a small
58 * change occurs.
59 */
60public class StableIdActivity extends BaseLayoutManagerActivity<LinearLayoutManager> {
61    @Override
62    protected LinearLayoutManager createLayoutManager() {
63        return new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
64    }
65
66    @Override
67    protected RecyclerView.Adapter createAdapter() {
68        return new StableIdAdapter(Cheeses.sCheeseStrings);
69    }
70
71    @Override
72    protected void onRecyclerViewInit(RecyclerView recyclerView) {
73        recyclerView.addItemDecoration(
74                new DividerItemDecoration(this, mLayoutManager.getOrientation()));
75    }
76
77    static class StableIdAdapter extends RecyclerView.Adapter<StableIdAdapter.ViewHolder> {
78        List<Pair<Integer, String>> mData = new ArrayList<>();
79
80        public static class ViewHolder extends RecyclerView.ViewHolder {
81            static int sHolderNumber = 0;
82
83            private final TextView mHolderNumberView;
84            private final TextView mDataView;
85
86            public ViewHolder(View itemView) {
87                super(itemView);
88                mHolderNumberView = (TextView) itemView.findViewById(R.id.holder_number_text);
89                mDataView = (TextView) itemView.findViewById(R.id.data_text);
90
91                // Just for demonstration, we visually uniquely identify which ViewHolder is which,
92                // so rebinding / moving due to stable IDs can be observed:
93                mHolderNumberView.setText("View Nr: " + sHolderNumber++);
94            }
95        }
96
97        StableIdAdapter(String[] strings) {
98            // comment out this line to dispatch updates without using stable IDs -
99            // this prevents RecyclerView from knowing what changed, so animations don't run
100            setHasStableIds(true);
101
102
103            for (int i = 0; i < 20; i++) {
104                mData.add(new Pair<>(500 + i, strings[i]));
105            }
106        }
107
108        @Override
109        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
110            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
111            final ViewHolder viewHolder = new ViewHolder(
112                    inflater.inflate(R.layout.stable_id_item, parent, false));
113
114            viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
115                @Override
116                public void onClick(View v) {
117                    final int pos = viewHolder.getAdapterPosition();
118                    if (pos != RecyclerView.NO_POSITION) {
119                        // swap item to top, and notify data set changed
120                        Pair<Integer, String> d = mData.remove(pos);
121                        mData.add(0, d);
122
123                        notifyDataSetChanged();
124                    }
125                }
126            });
127            return viewHolder;
128        }
129
130        @Override
131        public void onBindViewHolder(ViewHolder holder, int position) {
132            holder.mDataView.setText(mData.get(position).second);
133        }
134
135        @Override
136        public long getItemId(int position) {
137            return mData.get(position).first;
138        }
139
140        @Override
141        public int getItemCount() {
142            return mData.size();
143        }
144    }
145}
146