快速實現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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章