如果调用notifyItemRemoved(position),则检测到自定义分区RecyclerView不一致;

时间:2016-11-10 09:53:01

标签: android android-recyclerview

我正在实施SectionedRecyclerViewAdapter。我知道有很多伟大的图书馆可以完成这项任务,但在退出之前我想尝试一下。所以,请在拍摄之前考虑一下这是一个原型,欢迎提出改进建议。

这里实施。这个概念很简单,每个pojo希望分区的每个Sectioned都必须使用一个返回section标题的方法来实现LinkedHashMap接口。然后import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.ViewGroup; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.LinkedHashMap; import java.util.List; abstract public class SectionedRecyclerViewAdapter< HeaderViewHolder extends RecyclerView.ViewHolder, ValueViewHolder extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<RecyclerView.ViewHolder> { public interface Sectioned { String getSection(); } static final private String TAG = "sectioned-rv"; private static final int TYPE_NORMAL = 1; private static final int TYPE_HEADER = 0; final private List<Sectioned> mObjects; final private Object mLock; final private LinkedHashMap<Integer, String> sectionsIndexer; /* Removed for now, not sure if will be more elegant/better or worst private final RecyclerView.AdapterDataObserver mDataSetObserver = new RecyclerView.AdapterDataObserver() { @Override public void onChanged() { calculateSectionHeaders(); } @Override public void onItemRangeRemoved(int positionStart, int itemCount) { onChanged(); } }; */ private boolean mNotifyOnChange = true; public SectionedRecyclerViewAdapter() { super(); mLock = new Object(); sectionsIndexer = new LinkedHashMap<>(); mObjects = new ArrayList<>(); setHasStableIds(true); } public void changeData(List<? extends Sectioned> data) { synchronized (mLock) { mObjects.clear(); if (data != null) mObjects.addAll(data); calculateSectionHeaders(); } if (mNotifyOnChange) notifyDataSetChanged(); } public void removeItemAt(int position) { int viewType = getItemViewType(position); if (viewType == TYPE_NORMAL) { int index = getSectionForPosition(position); synchronized (mLock) { mObjects.remove(index); calculateSectionHeaders(); } if (mNotifyOnChange) notifyItemRemoved(index); //<- Crash // if (mNotifyOnChange) notifyDataSetChanged(); //<- Fine } } public void clear() { synchronized (mLock) { mObjects.clear(); sectionsIndexer.clear(); } if (mNotifyOnChange) notifyDataSetChanged(); } public void sort(Comparator<? super Sectioned> comparator) { synchronized (mLock) { Collections.sort(mObjects, comparator); calculateSectionHeaders(); } if (mNotifyOnChange) notifyDataSetChanged(); } /** * Interceptor before the sections are calculated so we can transform some computer data into human readable, * e.g. format a unix timestamp, or a status * * By default this method returns the original data for the group. * @param groupData original group name * @return the transformed group name */ protected String getCustomGroup(String groupData) { return groupData; } @Override public int getItemCount() { return mObjects.size() + sectionsIndexer.size(); } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == TYPE_HEADER) { return onCreateHeaderViewHolder(parent); } return onCreateValueViewHolder(parent); } @SuppressWarnings("unchecked") @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { int viewType = getItemViewType(position); if (viewType == TYPE_NORMAL) { Sectioned item = getItem(position); if (item == null) { Log.v(TAG, "getItem(" + position + ") = null"); return; } onBindValueViewHolder((ValueViewHolder)holder, position); } else { final String group = sectionsIndexer.get(position); onBindHeaderViewHolder(group, (HeaderViewHolder) holder); } } public Sectioned getItem(int position) { if (getItemViewType(position) == TYPE_NORMAL) { return mObjects.get(getSectionForPosition(position)); } return mObjects.get(position); } @Override public long getItemId(int position) { return position; } @Override public int getItemViewType(int position) { if (position == getPositionForSection(position)) { return TYPE_NORMAL; } return TYPE_HEADER; } abstract protected HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent); abstract protected void onBindHeaderViewHolder(String group, HeaderViewHolder viewHolder); abstract protected ValueViewHolder onCreateValueViewHolder(ViewGroup parent); abstract protected void onBindValueViewHolder(ValueViewHolder holder, int position); private int getPositionForSection(int section) { if (sectionsIndexer.containsKey(section)) { return section + 1; } return section; } private int getSectionForPosition(int position) { int offset = 0; for (Integer key : sectionsIndexer.keySet()) { if (position > key) { offset++; } else { break; } } return position - offset; } private void calculateSectionHeaders() { int i = 0; String previous = ""; int count = 0; sectionsIndexer.clear(); for (Sectioned item: mObjects) { final String group = getCustomGroup(item.getSection()); if (!previous.equals(group)) { sectionsIndexer.put(i + count, group); previous = group; Log.v(TAG, "Group <" + group + "> at position: " + (i + count)); count++; } i++; } } } 将跟踪部分与索引。

removeItemAt(position)

一切正常但我注意到当我删除一个名为notifyItemRemoved(index);的项目时,如果我拨打java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{855a1d3 position=3 id=4, oldPos=4, pLpos:4 scrap [attachedScrap] tmpDetached no parent} at android.support.v7.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition(RecyclerView.java:5041) at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5172) at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5153) 该应用将因此异常而崩溃:

notifyDataSetChanged();

如果我在删除项目后调用holder.mPosition >= mAdapter.getItemCount() ,一切正常。

所以我只是好奇为什么会这样。使用断点调试到RecyclerView实现我注意到原因可能是检查:

getItemCount

但是如果在我的实现@Override public int getItemCount() { return mObjects.size() + sectionsIndexer.size(); } 中返回此内容,则无法查看检查失败的原因:

<div id="ajaxcontent"></div>

<script type='text/javascript' src='http://www.vhb.wiwi.uni-wuerzburg.de/bp1/wp/wp-includes/js/jquery/jquery.js?ver=1.12.4'></script>

<script>
jQuery(document).ready(function($){
    $.ajax({
        url: "http://www.vhb.wiwi.uni-wuerzburg.de/bp1/wp/contentgrabber/contentgrabber.php",
        type: 'GET',
        data: "page_id=73&ajax=1",
        crossDomain: true,
        success: function(data){
            $("#ajaxcontent").html(data);
        }
    });
});
</script>

非常感谢

0 个答案:

没有答案