快速实现RecycleView的网格和瀑布流布局添加头部(尾部原理一样,故略之)
2017-03-08 21:59
435 查看
RecycleView高度的解耦,非常灵活,通过布局管理器LayoutManager控制其显示的布局;通过ItemDecoration控制Item间的间隔;通过ItemAnimator控制Item增删的动画;不过需要自己写接口实现点击、长按事件。它只管回收与复用View。 然后最坑爹的是没了addHeader和addFooter,需要自己实现(怨念中…),自己找开源的代码发现稍微有点繁琐,于是自己动手研究利用Manager的特性还有LayoutParams实 现了添加头尾等操作,接下来介绍GridLayoutManager和StaggeredGridLayoutManager怎么添加头部,LinearLayout太简单了就不介绍了。①GridLayoutManager添加头部操作网格布局配置:关键在于继承GridLayoutManager.SpanSizeLookup实现HeaderSpanSizeLookup来处理指定位置占多少列的操作来实现添加头部
GridLayoutManager glm = new GridLayoutManager(this, 2); glm.setSpanSizeLookup(new HeaderSpanSizeLookup(glm)); recyclerView.setLayoutManager(glm); recyclerView.addItemDecoration(new SpacesItemDecoration(5)); recyclerView.setAdapter(new LayoutAdapter());HeaderSpanSizeLookup 在getSpanSize方法这里通过position=0来设置该位置的view占满getSpanCount即我们设置的两列
class HeaderSpanSizeLookup extends GridLayoutManager.SpanSizeLookup { private final GridLayoutManager layoutManager; public HeaderSpanSizeLookup(GridLayoutManager layoutManager) { this.layoutManager = layoutManager; } @Override public int getSpanSize(int position) { position = position == 0 ? layoutManager.getSpanCount() : 1; return position; } }LayoutAdapter 的实现跟瀑布流的大同小异,细节注释去看下面的瀑布流即可
class LayoutAdapter extends RecyclerView.Adapter<LayoutAdapter.SimpleViewHolder> { private static final int TYPE_HEADER = 1, TYPE_ITEM = 2; class SimpleViewHolder extends RecyclerView.ViewHolder { ImageView header; ImageView item; public SimpleViewHolder(View view) { super(view); header = (ImageView) view.findViewById(R.id.iv_header); item = (ImageView) view.findViewById(R.id.iv_item); } } @Override public SimpleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = null; switch (viewType) { case TYPE_HEADER: view = LayoutInflater.from(GridLayoutManagerActivity.this).inflate(R.layout.header, parent, false); break; case TYPE_ITEM: view = LayoutInflater.from(GridLayoutManagerActivity.this).inflate(R.layout.item, parent, false); break; } return new SimpleViewHolder(view); } int size[] = new int[2]; @Override public void onBindViewHolder(final SimpleViewHolder holder, int position) { switch (holder.getItemViewType()) { case TYPE_HEADER: FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) holder.header.getLayoutParams(); size = getImageRealSize(R.drawable.ic_girls_banner); lp.width = metrics.widthPixels; lp.height = lp.width * size[1] / size[0]; holder.header.setLayoutParams(lp); Glide.with(GridLayoutManagerActivity.this). load(R.drawable.ic_girls_banner). asBitmap(). diskCacheStrategy(DiskCacheStrategy.ALL). into(new BitmapImageViewTarget(holder.header) { @Override public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) { super.onResourceReady(resource, glideAnimation); Palette.from(resource).generate(new Palette.PaletteAsyncListener() { @Override public void onGenerated(Palette palette) { ((CardView)holder.header.getParent()).setCardBackgroundColor(palette.getMutedColor(Color.parseColor("#ffffff"))); } }); } }); break; case TYPE_ITEM: final ViewGroup.LayoutParams lp1 = (ViewGroup.LayoutParams) holder.item.getLayoutParams(); size = getImageRealSize(mItems.get(position - 1)); lp1.width = metrics.widthPixels / 2; lp1.height = metrics.widthPixels / 2 * size[1] / size[0]; holder.item.setLayoutParams(lp1); Glide.with(GridLayoutManagerActivity.this). load(mItems.get(position - 1)). asBitmap(). diskCacheStrategy(DiskCacheStrategy.ALL). into(new BitmapImageViewTarget(holder.item) { @Override public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) { super.onResourceReady(resource, glideAnimation); Palette.from(resource).generate(new Palette.PaletteAsyncListener() { @Override public void onGenerated(Palette palette) { ((CardView) holder.item.getParent()).setCardBackgroundColor(palette.getMutedColor(Color.parseColor("#ffffff"))); } }); } }); break; } } @Override public int getItemViewType(int position) { return position = position == 0 ? TYPE_HEADER : TYPE_ITEM; } @Override public int getItemCount() { return mItems.size() + 1; } }②StaggeredGridLayoutManager添加头部操作首先按正常的配置配,这里我配的是两列,垂直
StaggeredGridLayoutManager sglm = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); recyclerView.setLayoutManager(sglm); recyclerView.addItemDecoration(new SpacesItemDecoration(5)); recyclerView.setAdapter(new LayoutAdapter()); SpacesItemDecoration是继承ItemDecoration用来设间隔的
class SpacesItemDecoration extends RecyclerView.ItemDecoration { private int space; private StaggeredGridLayoutManager.LayoutParams lp; public SpacesItemDecoration(int space) { this.space = space; } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { int position = parent.getChildAdapterPosition(view); outRect.left = space; outRect.right = space; outRect.bottom = space; if (position == 0) { // 用于设第一个位置跟顶部的距离 outRect.top = space; } else { lp = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams(); if (lp.getSpanIndex() == 0) { // 用于设同行两个间隔间跟其距离左右屏幕间隔相同 outRect.right = 0; } } } }
最关键的是Adapter的实现了, 头部是利用StaggeredGridLayoutManager.LayoutParams.setFullSpan(true)来实现占满两列的
class LayoutAdapter extends RecyclerView.Adapter<LayoutAdapter.SimpleViewHolder> { // view的type:头部,内容 private static final int TYPE_HEADER = 1, TYPE_ITEM = 2; class SimpleViewHolder extends RecyclerView.ViewHolder { // 头部的控件 ImageView header; CardView cardview; // 内容的控件 ImageView item; public SimpleViewHolder(View view) { super(view); header = (ImageView) view.findViewById(R.id.iv_header); item = (ImageView) view.findViewById(R.id.iv_item); cardview = (CardView) view.findViewById(R.id.cardview); } } @Override public int getItemViewType(int position) { // 位置为0的话类型为头部,其余为内容 return position = position == 0 ? TYPE_HEADER : TYPE_ITEM; } @Override public int getItemCount() { // 加一是因为添加了头部 return mItems.size() + 1; } @Override public SimpleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = null; switch (viewType) { // 根据类型填充不同布局 case TYPE_HEADER: view = LayoutInflater.from(MainActivity.this).inflate(R.layout.header, parent, false); break; case TYPE_ITEM: view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item, parent, false); break; } return new SimpleViewHolder(view); } int size[] = new int[2]; @Override public void onBindViewHolder(final SimpleViewHolder holder, int position) { switch (holder.getItemViewType()) { case TYPE_HEADER: // 根据view类型处理不同的操作 // 获取cardview的布局属性,记住这里要是布局的最外层的控件的布局属性,如果是里层的会报cast错误 StaggeredGridLayoutManager.LayoutParams clp = (StaggeredGridLayoutManager.LayoutParams) holder.cardview.getLayoutParams(); // 最最关键一步,设置当前view占满列数,这样就可以占据两列实现头部了 clp.setFullSpan(true); FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) holder.header.getLayoutParams(); // 获取资源图片宽高,用于等比例加载图片 size = getImageRealSize(R.drawable.ic_girls_banner); // 宽度为屏幕宽度 lp.width = metrics.widthPixels; // 等比例得到高度 lp.height = lp.width * size[1] / size[0]; holder.header.setLayoutParams(lp); Glide.with(MainActivity.this). load(R.drawable.ic_girls_banner). asBitmap(). diskCacheStrategy(DiskCacheStrategy.ALL). into(new BitmapImageViewTarget(holder.header) { @Override public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) { super.onResourceReady(resource, glideAnimation); Palette.from(resource).generate(new Palette.PaletteAsyncListener() { @Override public void onGenerated(Palette palette) { /*Palette这个类中提取以下突出的颜色: Vibrant(有活力) Vibrant dark(有活力 暗色) Vibrant light(有活力 亮色) Muted (柔和) Muted dark(柔和 暗色) Muted light(柔和 亮色)*/ holder.cardview.setCardBackgroundColor(palette.getMutedColor(Color.parseColor("#ffffff"))); } }); } }); break; case TYPE_ITEM: final ViewGroup.LayoutParams lp1 = (ViewGroup.LayoutParams) holder.item.getLayoutParams(); size = getImageRealSize(mItems.get(position - 1)); lp1.width = metrics.widthPixels / 2; lp1.height = metrics.widthPixels / 2 * size[1] / size[0]; holder.item.setLayoutParams(lp1); Glide.with(MainActivity.this). load(mItems.get(position - 1)). asBitmap(). diskCacheStrategy(DiskCacheStrategy.ALL). into(new BitmapImageViewTarget(holder.item) { @Override public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) { super.onResourceReady(resource, glideAnimation); Palette.from(resource).generate(new Palette.PaletteAsyncListener() { @Override public void onGenerated(Palette palette) { ((CardView) holder.item.getParent()).setCardBackgroundColor(palette.getMutedColor(Color.parseColor("#ffffff"))); } }); } }); break; } } }
头部布局
<?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/cardview" android:layout_width="wrap_content" android:layout_height="wrap_content" app:cardCornerRadius="8dp" app:cardElevation="5dp" app:contentPadding="1dp"> <ImageView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/iv_header" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="#44000000"> </ImageView> </android.support.v7.widget.CardView>
内容布局
<?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/cardview" android:layout_width="wrap_content" android:layout_height="wrap_content" app:cardCornerRadius="8dp" app:cardElevation="5dp" app:contentPadding="1dp"> <ImageView android:id="@+id/iv_item" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="#44000000"> </ImageView> </android.support.v7.widget.CardView>
由上可以看到,GridLayoutManager是通过GridLayoutManager.SpanSizeLookup来控制占的列数来实现头部添加的; StaggeredGridLayoutManager是通过在Adapter的oncreateView那里判断ViewType为头部类型的时候,获取布局最外层控件的StaggeredGridLayoutManager.LayoutParams,调用StaggeredGridLayoutManager.LayoutParams.setFullSpan(true)来控制占的列数来实现头部添加的。
看完了是不是觉得so easy呢?只要掌握了这个,任意添加不同布局,控制占的不同列数,我们可以实现各种各样的需求,我想这就是RecycleView取代listview和gridview的必杀技。讲解完毕,谢谢!
附图片:[align=left] [/align][align=left] [/align][align=left] [/align][align=left] [/align]
附demo如下:http://download.csdn.net/detail/oushangfeng123/8993489
相关文章推荐
- 快速实现RecycleView的网格和瀑布流布局添加头部(尾部原理一样,故略之)
- 快速实现RecycleView的网格和瀑布流布局添加头部(尾部原理一样,故略之)
- RecyclerView添加头尾部,支持网格和瀑布流布局
- RecyclerView添加头尾部,支持网格和瀑布流布局
- RecycleView添加头部尾部原理笔记
- iOS UICollectionView学习之二,纯代码实现布局,纯代码实现添加Section Header头部视图和 Section Footer尾部视图
- RecycleView在中间“插入”布局以及添加头部的姿势
- RecycleView实现瀑布流添加分隔线
- Android RecyclerView瀑布流布局添加Footer实现上拉加载
- RecyclerView 添加头部和尾部,并实现Item的点击事件
- Recycleview实现复杂页面三种以上布局 瀑布流多布 scrollview嵌套recyclerView 显示不全滑动冲突
- RecyclerView 添加头部和尾部布局
- Android RecyclerView瀑布流布局添加Footer实现上拉加载
- 给RecyclerView添加头部和尾部最简单的实现方式
- RecyclerView 添加头部和尾部布局
- (4.1.45.1)RecyclerView 添加头部和尾部布局: 仿HeaderViewListAdapter装饰器模式
- RecyclerView 添加head view头部 和foot view尾部、设置水平horizontal时左侧布局和右侧布局
- RecyclerView封装——添加Header、Footer(在网格瀑布流布局中独占一行)
- 简单粗暴实现RecycleView的瀑布流的粘性头部(非ItemDecoration实现)
- Recycleview实现复杂页面 三种以上布局 瀑布流 多布局 scrollview嵌套recyclerView 显示不全 滑动冲突 之进阶终极篇