您的位置:首页 > 移动开发 > Android开发

RecyclerView+GridView分组效果

2016-12-28 18:06 489 查看
首先,不上图就是耍流氓!



项目中要做图中类似的效果。

方案一:

RecyclerView嵌套RecyclerView

缺点:页面卡顿

结果:放弃该方案

方案二:

重写适配器

缺点:需要做数据处理(可接受)

优点:顺滑无比,不用嵌套,不用修改recyclerview

结果:使用该方案

使用过程中,愈发感觉RecyclerView的强大,服,是真服!

借鉴了github的开源项目,在此对作者表示感谢。

地址:SectionedRecyclerView

开始使用

RecyclerView照常写就好,这里需要注意的Adapter。先说一下整个设计思路:在adapter 设置分组数getSectionCount , 设置每一组的个数getItemCountForSection,根据这个关键数据,把header和footer视为一个单项,计算出总共的单项数目,然后根据不同的类型,header,footer,item,让GridLayoutManager的getSpanSize不同,即可达到效果。

连个关键

一:在于记录每一个单项(包括头部和底部单项的 )详细属性,

1,是头部,正常,还是底部

2,在哪一个分组,又在分组的第一个位置

二:当是header或者footer是,将GridLayoutManger的spanSize设为全屏

GridLayoutManager layoutManager = new GridLayoutManager(getContext(), 5);
SectionedSpanSizeLookup lookup = new SectionedSpanSizeLookup(mAdapter, layoutManager);
layoutManager.setSpanSizeLookup(lookup);


来看下SectionedSpanSizeLookup的实现,这个就是用来处理每一项显示的宽度大小的,神奇的东西

public class SectionedSpanSizeLookup extends GridLayoutManager.SpanSizeLookup {

protected SectionedRecyclerViewAdapter<?, ?, ?> adapter = null;
protected GridLayoutManager layoutManager = null;

public SectionedSpanSizeLookup(SectionedRecyclerViewAdapter<?, ?, ?> adapter, GridLayoutManager layoutManager) {
this.adapter = adapter;
this.layoutManager = layoutManager;
}

@Override
public int getSpanSize(int position) {

if(adapter.isSectionHeaderPosition(position) || adapter.isSectionFooterPosition(position)){
return layoutManager.getSpanCount();
}else{
return 1;
}

}
}


下面来看一下Adapter的实现,

public class RoomPreAdapter2 extends SimpleSectionedAdapter2<RoomPreAdapter2.RoomHodler> {
private Context context;

@Override
protected String getSectionHeaderTitle(int section) {
return section + "#";
}

/**
* 获取分组数
* @return
*/
@Override
protected int getSectionCount() {
return 10;
}

/**
* 获取指定组的个数
* @param section
* @return
*/
@Override
protected int getItemCountForSection(int section) {
return section + 6;
}

@Override
protected RoomHodler onCreateItemViewHolder(ViewGroup parent, int viewType) {
context = parent.getContext();
View itemView = LayoutInflater.from(context).inflate(R.layout.item_room, parent, false);
return new RoomHodler(itemView);
}

@Override
protected void onBindItemViewHolder(RoomHodler holder, int section, final int position) {
holder.rb_room.setText(section + "-" + position);

holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context, "点了" + position, Toast.LENGTH_SHORT).show();
}
});
holder.rb_room.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Toast.makeText(context, "选了" + position, Toast.LENGTH_SHORT).show();
}
});
}

class RoomHodler extends RecyclerView.ViewHolder {
RadioButton rb_room;

public RoomHodler(View itemView) {
super(itemView);
rb_room = (RadioButton) itemView.findViewById(R.id.rb_room);
}
}
}


这个没什么讲的,就是普通的用法,注意两个方法,getSectionCount()和getItemCountForSection(),还有一个方法getSectionHeaderTitle()用于设置显示的分组名,显然父类中已经进行了完美的封装,下面我们来看父类

