From fd50a2a6b89a53509064b776aed7c5d6b782d57f Mon Sep 17 00:00:00 2001 From: liushuai1 Date: Tue, 18 Jun 2019 17:57:36 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96payload=E7=9A=84=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../silencedut/diffadapterdemo/LOLActivity.kt | 4 +- .../diffadapterdemo/LegendViewModel.kt | 19 +- .../silencedut/diffadapter/DiffAdapter.java | 198 ++++++++++-------- .../diffadapter/data/BaseMutableData.java | 9 +- .../diffadapter/utils/UpdateFunction.java | 2 + .../utils/UpdatePayloadFunction.java | 38 ++++ 6 files changed, 172 insertions(+), 98 deletions(-) create mode 100644 diffadapter/src/main/java/com/silencedut/diffadapter/utils/UpdatePayloadFunction.java diff --git a/app/src/main/java/com/silencedut/diffadapterdemo/LOLActivity.kt b/app/src/main/java/com/silencedut/diffadapterdemo/LOLActivity.kt index 457904e..5e81212 100644 --- a/app/src/main/java/com/silencedut/diffadapterdemo/LOLActivity.kt +++ b/app/src/main/java/com/silencedut/diffadapterdemo/LOLActivity.kt @@ -49,7 +49,7 @@ class LOLActivity : AppCompatActivity(){ diffAdapter.registerHolder(LegendHolder::class.java, LegendViewData.VIEW_ID) - val linearLayoutManager = LinearLayoutManager(this) + val linearLayoutManager = LinearLayoutManagerWrapper(this) mRVTest!!.layoutManager = linearLayoutManager mRVTest!!.adapter = diffAdapter @@ -173,7 +173,7 @@ class LOLActivity : AppCompatActivity(){ it.post(object :Runnable{ override fun run() { - diffAdapter.clear() + // diffAdapter.clear() val time = (30 until 450L).random() it.postDelayed(this, time) } diff --git a/app/src/main/java/com/silencedut/diffadapterdemo/LegendViewModel.kt b/app/src/main/java/com/silencedut/diffadapterdemo/LegendViewModel.kt index 9b494fe..6fcb702 100644 --- a/app/src/main/java/com/silencedut/diffadapterdemo/LegendViewModel.kt +++ b/app/src/main/java/com/silencedut/diffadapterdemo/LegendViewModel.kt @@ -9,6 +9,7 @@ import com.silencedut.core.provider.legend.LegendNotification import com.silencedut.core.provider.legend.pojo.* import com.silencedut.diffadapter.DiffAdapter import com.silencedut.diffadapter.data.BaseMutableData +import com.silencedut.diffadapter.utils.UpdatePayloadFunction import com.silencedut.diffadapter.utils.UpdateFunction import com.silencedut.diffadapterdemo.adapter.LegendViewData import com.silencedut.diffadapterdemo.adapter.SkinViewData @@ -90,15 +91,17 @@ class LegendViewModel: ViewModel(), LegendNotification.LegendInfo, LegendNotific }) //如果变化的数据只需要特定类型的Holder刷新,类型即可指定 - diffAdapter.addUpdateMediator(legendPriceData, object : UpdateFunction { - override fun providerMatchFeature(input: LegendPrice): Any { - return input.id + diffAdapter.addUpdateMediator(legendPriceData, object : UpdatePayloadFunction() { + override fun applyChange( + input: LegendPrice, originalData: LegendViewData, payloadKeys: MutableSet + ): LegendViewData { + originalData.price = input.price + payloadKeys.add(LegendViewData.KEY_PRICE) + return originalData } - override fun applyChange(input: LegendPrice, originalData: LegendViewData): LegendViewData { - Log.d(TAG,"applyChange legendPriceData $input") - //可以new对象 - return LegendViewData(originalData.id, originalData.legendBaseInfo, input.price) + override fun providerMatchFeature(input: LegendPrice): Any { + return input.id } }) @@ -124,7 +127,7 @@ class LegendViewModel: ViewModel(), LegendNotification.LegendInfo, LegendNotific override fun onLegendPriceFetched(legendPrice: LegendPrice) { Log.d(TAG,"onLegendPriceFetched $legendPrice") - legendPriceData.value = legendPrice + legendPriceData.postValue(legendPrice) } diff --git a/diffadapter/src/main/java/com/silencedut/diffadapter/DiffAdapter.java b/diffadapter/src/main/java/com/silencedut/diffadapter/DiffAdapter.java index 70f4e4e..a287d2b 100644 --- a/diffadapter/src/main/java/com/silencedut/diffadapter/DiffAdapter.java +++ b/diffadapter/src/main/java/com/silencedut/diffadapter/DiffAdapter.java @@ -27,6 +27,7 @@ import com.silencedut.diffadapter.holder.BaseDiffViewHolder; import com.silencedut.diffadapter.holder.NoDataDifferHolder; import com.silencedut.diffadapter.utils.ListChangedCallback; +import com.silencedut.diffadapter.utils.UpdatePayloadFunction; import com.silencedut.diffadapter.utils.UpdateFunction; import java.lang.reflect.Constructor; @@ -34,26 +35,28 @@ import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Set; /** * 大部分情况下是不需要再Adapter做过多的逻辑操作的,Adapter的目的就是用来组织Holder * * @author SilenceDut * @date 2018/9/6 - * + *

* 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. * 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. * So never modify the view. Always modify the data and notify the adapter. bindView should be the place where you treat these cases. - * + *

* 异步数据结果回来不应该直接改变view的状态,而是应该改变数据,然后通过adapter来改变View */ public class DiffAdapter extends RecyclerView.Adapter { private static final String TAG = "DiffAdapter"; private SparseArray> typeHolders = new SparseArray<>(); - private List mDatas ; + private List mDatas; private LayoutInflater mInflater; private LifecycleOwner mLifecycleOwner; @@ -65,18 +68,17 @@ public class DiffAdapter extends RecyclerView.Adapter { public Fragment attachedFragment; public Context mContext; - @SuppressWarnings("unchecked") public DiffAdapter(FragmentActivity appCompatActivity) { - doInit(appCompatActivity,appCompatActivity); + doInit(appCompatActivity, appCompatActivity); } public DiffAdapter(Fragment attachedFragment) { - doInit(attachedFragment.getActivity(),attachedFragment); + doInit(attachedFragment.getActivity(), attachedFragment); this.attachedFragment = attachedFragment; } - private void doInit(FragmentActivity appCompatActivity,LifecycleOwner lifecycleOwner) { + private void doInit(FragmentActivity appCompatActivity, LifecycleOwner lifecycleOwner) { this.mContext = appCompatActivity; this.mInflater = LayoutInflater.from(appCompatActivity); this.mLifecycleOwner = lifecycleOwner; @@ -89,11 +91,11 @@ public void onChanged(@Nullable Boolean o) { mLifecycleOwner.getLifecycle().addObserver(new GenericLifecycleObserver() { @Override public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) { - if(event == Lifecycle.Event.ON_DESTROY) { - if(mLifecycleOwner != null ) { + if (event == Lifecycle.Event.ON_DESTROY) { + if (mLifecycleOwner != null) { mLifecycleOwner.getLifecycle().removeObserver(this); } - Log.d(TAG,"latchList removeCallbacksAndMessages"); + Log.d(TAG, "latchList removeCallbacksAndMessages"); mDiffHandler.removeCallbacksAndMessages(null); } } @@ -108,7 +110,8 @@ public void onListChanged(List currentList) { }, new DiffUtil.ItemCallback() { @Override public boolean areItemsTheSame(@NonNull BaseMutableData oldItem, @NonNull BaseMutableData newItem) { - return oldItem.getItemViewId() == newItem.getItemViewId() && oldItem.uniqueItemFeature().equals(newItem.uniqueItemFeature()); + return oldItem.getItemViewId() == newItem.getItemViewId() && + oldItem.uniqueItemFeature().equals(newItem.uniqueItemFeature()); } @@ -147,26 +150,39 @@ public void registerHolder(Class viewHolder, List< setDatas(data); } - public void addUpdateMediator(LiveData elementData, final UpdateFunction updateFunction) { + public void addUpdateMediator(LiveData elementData, + final UpdateFunction updateFunction) { mUpdateMediatorLiveData.addSource(elementData, new Observer() { @Override public void onChanged(@Nullable final I dataSource) { - if(dataSource!=null) { + if (dataSource != null) { Object matchFeature = updateFunction.providerMatchFeature(dataSource); + Log.d("addUpdateMediator", + "before"+dataSource.toString()+",,"+ updateFunction.getClass().getGenericSuperclass()+ + ";;;;"+updateFunction.getClass().getGenericSuperclass()); + ParameterizedType parameterizedType ; + + + if(updateFunction instanceof UpdatePayloadFunction) { + parameterizedType = + (ParameterizedType)((UpdatePayloadFunction)updateFunction).getClass().getGenericSuperclass(); + }else { + parameterizedType = (ParameterizedType) updateFunction.getClass().getGenericInterfaces()[0]; + } - ParameterizedType parameterizedType = (ParameterizedType) updateFunction.getClass().getGenericInterfaces()[0]; Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); - Type neededDataType ; - if(actualTypeArguments.length > 1 ) { + Log.d("addUpdateMediator","after type 1"+actualTypeArguments.toString()); + Type neededDataType; + if (actualTypeArguments.length > 1) { neededDataType = actualTypeArguments[1]; - }else { + } else { return; } - + Log.d("addUpdateMediator","after type"+neededDataType); Class clsType = null; - if(neededDataType!=null) { + if (neededDataType != null) { if (neededDataType instanceof Class) { clsType = (Class) neededDataType; } else if (neededDataType instanceof ParameterizedType) { @@ -178,27 +194,33 @@ public void onChanged(@Nullable final I dataSource) { } List oldMatchedDatas; - if(UpdateFunction.MATCH_ALL.equals(matchFeature)) { + if (UpdateFunction.MATCH_ALL.equals(matchFeature)) { oldMatchedDatas = getData(clsType); - }else { + } else { oldMatchedDatas = getMatchedData(matchFeature, clsType); - } - for(final R oldData : oldMatchedDatas) { - if(oldData != null ) { - long current = SystemClock.elapsedRealtime(); - if(current > mCanUpdateTimeMill || getItemCount() < UPDATE_DELAY_THRESHOLD) { + for (final R oldData : oldMatchedDatas) { + if (oldData != null) { + final R newData ; + final Set keys = new HashSet<>(); + if(updateFunction instanceof UpdatePayloadFunction) { + newData = (R) ((UpdatePayloadFunction)updateFunction).applyChange(dataSource,oldData,keys); + }else { + newData = updateFunction.applyChange(dataSource, oldData); + } + long current = SystemClock.elapsedRealtime(); + if (current > mCanUpdateTimeMill || getItemCount() < UPDATE_DELAY_THRESHOLD) { - updateData(updateFunction.applyChange(dataSource, oldData)); - mCanUpdateTimeMill = current+ AsyncListUpdateDiffer.DELAY_STEP; - }else { + updateData(newData,keys); + mCanUpdateTimeMill = current + AsyncListUpdateDiffer.DELAY_STEP; + } else { long delay = mCanUpdateTimeMill - current; mDiffHandler.postDelayed(new Runnable() { @Override public void run() { - updateData(updateFunction.applyChange(dataSource, oldData)); + updateData(newData,keys); } }, delay); mCanUpdateTimeMill += AsyncListUpdateDiffer.DELAY_STEP; @@ -211,8 +233,6 @@ public void run() { }); } - - public void setDatas(List datas) { List newList = new ArrayList<>(datas); @@ -237,11 +257,10 @@ public void run() { notifyItemChanged(mDatas.size() - 1); } - },mDatas); + }, mDatas); } - public void deleteData(final Object uniqueItemFeature) { if (uniqueItemFeature == null) { return; @@ -254,19 +273,18 @@ public void run() { int position = -1; while (iterator.hasNext()) { - position ++; + position++; - if(uniqueItemFeature.equals(iterator.next().uniqueItemFeature())) { + if (uniqueItemFeature.equals(iterator.next().uniqueItemFeature())) { iterator.remove(); break; } } notifyItemRemoved(position); } - },mDatas); + }, mDatas); } - public void deleteData(final BaseMutableData data) { if (data == null) { return; @@ -279,16 +297,16 @@ public void run() { int position = -1; while (iterator.hasNext()) { - position ++; + position++; - if(data.uniqueItemFeature().equals(iterator.next().uniqueItemFeature())) { + if (data.uniqueItemFeature().equals(iterator.next().uniqueItemFeature())) { iterator.remove(); break; } } notifyItemRemoved(position); } - },mDatas); + }, mDatas); } public void deleteData(final int startPosition, final int size) { @@ -300,9 +318,9 @@ public void deleteData(final int startPosition, final int size) { @Override public void run() { Iterator iterator = mDatas.iterator(); - int deleteSize =0; - int startIndex =0; - while (startIndex < startPosition && iterator.hasNext() ) { + int deleteSize = 0; + int startIndex = 0; + while (startIndex < startPosition && iterator.hasNext()) { startIndex++; iterator.next(); } @@ -314,11 +332,11 @@ public void run() { notifyItemRangeRemoved(startPosition, deleteSize); } - },mDatas); + }, mDatas); } - public void insertData(final int startPosition , final List datas) { + public void insertData(final int startPosition, final List datas) { if (datas == null || datas.isEmpty()) { return; } @@ -326,17 +344,16 @@ public void insertData(final int startPosition , final List payloadKeys) { + if (newData == null) { + return; } Iterator iterator = mDatas.iterator(); @@ -344,30 +361,34 @@ public void updateData(BaseMutableData newData) { while (iterator.hasNext()) { BaseMutableData data = iterator.next(); - foundIndex ++; + foundIndex++; - if(data.getItemViewId() == newData.getItemViewId() - && newData.uniqueItemFeature().equals(data.uniqueItemFeature()) ) { + if (data.getItemViewId() == newData.getItemViewId() + && newData.uniqueItemFeature().equals(data.uniqueItemFeature())) { - mDatas.set(foundIndex,newData); + mDatas.set(foundIndex, newData); - if(mDatas.size() > foundIndex) { + if (mDatas.size() > foundIndex) { Bundle payload = data.getDiffPayload(newData); - if(payload.isEmpty()) { - Log.d(TAG,"notifyItemChanged :"+foundIndex+"isEmpty"); + if (payloadKeys.isEmpty() && payload.isEmpty()) { + Log.d(TAG, "notifyItemChanged :" + foundIndex + "isEmpty"); notifyItemChanged(foundIndex); + } else if(payload.isEmpty()){ + Log.d(TAG, "notifyItemChanged :" + foundIndex + ",size:" + payload); + notifyItemChanged(foundIndex, payloadKeys); }else { - Log.d(TAG,"notifyItemChanged :"+foundIndex+",size:"+payload); - notifyItemChanged(foundIndex,payload); + notifyItemChanged(foundIndex, payload); } } - } } } + public void updateData(BaseMutableData newData) { + updateData(newData,new HashSet()); + } @NonNull @Override @@ -392,16 +413,15 @@ public void onBindViewHolder(@NonNull BaseDiffViewHolder baseDiffViewHolder, int try { baseDiffViewHolder.update(mDatas.get(position), position); - }catch (Exception e) { - Log.e(TAG,"onBindViewHolder updatePartWithPayload error",e); + } catch (Exception e) { + Log.e(TAG, "onBindViewHolder updatePartWithPayload error", e); } } - @Override public void onBindViewHolder(@NonNull BaseDiffViewHolder holder, int position, @NonNull List payloads) { - if (mDatas.size() == 0 || mDatas.get(position)==null) { + if (mDatas.size() == 0 || mDatas.get(position) == null) { return; } @@ -410,45 +430,50 @@ public void onBindViewHolder(@NonNull BaseDiffViewHolder holder, int position, @ } if (payloads.isEmpty()) { - this.onBindViewHolder(holder,position); - }else { + this.onBindViewHolder(holder, position); + } else { try { Bundle diffPayloads = new Bundle(); - for(Object payload:payloads) { - if(payload instanceof Bundle) { + for (Object payload : payloads) { + if (payload instanceof Bundle) { diffPayloads.putAll((Bundle) payload); + } else if (payload instanceof HashSet) { + for (Object key : (HashSet) payload) { + if (key instanceof String) { + diffPayloads.putString((String)key, (String)key); + } + } } } - if(diffPayloads.isEmpty()) { - this.onBindViewHolder(holder,position); - }else { - holder.updatePartWithPayload(mDatas.get(position),diffPayloads, position); + if (diffPayloads.isEmpty()) { + this.onBindViewHolder(holder, position); + } else { + holder.updatePartWithPayload(mDatas.get(position), diffPayloads, position); } - }catch (Exception e) { - Log.e(TAG,"onBindViewHolder updatePartWithPayload payload error",e); + } catch (Exception e) { + Log.e(TAG, "onBindViewHolder updatePartWithPayload payload error", e); } } } - - public List getMatchedData(Object matchChangeFeature,Class cls) { + public List getMatchedData(Object matchChangeFeature, Class cls) { List matchedMutableData = new ArrayList<>(); - for(BaseMutableData baseMutableData : mDatas) { - if(baseMutableData!=null && baseMutableData.matchChangeFeatures().contains(matchChangeFeature) && cls.isInstance(baseMutableData)) { - matchedMutableData.add((T)baseMutableData); + for (BaseMutableData baseMutableData : mDatas) { + if (baseMutableData != null && baseMutableData.matchChangeFeatures().contains(matchChangeFeature) && + cls.isInstance(baseMutableData)) { + matchedMutableData.add((T) baseMutableData); } } return matchedMutableData; } - public List getData(Class tClass) { List classLists = new ArrayList<>(); - for(BaseMutableData baseMutableData : mDatas) { - if(tClass.isInstance(baseMutableData)) { + for (BaseMutableData baseMutableData : mDatas) { + if (tClass.isInstance(baseMutableData)) { classLists.add((T) baseMutableData); } } @@ -463,7 +488,6 @@ public List getDatas() { return Collections.unmodifiableList(mDatas); } - @Override public int getItemCount() { return mDatas.size(); @@ -472,7 +496,7 @@ public int getItemCount() { @Override public int getItemViewType(int position) { - if(mDatas.get(position)!=null) { + if (mDatas.get(position) != null) { return mDatas.get(position).getItemViewId(); } diff --git a/diffadapter/src/main/java/com/silencedut/diffadapter/data/BaseMutableData.java b/diffadapter/src/main/java/com/silencedut/diffadapter/data/BaseMutableData.java index de49641..09dbe8f 100644 --- a/diffadapter/src/main/java/com/silencedut/diffadapter/data/BaseMutableData.java +++ b/diffadapter/src/main/java/com/silencedut/diffadapter/data/BaseMutableData.java @@ -5,8 +5,10 @@ import android.support.annotation.NonNull; import com.silencedut.diffadapter.IProvideItemId; +import com.silencedut.diffadapter.utils.UpdatePayloadFunction; import java.util.HashSet; +import java.util.List; import java.util.Set; /** @@ -40,7 +42,7 @@ public abstract class BaseMutableData implements IPro public abstract boolean areUISame(@NonNull T data); /** - * payload 方式 更新item,可实现对单个Item的局部刷新 + * payload 方式 更新item,可实现对Item的局部刷新 * @param newData 新数据 * @return 旧数据和新数据需要改变的部分, */ @@ -50,6 +52,11 @@ public abstract class BaseMutableData implements IPro return mPayloadBundle; } + /** + * 用于全量数据对比时对Item进行局部更新 + * 如果在一个页面不会多次调用{@link com.silencedut.diffadapter.DiffAdapter#setDatas(List)}或者不使用payload更新方式,可不实现此方法 + * 单个数据局部更新的方式用use {@link UpdatePayloadFunction}, + */ public void appendDiffPayload(@NonNull T newData,@NonNull Bundle diffPayloadBundle){ } diff --git a/diffadapter/src/main/java/com/silencedut/diffadapter/utils/UpdateFunction.java b/diffadapter/src/main/java/com/silencedut/diffadapter/utils/UpdateFunction.java index 1ec07d9..3103c62 100644 --- a/diffadapter/src/main/java/com/silencedut/diffadapter/utils/UpdateFunction.java +++ b/diffadapter/src/main/java/com/silencedut/diffadapter/utils/UpdateFunction.java @@ -6,7 +6,9 @@ /** * @author SilenceDut + * use {@link UpdatePayloadFunction} */ +@Deprecated public interface UpdateFunction { /** diff --git a/diffadapter/src/main/java/com/silencedut/diffadapter/utils/UpdatePayloadFunction.java b/diffadapter/src/main/java/com/silencedut/diffadapter/utils/UpdatePayloadFunction.java new file mode 100644 index 0000000..745306c --- /dev/null +++ b/diffadapter/src/main/java/com/silencedut/diffadapter/utils/UpdatePayloadFunction.java @@ -0,0 +1,38 @@ +package com.silencedut.diffadapter.utils; + + +import android.os.Bundle; +import android.support.annotation.NonNull; + +import com.silencedut.diffadapter.data.BaseMutableData; + +import java.util.Set; + +/** + * @author SilenceDut + * @date 2019-06-18 + * 用来动态跟新单个数据项 + */ +public abstract class UpdatePayloadFunction implements UpdateFunction { + + /** + * 匹配到对应的数据,如果符合条件的数据有很多个,可能会被回调多次,不需要新建对象,主需要根据Input把originalData改变相应的值就行了 + * @param input 是数据改变的部分数据源 + * @param originalData 需要改变的数据项 + * @param payloadKeys 用来标识改变后的数据哪些部分发生了改变,if payloadKeys is not empty , + * {@link com.silencedut.diffadapter.holder.BaseDiffViewHolder#updatePartWithPayload(BaseMutableData, Bundle, int)} + * will be call rather than + * {@link com.silencedut.diffadapter.holder.BaseDiffViewHolder#updateItem(BaseMutableData, int)} + * @return 改变后的数据项, + * + */ + + public abstract R applyChange(@NonNull I input, @NonNull R originalData, @NonNull Set payloadKeys); + + @Override + @Deprecated + public R applyChange(@NonNull I input, @NonNull R originalData){ + return null; + } + +}