快速实现RecycleView的网格和瀑布流布局添加头部(尾部原理一样,故略之)

 

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的必杀技。讲解完毕,谢谢!
附图片:
 
这里写图片描述
 
 
 
 
这里写图片描述
 
 
 
 
这里写图片描述
 
 
 
 
这里写图片描述
 
 
 
附demo如下: 
http://download.csdn.net/detail/oushangfeng123/8993489
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个示例代码,用于实现一个简单的新闻列表: 1. 布局文件 `activity_main.xml`: ```xml <?xml version="1.0" encoding="utf-8"?> <androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" /> ``` 2. 新闻项的布局文件 `item_news.xml`: ```xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="16dp"> <TextView android:id="@+id/text_view_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="18sp" android:textStyle="bold" /> <TextView android:id="@+id/text_view_content" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:textSize="14sp" /> </LinearLayout> ``` 3. 新闻项的数据类 `NewsItem.java`: ```java public class NewsItem { private String title; private String content; public NewsItem(String title, String content) { this.title = title; this.content = content; } public String getTitle() { return title; } public String getContent() { return content; } } ``` 4. 适配器类 `NewsAdapter.java`: ```java public class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.NewsViewHolder> { private List<NewsItem> newsList; public NewsAdapter(List<NewsItem> newsList) { this.newsList = newsList; } @NonNull @Override public NewsViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_news, parent, false); return new NewsViewHolder(itemView); } @Override public void onBindViewHolder(@NonNull NewsViewHolder holder, int position) { NewsItem item = newsList.get(position); holder.titleTextView.setText(item.getTitle()); holder.contentTextView.setText(item.getContent()); } @Override public int getItemCount() { return newsList.size(); } public static class NewsViewHolder extends RecyclerView.ViewHolder { public TextView titleTextView; public TextView contentTextView; public NewsViewHolder(View itemView) { super(itemView); titleTextView = itemView.findViewById(R.id.text_view_title); contentTextView = itemView.findViewById(R.id.text_view_content); } } } ``` 5. 在 MainActivity 中设置 RecycleView 的适配器和布局管理器: ```java public class MainActivity extends AppCompatActivity { private RecyclerView recyclerView; private NewsAdapter newsAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); recyclerView = findViewById(R.id.recycler_view); // 设置布局管理器 LinearLayoutManager layoutManager = new LinearLayoutManager(this); recyclerView.setLayoutManager(layoutManager); // 设置适配器 List<NewsItem> newsList = generateNewsList(); // 生成新闻列表 newsAdapter = new NewsAdapter(newsList); recyclerView.setAdapter(newsAdapter); } private List<NewsItem> generateNewsList() { List<NewsItem> newsList = new ArrayList<>(); newsList.add(new NewsItem("新闻标题1", "新闻内容1")); newsList.add(new NewsItem("新闻标题2", "新闻内容2")); newsList.add(new NewsItem("新闻标题3", "新闻内容3")); return newsList; } } ``` 以上代码中,`generateNewsList()` 方法用于生成一个包含三个新闻项的列表。在 `onCreate()` 方法中,我们使用 `LinearLayoutManager` 设置 RecycleView 的布局管理器,使用 `NewsAdapter` 设置 RecycleView 的适配器,并将新闻列表传递给适配器。适配器中的 `onBindViewHolder()` 方法用于将每个新闻项的数据绑定到对应的视图上。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值