public abstract class SimpleSectionedAdapter2<VH <
4000
span class="hljs-keyword">extends RecyclerView.ViewHolder> extends SectionedRecyclerViewAdapter<HeaderViewHolder,
VH, RecyclerView.ViewHolder> {

/**
* 是否显示每一个分组的底部
* @param section
* @return
*/
@Override
protected boolean hasFooterInSection(int section) {
return true;
}

/*
创建头部header的ViewHolder
*/
@Override
protected HeaderViewHolder onCreateSectionHeaderViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View view = inflater.inflate(getLayoutResource(), parent, false);
HeaderViewHolder holder = new HeaderViewHolder(view, getTitleTextID());
return holder;
}

/*
创建底部footer的ViewHolder
*/
@Override
protected RecyclerView.ViewHolder onCreateSectionFooterViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View view = inflater.inflate(getFooterLayoutResource(), parent, false);
FooterViewHolder holder = new FooterViewHolder(view);

return holder;
}

private int getFooterLayoutResource() {
return R.layout.item_room_footer;
}

@Override
protected void onBindSectionHeaderViewHolder(HeaderViewHolder holder, int section) {
String title = getSectionHeaderTitle(section);
holder.render(title);
}

@Override
protected void onBindSectionFooterViewHolder(RecyclerView.ViewHolder holder, int section) {

}

/**
* Provides a layout identifier for the header. Override it to change the appearance of the
* header view.
*/
protected
@LayoutRes
int getLayoutResource() {
return R.layout.item_room_header;
}

/**
* Provides the identifier of the TextView to render the section header title. Override it if
* you provide a custom layout for a header.
*/
protected
@IdRes
int getTitleTextID() {
return R.id.tv_group_name;
}

/**
* Returns the title for a given section
*/
protected abstract String getSectionHeaderTitle(int section);

/**
* 底部Footer的ViewHolder
*/
class FooterViewHolder extends RecyclerView.ViewHolder{
public FooterViewHolder(View itemView) {
super(itemView);
}
}
}


而这个父类这是封装了header和footer的显示,至于header和footer是如何判断?以及如何 根据分组数和每个分组的个数 绘制出我们要的效果?这些问题还得再找父类,找的爷爷辈,别急,继续看,

public abstract class SectionedRecyclerViewAdapter<H extends RecyclerView.ViewHolder,
VH extends RecyclerView.ViewHolder,
F extends RecyclerView.ViewHolder>
extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

protected static final int TYPE_SECTION_HEADER = -1;
protected static final int TYPE_SECTION_FOOTER = -2;
protected static final int TYPE_ITEM = -3;

//用四个数组,对应保存每一个单项(包括header和footer)的四个属性
//1.哪个分组;2.在该组的位置;3.是否是头部;4.是否是底部;
private int[] sectionForPosition = null;
private int[] positionWithinSection = null;
private boolean[] isHeader = null;
private boolean[] isFooter = null;
private int count = 0;

public SectionedRecyclerViewAdapter() {
super();
registerAdapterDataObserver(new SectionDataObserver());
}

@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
setupIndices();
}

/**
* Returns the sum of number of items for each section plus headers and footers if they
* are provided.
*/
@Override
public int getItemCount() {
return count;
}

private void setupIndices(){
count = countItems();
allocateAuxiliaryArrays(count);
precomputeIndices();
}

private int countItems() {
int count = 0;
int sections = getSectionCount();
//计算总个数
for(int i = 0; i < sections; i++){
count += 1 + getItemCountForSection(i) + (hasFooterInSection(i) ? 1 : 0);
}
return count;
}
//为每一个单项,设置四大属性
private void precomputeIndices(){
int sections = getSectionCount();
int index = 0;

for(int i = 0; i < sections; i++){
setPrecomputedItem(index, true, false, i, 0);
index++;

for(int j = 0; j < getItemCountForSection(i); j++){
setPrecomputedItem(index, false, false, i, j);
index++;
}

if(hasFooterInSection(i)){
setPrecomputedItem(index, false, true, i, 0);
index++;
}
}
}

