oldData) {
227 | mCanSyncTime = SystemClock.elapsedRealtime() + (oldData != null ? oldData.size() * DELAY_STEP : 0);
228 | }
229 |
230 |
231 | }
232 |
--------------------------------------------------------------------------------
/diffadapter/src/main/java/com/silencedut/diffadapter/DiffAdapter.java:
--------------------------------------------------------------------------------
1 | package com.silencedut.diffadapter;
2 |
3 | import android.arch.lifecycle.LifecycleOwner;
4 | import android.arch.lifecycle.LiveData;
5 | import android.arch.lifecycle.MediatorLiveData;
6 | import android.arch.lifecycle.Observer;
7 | import android.content.Context;
8 | import android.os.Handler;
9 | import android.os.Looper;
10 | import android.os.SystemClock;
11 | import android.support.annotation.NonNull;
12 | import android.support.annotation.Nullable;
13 | import android.support.v4.app.Fragment;
14 | import android.support.v4.app.FragmentActivity;
15 | import android.support.v7.util.DiffUtil;
16 | import android.support.v7.widget.RecyclerView;
17 | import android.util.Log;
18 | import android.util.SparseArray;
19 | import android.view.LayoutInflater;
20 | import android.view.View;
21 | import android.view.ViewGroup;
22 |
23 | import com.silencedut.diffadapter.data.BaseMutableData;
24 | import com.silencedut.diffadapter.holder.BaseDiffViewHolder;
25 | import com.silencedut.diffadapter.holder.NoDataDifferHolder;
26 | import com.silencedut.diffadapter.utils.ListChangedCallback;
27 | import com.silencedut.diffadapter.utils.UpdatePayloadFunction;
28 |
29 | import java.lang.reflect.Constructor;
30 | import java.lang.reflect.ParameterizedType;
31 | import java.lang.reflect.Type;
32 | import java.util.ArrayList;
33 | import java.util.HashSet;
34 | import java.util.Iterator;
35 | import java.util.List;
36 | import java.util.Set;
37 |
38 | /**
39 | * 大部分情况下是不需要再Adapter做过多的逻辑操作的,Adapter的目的就是用来组织Holder
40 | *
41 | * @author SilenceDut
42 | * @date 2018/9/6
43 | *
44 | * When the async code is done, you should update the data, not the views. After updating the data, tell the adapter that the data changed. The RecyclerView gets note of this and re-renders your view.
45 | * When working with recycling views (ListView or RecyclerView), you cannot know what item a view is representing. In your case, that view gets recycled before the async work is done and is assigned to a different item of your data.
46 | * So never modify the view. Always modify the data and notify the adapter. bindView should be the place where you treat these cases.
47 | *
48 | * 异步数据结果回来不应该直接改变view的状态,而是应该改变数据,然后通过adapter来改变View
49 | */
50 | public class DiffAdapter extends RecyclerView.Adapter {
51 |
52 | private static final String TAG = "DiffAdapter";
53 | private SparseArray> typeHolders = new SparseArray<>();
54 | private List mDatas;
55 |
56 | private LayoutInflater mInflater;
57 | private LifecycleOwner mLifecycleOwner;
58 | private AsyncListUpdateDiffer mDifferHelper;
59 | private MediatorLiveData mUpdateMediatorLiveData = new MediatorLiveData<>();
60 | private long mCanUpdateTimeMill;
61 | private static final int UPDATE_DELAY_THRESHOLD = 100;
62 | Handler mDiffHandler = new Handler(Looper.getMainLooper());
63 | public Fragment attachedFragment;
64 | public Context mContext;
65 |
66 | @SuppressWarnings("unchecked")
67 | public DiffAdapter(FragmentActivity appCompatActivity) {
68 | doInit(appCompatActivity, appCompatActivity);
69 | }
70 |
71 | public DiffAdapter(Fragment attachedFragment) {
72 | doInit(attachedFragment.getActivity(), attachedFragment);
73 | this.attachedFragment = attachedFragment;
74 | }
75 |
76 | private void doInit(FragmentActivity appCompatActivity, LifecycleOwner lifecycleOwner) {
77 | this.mContext = appCompatActivity;
78 | this.mInflater = LayoutInflater.from(appCompatActivity);
79 | this.mLifecycleOwner = lifecycleOwner;
80 | this.mUpdateMediatorLiveData.observe(mLifecycleOwner, new Observer() {
81 | @Override
82 | public void onChanged(@Nullable Boolean o) {
83 | }
84 | });
85 |
86 | mDifferHelper = new AsyncListUpdateDiffer<>(this, new ListChangedCallback() {
87 | @Override
88 | public void onListChanged(List currentList) {
89 | mDatas = currentList;
90 | }
91 | }, new DiffUtil.ItemCallback() {
92 | @Override
93 | public boolean areItemsTheSame(@NonNull BaseMutableData oldItem, @NonNull BaseMutableData newItem) {
94 | return oldItem.getItemViewId() == newItem.getItemViewId() &&
95 | oldItem.uniqueItemFeature().equals(newItem.uniqueItemFeature());
96 |
97 | }
98 |
99 | @Override
100 | public boolean areContentsTheSame(@NonNull BaseMutableData oldItem, @NonNull BaseMutableData newItem) {
101 |
102 | return oldItem.areUISame(newItem);
103 | }
104 |
105 | @Override
106 | public Object getChangePayload(@NonNull BaseMutableData oldItem, @NonNull BaseMutableData newItem) {
107 |
108 | return oldItem.getPayloadKeys(newItem);
109 | }
110 | });
111 | }
112 |
113 | public void registerHolder(Class extends BaseDiffViewHolder> viewHolder, int itemViewType) {
114 | typeHolders.put(itemViewType, viewHolder);
115 | }
116 |
117 | public void registerHolder(Class extends BaseDiffViewHolder> viewHolder, T data) {
118 | if (data == null) {
119 | return;
120 | }
121 | typeHolders.put(data.getItemViewId(), viewHolder);
122 |
123 | addData(data);
124 | }
125 |
126 | public void registerHolder(Class extends BaseDiffViewHolder> viewHolder, List extends BaseMutableData> data) {
127 | if (data == null || data.size() == 0) {
128 | return;
129 | }
130 | typeHolders.put(data.get(0).getItemViewId(), viewHolder);
131 | setDatas(data);
132 | }
133 |
134 | private @Nullable Class findNeedUpdateDataType(Object updateFunction) {
135 | ParameterizedType parameterizedType = (ParameterizedType) updateFunction.getClass().getGenericInterfaces()[0];
136 |
137 | Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
138 | Type neededDataType;
139 | if (actualTypeArguments.length > 1) {
140 | neededDataType = actualTypeArguments[1];
141 | } else {
142 | return null;
143 | }
144 | Class clsType = null;
145 | if (neededDataType != null) {
146 | if (neededDataType instanceof Class) {
147 | clsType = (Class) neededDataType;
148 | } else if (neededDataType instanceof ParameterizedType) {
149 | Type type = ((ParameterizedType) neededDataType).getRawType();
150 | if (type instanceof Class) {
151 | clsType = (Class) type;
152 | }
153 | }
154 | }
155 | return clsType;
156 | }
157 |
158 | public void addUpdateMediator(LiveData elementData,
159 | final UpdatePayloadFunction updatePayloadFunction) {
160 | mUpdateMediatorLiveData.addSource(elementData, new Observer() {
161 | @Override
162 | public void onChanged(@Nullable final I dataSource) {
163 |
164 | if (dataSource != null) {
165 |
166 | Class clsType = findNeedUpdateDataType(updatePayloadFunction);
167 |
168 | if (clsType == null) {
169 | return;
170 | }
171 | Object matchFeature = updatePayloadFunction.providerMatchFeature(dataSource);
172 | List oldMatchedDatas = getMatchedData(matchFeature, clsType);
173 |
174 | for (final R oldData : oldMatchedDatas) {
175 | if (oldData != null) {
176 | final R newData;
177 | final Set keys = oldData.getPayloadKeys();
178 | newData = updatePayloadFunction.applyChange(dataSource, oldData, keys);
179 | long current = SystemClock.elapsedRealtime();
180 | if (current > mCanUpdateTimeMill || getItemCount() < UPDATE_DELAY_THRESHOLD) {
181 |
182 | updateData(newData, keys);
183 | mCanUpdateTimeMill = current + AsyncListUpdateDiffer.DELAY_STEP;
184 | } else {
185 | long delay = mCanUpdateTimeMill - current;
186 |
187 | mDiffHandler.postDelayed(new Runnable() {
188 | @Override
189 | public void run() {
190 | updateData(newData, keys);
191 | }
192 | }, delay);
193 | mCanUpdateTimeMill += AsyncListUpdateDiffer.DELAY_STEP;
194 | }
195 |
196 | }
197 | }
198 | }
199 | }
200 | });
201 | }
202 |
203 |
204 | public void setDatas(List extends BaseMutableData> datas) {
205 |
206 | List newList = new ArrayList<>(datas);
207 | mDifferHelper.submitList(newList);
208 | }
209 |
210 | public void clear() {
211 |
212 | mDifferHelper.submitList(null);
213 |
214 | }
215 |
216 | public void addData(final T data) {
217 | if (data == null) {
218 | return;
219 | }
220 | mDifferHelper.updateOldListSize(new Runnable() {
221 | @Override
222 | public void run() {
223 | mDatas.add(data);
224 | notifyItemChanged(mDatas.size() - 1);
225 |
226 | }
227 | }, mDatas);
228 | }
229 |
230 | public void addDatas(final List datas) {
231 | if (datas == null) {
232 | return;
233 | }
234 | mDifferHelper.updateOldListSize(new Runnable() {
235 | @Override
236 | public void run() {
237 | mDatas.addAll(datas);
238 | notifyItemChanged(mDatas.size() - datas.size());
239 | }
240 | }, mDatas);
241 |
242 | }
243 |
244 | public void deleteData(final Object uniqueItemFeature) {
245 | if (uniqueItemFeature == null) {
246 | return;
247 | }
248 |
249 | mDifferHelper.updateOldListSize(new Runnable() {
250 | @Override
251 | public void run() {
252 | Iterator iterator = mDatas.iterator();
253 |
254 | int position = -1;
255 | while (iterator.hasNext()) {
256 | position++;
257 |
258 | if (uniqueItemFeature.equals(iterator.next().uniqueItemFeature())) {
259 | iterator.remove();
260 | break;
261 | }
262 | }
263 | notifyItemRemoved(position);
264 | }
265 | }, mDatas);
266 | }
267 |
268 | public void deleteData(final BaseMutableData data) {
269 | if (data == null) {
270 | return;
271 | }
272 |
273 | mDifferHelper.updateOldListSize(new Runnable() {
274 | @Override
275 | public void run() {
276 | Iterator iterator = mDatas.iterator();
277 |
278 | int position = -1;
279 | while (iterator.hasNext()) {
280 | position++;
281 |
282 | if (data.uniqueItemFeature().equals(iterator.next().uniqueItemFeature())) {
283 | iterator.remove();
284 | break;
285 | }
286 | }
287 | notifyItemRemoved(position);
288 | }
289 | }, mDatas);
290 | }
291 |
292 | public void deleteData(final int startPosition, final int size) {
293 | if (startPosition + size >= mDatas.size()) {
294 | return;
295 | }
296 |
297 | mDifferHelper.updateOldListSize(new Runnable() {
298 | @Override
299 | public void run() {
300 | Iterator iterator = mDatas.iterator();
301 | int deleteSize = 0;
302 | int startIndex = 0;
303 | while (startIndex < startPosition && iterator.hasNext()) {
304 | startIndex++;
305 | iterator.next();
306 | }
307 | while (iterator.hasNext() && deleteSize < size) {
308 | iterator.next();
309 | iterator.remove();
310 | deleteSize++;
311 | }
312 |
313 | notifyItemRangeRemoved(startPosition, deleteSize);
314 | }
315 | }, mDatas);
316 |
317 | }
318 |
319 | public void insertData(final int startPosition, final List extends BaseMutableData> datas) {
320 | if (datas == null || datas.isEmpty()) {
321 | return;
322 | }
323 |
324 | mDifferHelper.updateOldListSize(new Runnable() {
325 | @Override
326 | public void run() {
327 | if (startPosition > datas.size()) {
328 | mDatas.addAll(datas);
329 | } else {
330 | mDatas.addAll(startPosition, datas);
331 | }
332 |
333 | notifyItemRangeInserted(startPosition, datas.size());
334 | }
335 | }, mDatas);
336 |
337 | }
338 |
339 | private void updateData(BaseMutableData newData, @NonNull Set payloadKeys) {
340 | if (newData == null) {
341 | return;
342 | }
343 | Iterator iterator = mDatas.iterator();
344 | int foundIndex = -1;
345 |
346 | while (iterator.hasNext()) {
347 | BaseMutableData data = iterator.next();
348 | foundIndex++;
349 |
350 | if (data.getItemViewId() == newData.getItemViewId()
351 | && newData.uniqueItemFeature().equals(data.uniqueItemFeature())) {
352 |
353 | mDatas.set(foundIndex, newData);
354 |
355 | if (mDatas.size() > foundIndex) {
356 | Set dataPayloadKeys = data.getPayloadKeys(newData);
357 |
358 | payloadKeys.addAll(dataPayloadKeys);
359 | if (payloadKeys.isEmpty()) {
360 | notifyItemChanged(foundIndex);
361 | } else {
362 | Log.d(TAG, "notifyItemChanged :" + foundIndex + ",payloadKeys:" + payloadKeys);
363 | notifyItemChanged(foundIndex, payloadKeys);
364 | }
365 | }
366 | }
367 | }
368 | }
369 |
370 | public void updateData(BaseMutableData newData) {
371 | updateData(newData, newData.getPayloadKeys());
372 | }
373 |
374 | @NonNull
375 | @Override
376 | public BaseDiffViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
377 | View itemView = mInflater.inflate(viewType, parent, false);
378 | BaseDiffViewHolder viewHolder = new NoDataDifferHolder(itemView, this);
379 | try {
380 | Class> cls = typeHolders.get(viewType);
381 | Constructor holderConstructor = cls.getDeclaredConstructor(View.class, DiffAdapter.class);
382 | holderConstructor.setAccessible(true);
383 | viewHolder = (BaseDiffViewHolder) holderConstructor.newInstance(itemView, this);
384 | } catch (NoSuchMethodException e) {
385 | Log.e(TAG, "Create error,is it a inner class? can't create no static inner ViewHolder ");
386 | } catch (Exception e) {
387 | Log.e(TAG, e.getCause() + "");
388 | }
389 | return viewHolder;
390 | }
391 |
392 | @Override
393 | public void onBindViewHolder(@NonNull BaseDiffViewHolder baseDiffViewHolder, int position) {
394 |
395 | try {
396 | baseDiffViewHolder.update(mDatas.get(position), position);
397 | } catch (Exception e) {
398 | Log.e(TAG, "onBindViewHolder updatePartWithPayload error", e);
399 | }
400 | }
401 |
402 | @Override
403 | public void onBindViewHolder(@NonNull BaseDiffViewHolder holder, int position, @NonNull List