extends:http://blog.csdn.net/wzlyd1/article/details/52292548android
一直在鸿洋大神的安卓群里水群,渐渐的loader和安卓弟等人都成长了起来,还记得当初他们清纯的模样。小L在群里不水了,安卓弟成长为CTO了,只有我依然默默无闻,因而决定再写博客了,以前不写,一是由于工做比较忙,二是由于我水平有限,简单的不想写,由于写了也没用,网上demo不少,难的本身也没多高的造诣,写也写不出来,因此一直都是处于“半荒废状态”,固然说到底其实仍是由于懒,因而今天我再次执笔,将学到的东西所有记录下来。git
先上效果图给你们看看,好有一个总体的认识github
效果就是这样的,可是不只仅局限于这种布局,事实上只要是三段式布局,均可以经过该demo的学习来实现,什么是三段式布局呢,就是有header -content-footer类型的布局,画一个图来解释算法
好比下面这个图就能够 json
能够看到,用途仍是很普遍的,因此很须要咱们去学习一下数据结构
gitbub上有一个很牛逼的类,可是貌似知道的人不多,名字叫作SectionedRecyclerViewAdapter ,可是今天咱们不去研究她是怎么实现的,咱们来研究他怎么用就好了app
/** * Created by lyd10892 on 2016/8/23. */ public class HotelEntityAdapter extends SectionedRecyclerViewAdapter<HeaderHolder, DescHolder, RecyclerView.ViewHolder> { public ArrayList<HotelEntity.TagsEntity> allTagList; private Context mContext; private LayoutInflater mInflater; private SparseBooleanArray mBooleanMap;//记录下哪一个section是被打开的 public HotelEntityAdapter(Context context) { mContext = context; mInflater = LayoutInflater.from(context); mBooleanMap = new SparseBooleanArray(); } public void setData(ArrayList<HotelEntity.TagsEntity> allTagList) { this.allTagList = allTagList; notifyDataSetChanged(); } @Override protected int getSectionCount() { return HotelUtils.isEmpty(allTagList) ? 0 : allTagList.size(); } @Override protected int getItemCountForSection(int section) { int count = allTagList.get(section).tagInfoList.size(); if (count >= 8 && !mBooleanMap.get(section)) { count = 8; } return HotelUtils.isEmpty(allTagList.get(section).tagInfoList) ? 0 : count; } //是否有footer布局 @Override protected boolean hasFooterInSection(int section) { return false; } @Override protected HeaderHolder onCreateSectionHeaderViewHolder(ViewGroup parent, int viewType) { return new HeaderHolder(mInflater.inflate(R.layout.hotel_title_item, parent, false)); } @Override protected RecyclerView.ViewHolder onCreateSectionFooterViewHolder(ViewGroup parent, int viewType) { return null; } @Override protected DescHolder onCreateItemViewHolder(ViewGroup parent, int viewType) { return new DescHolder(mInflater.inflate(R.layout.hotel_desc_item, parent, false)); } @Override protected void onBindSectionHeaderViewHolder(final HeaderHolder holder, final int section) { holder.openView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { boolean isOpen = mBooleanMap.get(section); String text = isOpen ? "展开" : "关闭"; mBooleanMap.put(section, !isOpen); holder.openView.setText(text); notifyDataSetChanged(); } }); holder.titleView.setText(allTagList.get(section).tagsName); holder.openView.setText(mBooleanMap.get(section) ? "关闭" : "展开"); } @Override protected void onBindSectionFooterViewHolder(RecyclerView.ViewHolder holder, int section) { } @Override protected void onBindItemViewHolder(DescHolder holder, int section, int position) { holder.descView.setText(allTagList.get(section).tagInfoList.get(position).tagName); } }
这里面有几个很重要的方法也是须要咱们必须重写的,是咱们实现效果的关键ide
protected abstract int getSectionCount(); protected abstract int getItemCountForSection(int section); protected abstract boolean hasFooterInSection(int section); protected abstract H onCreateSectionHeaderViewHolder(ViewGroup parent, int viewType); protected abstract F onCreateSectionFooterViewHolder(ViewGroup parent, int viewType); protected abstract VH onCreateItemViewHolder(ViewGroup parent, int viewType); protected abstract void onBindSectionHeaderViewHolder(H holder, int section); protected abstract void onBindSectionFooterViewHolder(F holder, int section); protected abstract void onBindItemViewHolder(VH holder, int section, int position);
接下来咱们详细分析这几个方法存在的具体意义
不过在说以前咱们须要看一下咱们的数据结构,这个也很重要布局
public class HotelEntity { /** * 要注意这个类的数据结构,很重要,直接决定了咱们能不能实现分组展现 */ public ArrayList<TagsEntity> allTagsList; public class TagsEntity { public String tagsName; public ArrayList<TagInfo> tagInfoList; public class TagInfo { public String tagName; } } }
这个方法主要是用来计算咱们一共有多少个section须要展现,返回值是咱们最外称list的大小,在咱们的示例图中,对应的为热门品牌—商业区—热门景点 等,对应的数据是咱们的allTagList学习
protected abstract int getSectionCount();
这个方法是用来展现content内容区域,返回值是咱们须要展现多少内容,在本例中,咱们超过8条数据只展现8条,点击展开后就会展现所有数据,逻辑就在这里控制。 对应数据结构为tagInfoList
protected abstract int getItemCountForSection(int section);
判断是否须要底部footer布局,在该例中,咱们并不须要显示footer,因此默认返回false就能够,若是你对应的section须要展现footer布局,那么就在对应的section返回true就好了
protected abstract boolean hasFooterInSection(int section);
咱们要单独说一下这个方法,这里有一个section和position ,有些人可能会弄混
section是区域,也就是咱们最外层的index,position是每一个section对应的内容数据的position
@Override protected void onBindItemViewHolder(DescHolder holder, int section, int position) { holder.descView.setText(allTagList.get(section).tagInfoList.get(position).tagName); }
至于下面的onCreateViewHolder ,onBindViewHolder很少作解释了,若是你用过recyclerView,使用方法是同样的,无非是渲染布局,绑定数据
public class MainActivity extends AppCompatActivity { private RecyclerView mRecyclerView; private HotelEntityAdapter mAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } private void initView() { mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView); mAdapter = new HotelEntityAdapter(this); GridLayoutManager manager = new GridLayoutManager(this,4);//咱们须要网格式的布局 //设置header占据的空间 manager.setSpanSizeLookup(new SectionedSpanSizeLookup(mAdapter,manager)); mRecyclerView.setLayoutManager(manager); mRecyclerView.setAdapter(mAdapter); HotelEntity entity = JsonUtils.analysisJsonFile(this,"json"); mAdapter.setData(entity.allTagsList); } }
代码里有一段很重要的注释,设置header占据的空间,没错,由于咱们要仿造header的效果,咱们设置的manager是GridLayoutManager,设置的每一行item数量是4,若是不重写该方法,那么header显示就会出错,核心代码以下:
/** * A SpanSizeLookup to draw section headers or footer spanning the whole width of the RecyclerView * when using a GridLayoutManager * * 这个类是用来自定义每一个item须要占据的空间 * * */ 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) { //header和footer占据的是全屏 if(adapter.isSectionHeaderPosition(position) || adapter.isSectionFooterPosition(position)){ return layoutManager.getSpanCount(); }else{ return 1;//其余默认1 } } }
最重要的是getSpanSize方法,只要是header或者是footer就返回咱们设置的网格数,也就是4,表明header和footer占据4个网格的空间,其余占据1个
这样,咱们就能够完美的展现咱们须要的布局了
当前咱们的demo是网格布局的,你也能够设置流式布局,只须要设置不一样的layoutmanager就能够了
好比下图的效果咱们也能够实现
核心代码已经解释完毕,固然最核心的是SectionedRecyclerViewAdapter这个类,这个类好好学习一下,会学到不少,也会实现不少app常见的布局效果,好比设置不一样的viewType展示更复杂的布局
最后,看一下代码结构:
最后啰嗦一句,写博客比写代码难多了。
demo已经上传到github了,欢迎fork
https://github.com/nbwzlyd/SectionRecyclerViewDemo