private void allocateAuxiliaryArrays(int count) {
sectionForPosition = new int[count];
positionWithinSection = new int[count];
isHeader = new boolean[count];
isFooter = new boolean[count];
}

private void setPrecomputedItem(int index, boolean isHeader, boolean isFooter, int section, int position) {
this.isHeader[index] = isHeader;
this.isFooter[index] = isFooter;
sectionForPosition[index] = section;
positionWithinSection[index] = position;
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder viewHolder;

if(isSectionHeaderViewType(viewType)){
viewHolder = onCreateSectionHeaderViewHolder(parent, viewType);
}else if(isSectionFooterViewType(viewType)){
viewHolder = onCreateSectionFooterViewHolder(parent, viewType);
}else{
viewHolder = onCreateItemViewHolder(parent, viewType);
}

return viewHolder;
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
int section = sectionForPosition[position];
int index = positionWithinSection[position];

if(isSectionHeaderPosition(position)){
onBindSectionHeaderViewHolder((H) holder, section);
}else if(isSectionFooterPosition(position)){
onBindSectionFooterViewHolder((F) holder, section);
}else{
onBindItemViewHolder((VH) holder, section, index);
}

}

@Override
public int getItemViewType(int position) {

if(sectionForPosition == null){
setupIndices();
}

int section = sectionForPosition[position];
int index = positionWithinSection[position];

if(isSectionHeaderPosition(position)){
return getSectionHeaderViewType(section);
}else if(isSectionFooterPosition(position)){
return getSectionFooterViewType(section);
}else{
return getSectionItemViewType(section, index);
}

}

protected int getSectionHeaderViewType(int section){
return TYPE_SECTION_HEADER;
}

protected int getSectionFooterViewType(int section){
return TYPE_SECTION_FOOTER;
}

protected int getSectionItemViewType(int section, int position){
return TYPE_ITEM;
}

/**
* Returns true if the argument position corresponds to a header
*/
public boolean isSectionHeaderPosition(int position){
if(isHeader == null){
setupIndices();
}
return isHeader[position];
}

/**
* Returns true if the argument position
b423
corresponds to a footer
*/
public boolean isSectionFooterPosition(int position){
if(isFooter == null){
setupIndices();
}
return isFooter[position];
}

protected boolean isSectionHeaderViewType(int viewType){
return viewType == TYPE_SECTION_HEADER;
}

protected boolean isSectionFooterViewType(int viewType){
return viewType == TYPE_SECTION_FOOTER;
}

/**
* Returns the number of sections in the RecyclerView
*/
protected abstract int getSectionCount();

/**
* Returns the number of items for a given section
*/
protected abstract int getItemCountForSection(int section);

/**
* Returns true if a given section should have a footer
*/
protected abstract boolean hasFooterInSection(int section);

/**
* Creates a ViewHolder of class H for a Header
*/
protected abstract H  onCreateSectionHeaderViewHolder(ViewGroup parent, int viewType);

/**
* Creates a ViewHolder of class F for a Footer
*/
protected abstract F  onCreateSectionFooterViewHolder(ViewGroup parent, int viewType);

/**
* Creates a ViewHolder of class VH for an Item
*/
protected abstract VH onCreateItemViewHolder(ViewGroup parent, int viewType);

/**
* Binds data to the header view of a given section
*/
protected abstract void onBindSectionHeaderViewHolder(H holder, int section);

/**
* Binds data to the footer view of a given section
*/
protected abstract void onBindSectionFooterViewHolder(F holder, int section);

/**
* Binds data to the item view for a given position within a section
*/
protected abstract void onBindItemViewHolder(VH holder, int section, int position);

class SectionDataObserver extends RecyclerView.AdapterDataObserver{
@Override
public void onChanged() {
setupIndices();
}

@Override
public void onItemRangeChanged(int positionStart, int itemCount) {
setupIndices();
}

@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
setupIndices();
}

@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
setupIndices();
}

@Override
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
setupIndices();
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息