我正在实施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>
非常感谢