Android RecyclerView 复杂表格的实现

前言

     RecyclerView 是 Android 中非常受欢迎的控件,谷歌官方在 Android5.0 之后新添加的控件,用以替代传统的 ListView 和 GridView 列表控件,适用于大数据展示,相对于传统的 ListView, RecyclerView 的效率更高,更加强大而灵活,这里简单说一下二者的区别

     1 布局

         ListView:布局单一,只支持竖直方向滑动,而 RecyclerView 支持多种布局  线性布局,实现横向纵向列表方向的 item, 网格布局,指定 item 的数替代 GridView 。瀑布流布局,指定列表方向,以及同方向的item数量

     2 刷新

       ListView 中通常刷新数据 notifyDataSetChanged() ,即全局刷新,每个 item的 数据都会重新加载,资源消耗较大,当然 ListView 也可以实现局部刷新 而 RecyclerView  可以通过 notifyItemChanged() 来实现局部刷新 效率更高

    3 ViewHolder

       ListView 中 VewHolder 需要自定义,需要用 getview 去获取控件,则每次调用 getview 都要通过 findViewById 去获取控件, 如果控件个数过多,会严重影响性能 ,因为findViewById相对比较耗时,所以我们需要创建自定义 ViewHolder,通过getTag 和 setTag 直接获取 view 而 RecyclerView 继承 RecyclerView .ViewHolder,默认需要重写 RecyclerView ,已经封装好

    4 缓存机制

       ListView 仅支持二级缓存,即通过两级缓存View,而 RecyclerView 有四级缓存可支持自定义缓存,具体区请自行度娘

正文

          先看效果,即实现一个自定义的食谱列表 横向 包括星期,竖向包括早中晚三餐 以及相应食谱列表展示

           

 首先先导入RecyclerView 与 Gson 解析依赖包

    implementation 'androidx.recyclerview:recyclerview:1.1.0-beta01'
    implementation 'com.google.code.gson:gson:2.2.2'

 再其次就是需要用到的数据,这里我首先新建个 assets 资源文件夹 如图

                                                 

 assets 创建方法 如图

                                      

紧接着就是创建资源数据 在 assets  文件夹下创建一个data.json 用来存放数据资源 直接右键 new File 文件名以 .json 结尾 即可

{
  "status": "Y",
  "msg": "获取成功",
  "data": [
    {
      "week": "星期一",
      "dinner": [
        {
          "dinnername": "早点",
          "list": [
            {
              "DishName": "包子"
            },
            {
              "DishName": "窝窝头"
            },
            {
              "DishName": "白面膜"
            }
          ]
        },
        {
          "dinnername": "午点",
          "list": [
            {
              "DishName": "栗子"
            },
            {
              "DishName": "苹果"
            },
            {
              "DishName": "西瓜"
            }
          ]
        },
        {
          "dinnername": "晚餐",
          "list": [
            {
              "DishName": "鸡蛋灌饼"
            },
            {
              "DishName": "烤冷面"
            }
          ]
        },
        {
          "dinnername": "早餐",
          "list": [
            {
              "DishName": "花生米"
            },
            {
              "DishName": "白开水"
            },
            {
              "DishName": "西蓝花"
            }
          ]
        },
        {
          "dinnername": "午餐",
          "list": [
            {
              "DishName": "西红柿鸡蛋"
            },
            {
              "DishName": "宫爆鸡丁"
            },
            {
              "DishName": "火爆猪心"
            }
          ]
        }
      ]
    },
    {
      "week": "星期二",
      "dinner": [
        {
          "dinnername": "早点",
          "list": [
            {
              "DishName": "纯牛奶"
            },
            {
              "DishName": "酸奶"
            },
            {
              "DishName": "优酸乳"
            }
          ]
        },
        {
          "dinnername": "午点",
          "list": [
            {
              "DishName": "杨桃"
            },
            {
              "DishName": "玉米"
            }
          ]
        },
        {
          "dinnername": "晚餐",
          "list": [
            {
              "DishName": "酸辣粉"
            },
            {
              "DishName": "麻辣烫"
            }
          ]
        },
        {
          "dinnername": "早餐",
          "list": [
            {
              "DishName": "蔬菜粥"
            },
            {
              "DishName": "玉米粥"
            },
            {
              "DishName": "包子"
            }
          ]
        },
        {
          "dinnername": "午餐",
          "list": [
            {
              "DishName": "鲜虾丸子"
            },
            {
              "DishName": "麻婆豆腐"
            }
          ]
        }
      ]
    },
    {
      "week": "星期三",
      "dinner": [
        {
          "dinnername": "早点",
          "list": [
            {
              "DishName": "仙魔奶昔"
            },
            {
              "DishName": "奶酪"
            },
            {
              "DishName": "红薯"
            }
          ]
        },
        {
          "dinnername": "午点",
          "list": [
            {
              "DishName": "火龙果"
            }
          ]
        },
        {
          "dinnername": "晚餐",
          "list": [
            {
              "DishName": "兰州拉面"
            }
          ]
        },
        {
          "dinnername": "早餐",
          "list": [
            {
              "DishName": "八宝粥"
            },
            {
              "DishName": "小花卷"
            }
          ]
        },
        {
          "dinnername": "午餐",
          "list": [
            {
              "DishName": "清蒸鲤鱼"
            },
            {
              "DishName": "水煮肉片"
            }
          ]
        }
      ]
    },
    {
      "week": "星期四",
      "dinner": [
        {
          "dinnername": "早点",
          "list": [
            {
              "DishName": "绿豆饼"
            }
          ]
        },
        {
          "dinnername": "午点",
          "list": [
            {
              "DishName": "海苔"
            }
          ]
        },
        {
          "dinnername": "晚餐",
          "list": []
        },
        {
          "dinnername": "早餐",
          "list": [
            {
              "DishName": "燕麦粥"
            }
          ]
        },
        {
          "dinnername": "午餐",
          "list": [
            {
              "DishName": "娃娃菜"
            },
            {
              "DishName": "猪心肉"
            }
          ]
        }
      ]
    },
    {
      "week": "星期五",
      "dinner": [
        {
          "dinnername": "早点",
          "list": [
            {
              "DishName": "豆浆"
            }
          ]
        },
        {
          "dinnername": "午点",
          "list": [
            {
              "DishName": "干煸鸡翅"
            }
          ]
        },
        {
          "dinnername": "晚餐",
          "list": [
            {
              "DishName": "杀猪菜"
            }
          ]
        },
        {
          "dinnername": "早餐",
          "list": [
            {
              "DishName": "西红柿鸡蛋面"
            }
          ]
        },
        {
          "dinnername": "午餐",
          "list": [
            {
              "DishName": "炒豆芽"
            }
          ]
        }
      ]
    },
    {
      "week": "星期六",
      "dinner": [
        {
          "dinnername": "早点",
          "list": [
            {
              "DishName": "包子"
            }
          ]
        },
        {
          "dinnername": "午点",
          "list": [
            {
              "DishName": "娃娃菜"
            }
          ]
        },
        {
          "dinnername": "晚餐",
          "list": [
            {
              "DishName": "西瓜饼"
            }
          ]
        },
        {
          "dinnername": "早餐",
          "list": [
            {
              "DishName": "油条"
            }
          ]
        },
        {
          "dinnername": "午餐",
          "list": [
            {
              "DishName": "驴肉火烧"
            }
          ]
        }
      ]
    },
    {
      "week": "星期日",
      "dinner": [
        {
          "dinnername": "早点",
          "list": [
            {
              "DishName": "冰镇西瓜"
            }
          ]
        },
        {
          "dinnername": "午点",
          "list": [
            {
              "DishName": "扒肉"
            }
          ]
        },
        {
          "dinnername": "晚餐",
          "list": []
        },
        {
          "dinnername": "早餐",
          "list": [
            {
              "DishName": "小笼包"
            }
          ]
        },
        {
          "dinnername": "午餐",
          "list": [
            {
              "DishName": "大鸡腿"
            }
          ]
        }
      ]
    }
  ]
}

然后就需要创建实体类 FootBean 实现 Parcelable ,Parcelable 是安卓专用的序列化接口,这里不得不先提一下 Serializable 接口,Serializable 是 Java 为我们提供的一个标准化的序列化接口,使用简单,实现即可,而 Parcelable 是 Android 为我们提供的序列化的接口,Parcelable 相对于Serializable 的使用相对复杂一些,重写3个方法

createFromParcel(Parcelparcel)  writeToParcel(Parcel parcel)  newArray(int size)

但 Parcelable 的效率相对 Serializable 也高很多 ,这里多说一嘴解释下什么是序列化,序列化是将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到 Ram 或者是 rom,也就是临时或持久性存储区。以后,可以通过从存储区中读取对象的状态,重新创建该对象,这个就是反序列化。言归正题 由于接口比较复杂 我这里用可视化工具展示下以遍大家理解

层次嵌套有点多,每一层的List 都要实现  Parcelable

public class FootBean implements Parcelable {


    /**
     * status : Y
     * msg : 获取成功
     * data : [{"week":"星期一","dinner":[{"dinnername":"早点","list":[{"DishName":"包子"},{"DishName":"窝窝头"},{"DishName":"白面膜"}]},{"dinnername":"午点","list":[{"DishName":"栗子"},{"DishName":"苹果"},{"DishName":"西瓜"}]},{"dinnername":"晚餐","list":[{"DishName":"鸡蛋灌饼"},{"DishName":"烤冷面"}]},{"dinnername":"早餐","list":[{"DishName":"花生米"},{"DishName":"白开水"},{"DishName":"西蓝花"}]},{"dinnername":"午餐","list":[{"DishName":"西红柿鸡蛋"},{"DishName":"宫爆鸡丁"},{"DishName":"火爆猪心"}]}]},{"week":"星期二","dinner":[{"dinnername":"早点","list":[{"DishName":"纯牛奶"},{"DishName":"酸奶"},{"DishName":"优酸乳"}]},{"dinnername":"午点","list":[{"DishName":"杨桃"},{"DishName":"玉米"}]},{"dinnername":"晚餐","list":[{"DishName":"酸辣粉"},{"DishName":"麻辣烫"}]},{"dinnername":"早餐","list":[{"DishName":"蔬菜粥"},{"DishName":"玉米粥"},{"DishName":"包子"}]},{"dinnername":"午餐","list":[{"DishName":"鲜虾丸子"},{"DishName":"麻婆豆腐"}]}]},{"week":"星期三","dinner":[{"dinnername":"早点","list":[{"DishName":"仙魔奶昔"},{"DishName":"奶酪"},{"DishName":"红薯"}]},{"dinnername":"午点","list":[{"DishName":"火龙果"}]},{"dinnername":"晚餐","list":[{"DishName":"兰州拉面"}]},{"dinnername":"早餐","list":[{"DishName":"八宝粥"},{"DishName":"小花卷"}]},{"dinnername":"午餐","list":[{"DishName":"清蒸鲤鱼"},{"DishName":"水煮肉片"}]}]},{"week":"星期四","dinner":[{"dinnername":"早点","list":[{"DishName":"绿豆饼"}]},{"dinnername":"午点","list":[{"DishName":"海苔"}]},{"dinnername":"晚餐","list":[]},{"dinnername":"早餐","list":[{"DishName":"燕麦粥"}]},{"dinnername":"午餐","list":[{"DishName":"娃娃菜"},{"DishName":"猪心肉"}]}]},{"week":"星期五","dinner":[{"dinnername":"早点","list":[{"DishName":"豆浆"}]},{"dinnername":"午点","list":[{"DishName":"干煸鸡翅"}]},{"dinnername":"晚餐","list":[{"DishName":"杀猪菜"}]},{"dinnername":"早餐","list":[{"DishName":"西红柿鸡蛋面"}]},{"dinnername":"午餐","list":[{"DishName":"炒豆芽"}]}]},{"week":"星期六","dinner":[{"dinnername":"早点","list":[{"DishName":"包子"}]},{"dinnername":"午点","list":[{"DishName":"娃娃菜"}]},{"dinnername":"晚餐","list":[{"DishName":"西瓜饼"}]},{"dinnername":"早餐","list":[{"DishName":"油条"}]},{"dinnername":"午餐","list":[{"DishName":"驴肉火烧"}]}]},{"week":"星期日","dinner":[{"dinnername":"早点","list":[{"DishName":"冰镇西瓜"}]},{"dinnername":"午点","list":[{"DishName":"扒肉"}]},{"dinnername":"晚餐","list":[]},{"dinnername":"早餐","list":[{"DishName":"小笼包"}]},{"dinnername":"午餐","list":[{"DishName":"大鸡腿"}]}]}]
     */

    private String status;
    private String msg;
    private List<DataBean> data;

    @Override
    public String toString() {
        return "FootBean{" +
                "status='" + status + '\'' +
                ", msg='" + msg + '\'' +
                ", data=" + data +
                '}';
    }

    protected FootBean(Parcel in) {
        status = in.readString();
        msg = in.readString();
    }

    public static final Creator<FootBean> CREATOR = new Creator<FootBean>() {
        @Override
        public FootBean createFromParcel(Parcel in) {
            return new FootBean(in);
        }

        @Override
        public FootBean[] newArray(int size) {
            return new FootBean[size];
        }
    };

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public List<DataBean> getData() {
        return data;
    }

    public void setData(List<DataBean> data) {
        this.data = data;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(status);
        dest.writeString(msg);
    }

    public static class DataBean implements Parcelable {
        /**
         * week : 星期一
         * dinner : [{"dinnername":"早点","list":[{"DishName":"包子"},{"DishName":"窝窝头"},{"DishName":"白面膜"}]},{"dinnername":"午点","list":[{"DishName":"栗子"},{"DishName":"苹果"},{"DishName":"西瓜"}]},{"dinnername":"晚餐","list":[{"DishName":"鸡蛋灌饼"},{"DishName":"烤冷面"}]},{"dinnername":"早餐","list":[{"DishName":"花生米"},{"DishName":"白开水"},{"DishName":"西蓝花"}]},{"dinnername":"午餐","list":[{"DishName":"西红柿鸡蛋"},{"DishName":"宫爆鸡丁"},{"DishName":"火爆猪心"}]}]
         */

        private String week;
        private List<DinnerBean> dinner;

        @Override
        public String toString() {
            return "DataBean{" +
                    "week='" + week + '\'' +
                    ", dinner=" + dinner +
                    '}';
        }

        protected DataBean(Parcel in) {
            week = in.readString();
        }

        public static final Creator<DataBean> CREATOR = new Creator<DataBean>() {
            @Override
            public DataBean createFromParcel(Parcel in) {
                return new DataBean(in);
            }

            @Override
            public DataBean[] newArray(int size) {
                return new DataBean[size];
            }
        };

        public String getWeek() {
            return week;
        }

        public void setWeek(String week) {
            this.week = week;
        }

        public List<DinnerBean> getDinner() {
            return dinner;
        }

        public void setDinner(List<DinnerBean> dinner) {
            this.dinner = dinner;
        }

        @Override
        public int describeContents() {
            return 0;
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(week);
        }

        public static class DinnerBean implements Parcelable {
            /**
             * dinnername : 早点
             * list : [{"DishName":"包子"},{"DishName":"窝窝头"},{"DishName":"白面膜"}]
             */

            private String dinnername;
            private List<ListBean> list;

            @Override
            public String toString() {
                return "DinnerBean{" +
                        "dinnername='" + dinnername + '\'' +
                        ", list=" + list +
                        '}';
            }

            protected DinnerBean(Parcel in) {
                dinnername = in.readString();
            }

            public static final Creator<DinnerBean> CREATOR = new Creator<DinnerBean>() {
                @Override
                public DinnerBean createFromParcel(Parcel in) {
                    return new DinnerBean(in);
                }

                @Override
                public DinnerBean[] newArray(int size) {
                    return new DinnerBean[size];
                }
            };

            public String getDinnername() {
                return dinnername;
            }

            public void setDinnername(String dinnername) {
                this.dinnername = dinnername;
            }

            public List<ListBean> getList() {
                return list;
            }

            public void setList(List<ListBean> list) {
                this.list = list;
            }

            @Override
            public int describeContents() {
                return 0;
            }

            @Override
            public void writeToParcel(Parcel dest, int flags) {
                dest.writeString(dinnername);
            }

            public static class ListBean implements Parcelable {
                /**
                 * DishName : 包子
                 */

                private String DishName;

                @Override
                public String toString() {
                    return "ListBean{" +
                            "DishName='" + DishName + '\'' +
                            '}';
                }

                protected ListBean(Parcel in) {
                    DishName = in.readString();
                }

                public static final Creator<ListBean> CREATOR = new Creator<ListBean>() {
                    @Override
                    public ListBean createFromParcel(Parcel in) {
                        return new ListBean(in);
                    }

                    @Override
                    public ListBean[] newArray(int size) {
                        return new ListBean[size];
                    }
                };

                public String getDishName() {
                    return DishName;
                }

                public void setDishName(String DishName) {
                    this.DishName = DishName;
                }

                @Override
                public int describeContents() {
                    return 0;
                }

                @Override
                public void writeToParcel(Parcel dest, int flags) {
                    dest.writeString(DishName);
                }
            }
        }
    }
}

接下来 就是给 RecyclerView 添加自定义分割线  新建一个 DividerItemDecoration 类继承RecyclerView.ItemDecoration

public class DividerItemDecoration extends RecyclerView.ItemDecoration {
    private Paint mPaint;
    //取名mDivider似乎更恰当
    private Drawable mDrawable;
    //分割线高度,默认为1px
    private int mDividerHeight = 2;
    //列表的方向
    private int mOrientation;
    //系统自带的参数
    private static final int[] ATTRS = new int[]{android.R.attr.listDivider};
    //水平
    public static final int HORIZONTAL_LIST = RecyclerView.HORIZONTAL;
    //垂直
    public static final int VERTICAL_LIST = RecyclerView.VERTICAL;
    //水平+垂直
    public static final int BOTH_SET = 2;


    /**
     * 默认分割线:高度为2px,颜色为灰色
     *
     * @param context     上下文
     * @param orientation 列表方向
     */
    public DividerItemDecoration(Context context, int orientation) {
        this.setOrientation(orientation);
        //获取xml配置的参数
        final TypedArray a = context.obtainStyledAttributes(ATTRS);
        //typedArray.getDrawable(attr)这句是说我们可以通过我们的资源获得资源,使用我们的资源名attr去获得资源id
        //看不懂就用自己写一个分割线的图片吧,方法:ContextCompat.getDrawable(context, drawableId);
        mDrawable = a.getDrawable(0);
        //官方的解释是:回收TypedArray,以便后面重用。在调用这个函数后,你就不能再使用这个TypedArray。
        //在TypedArray后调用recycle主要是为了缓存。
        a.recycle();
    }

    /**
     * 自定义分割线
     *
     * @param context     上下文
     * @param orientation 列表方向
     * @param drawableId  分割线图片
     */
    public DividerItemDecoration(Context context, int orientation, int drawableId) {
        this.setOrientation(orientation);
        //旧的getDrawable方法弃用了,这个是新的
        mDrawable = ContextCompat.getDrawable(context, drawableId);
        mDividerHeight = mDrawable.getIntrinsicHeight();
    }

    /**
     * 自定义分割线
     *
     * @param context       上下文
     * @param orientation   列表方向
     * @param dividerHeight 分割线高度
     * @param dividerColor  分割线颜色
     */
    public DividerItemDecoration(Context context, int orientation, int dividerHeight, int dividerColor) {
        this.setOrientation(orientation);
        mDividerHeight = dividerHeight;
        Log.e("mDividerHeight", mDividerHeight + "===================");
        //抗锯齿画笔
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(dividerColor);
        //填满颜色
        mPaint.setStyle(Paint.Style.FILL);
    }

    /**
     * 设置方向
     *
     * @param orientation
     */
    public void setOrientation(int orientation) {
        if (orientation < 0 || orientation > 2)
            throw new IllegalArgumentException("invalid orientation");
        mOrientation = orientation;
    }


    /**
     * 绘制分割线之后,需要留出一个外边框,就是说item之间的间距要换一下
     *
     * @param outRect outRect.set(0, 0, 0, 0);的四个参数理解成margin就好了
     * @param view    视图
     * @param parent  父级view
     * @param state
     */
    @Override
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        //下面super...代码其实调用的就是那个过时的getItemOffsets,也就是说这个方法体内容也可以通通移到那个过时的getItemOffsets中
        super.getItemOffsets(outRect, view, parent, state);
        //获取layoutParams参数
        RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
        //当前位置
        int itemPosition = layoutParams.getViewLayoutPosition();
        //ItemView数量
        int childCount = parent.getAdapter().getItemCount();
        switch (mOrientation) {
            case BOTH_SET:
                //获取Layout的相关参数
                int spanCount = this.getSpanCount(parent);
                if (isLastRaw(parent, itemPosition, spanCount, childCount)) {
                    // 如果是最后一行,则不需要绘制底部
                    outRect.set(0, 0, mDividerHeight, 0);
                } else if (isLastColum(parent, itemPosition, spanCount, childCount)) {
                    // 如果是最后一列,则不需要绘制右边
                    outRect.set(0, 0, 0, mDividerHeight);
                } else {
                    outRect.set(0, 0, mDividerHeight, mDividerHeight);
                }
                break;
            case VERTICAL_LIST:
                childCount -= 1;
                //水平布局右侧留Margin,如果是最后一列,就不要留Margin了
                outRect.set(0, 0, (itemPosition != childCount) ? mDividerHeight : 0, 0);
                break;
            case HORIZONTAL_LIST:
                childCount -= 1;
                //垂直布局底部留边,最后一行不留
                outRect.set(0, 0, 0, (itemPosition != childCount) ? mDividerHeight : 0);
                break;
        }
    }

    /**
     * 绘制分割线
     *
     * @param c
     * @param parent
     * @param state
     */
    @Override
    public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.onDraw(c, parent, state);
        if (mOrientation == VERTICAL_LIST) {
            drawVertical(c, parent);
        } else if (mOrientation == HORIZONTAL_LIST) {
            drawHorizontal(c, parent);
        } else {
            drawHorizontal(c, parent);
            drawVertical(c, parent);
        }
    }

    /**
     * 绘制横向 item 分割线
     *
     * @param canvas 画布
     * @param parent 父容器
     */
    private void drawHorizontal(Canvas canvas, RecyclerView parent) {
        final int x = parent.getPaddingLeft();
        final int width = parent.getMeasuredWidth() - parent.getPaddingRight();
        //getChildCount()(ViewGroup.getChildCount) 返回的是显示层面上的“所包含的子 View 个数”。
        final int childSize = parent.getChildCount();
        for (int i = 0; i < childSize; i++) {
            final View child = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams =
                    (RecyclerView.LayoutParams) child.getLayoutParams();
            //item底部的Y轴坐标+margin值
            final int y = child.getBottom() + layoutParams.bottomMargin;
            final int height = y + mDividerHeight;
            Log.e("height", height + "===================");
            if (mDrawable != null) {
                //setBounds(x,y,width,height); x:组件在容器X轴上的起点 y:组件在容器Y轴上的起点
                // width:组件的长度 height:组件的高度
                mDrawable.setBounds(x, y, width, height);
                mDrawable.draw(canvas);
            }
            if (mPaint != null) {
                canvas.drawRect(x, y, width, height, mPaint);
            }
        }
    }

    /**
     * 绘制纵向 item 分割线
     *
     * @param canvas
     * @param parent
     */
    private void drawVertical(Canvas canvas, RecyclerView parent) {
        final int top = parent.getPaddingTop();
        final int bottom = parent.getMeasuredHeight() - parent.getPaddingBottom();
        final int childSize = parent.getChildCount();
        for (int i = 0; i < childSize; i++) {
            final View child = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
            final int left = child.getRight() + layoutParams.rightMargin;
            final int right = left + mDividerHeight;
            if (mDrawable != null) {
                mDrawable.setBounds(left, top, right, bottom);
                mDrawable.draw(canvas);
            }
            if (mPaint != null) {
                canvas.drawRect(left, top, right, bottom, mPaint);
            }
        }
    }


    /**
     * 获取列数
     *
     * @param parent
     * @return
     */
    private int getSpanCount(RecyclerView parent) {
        int spanCount = -1;
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) {
            spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
        } else if (layoutManager instanceof StaggeredGridLayoutManager) {
            spanCount = ((StaggeredGridLayoutManager) layoutManager).getSpanCount();
        }
        return spanCount;
    }


    private boolean isLastColum(RecyclerView parent, int pos, int spanCount,
                                int childCount) {
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) {
            int orientation = ((GridLayoutManager) layoutManager)
                    .getOrientation();
            if (orientation == StaggeredGridLayoutManager.VERTICAL) {
                // 如果是最后一列,则不需要绘制右边
                return (pos + 1) % spanCount == 0;
            } else {
                childCount = childCount - childCount % spanCount;
                // 如果是最后一列,则不需要绘制右边
                return pos >= childCount;
            }
        } else if (layoutManager instanceof StaggeredGridLayoutManager) {
            int orientation = ((StaggeredGridLayoutManager) layoutManager)
                    .getOrientation();
            if (orientation == StaggeredGridLayoutManager.VERTICAL) {
                // 如果是最后一列,则不需要绘制右边
                return (pos + 1) % spanCount == 0;
            } else {
                childCount = childCount - childCount % spanCount;
                // 如果是最后一列,则不需要绘制右边
                return pos >= childCount;
            }
        }
        return false;
    }

    private boolean isLastRaw(RecyclerView parent, int pos, int spanCount, int childCount) {
        int orientation;
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) {
            childCount = childCount - childCount % spanCount;
            orientation = ((GridLayoutManager) layoutManager)
                    .getOrientation();
            if (orientation == StaggeredGridLayoutManager.VERTICAL) {
                // 如果是最后一行,则不需要绘制底部
                childCount = childCount - childCount % spanCount;
                if (pos >= childCount)
                    return true;
            } else {// StaggeredGridLayoutManager 横向滚动
                // 如果是最后一行,则不需要绘制底部
                if ((pos + 1) % spanCount == 0)
                    return true;
            }
        } else if (layoutManager instanceof StaggeredGridLayoutManager) {
            orientation = ((StaggeredGridLayoutManager) layoutManager)
                    .getOrientation();
            if (orientation == StaggeredGridLayoutManager.VERTICAL) {
                // 如果是最后一行,则不需要绘制底部
                childCount = childCount - childCount % spanCount;
                if (pos >= childCount)
                    return true;
            } else {// StaggeredGridLayoutManager 横向滚动
                // 如果是最后一行,则不需要绘制底部
                if ((pos + 1) % spanCount == 0)
                    return true;
            }
        }
        return false;
    }
}

这里还需要一个工具类用来引用资源文件下的内容 新建一个Utils类

public class Utils {

    public static String getFromAssets(Context context, String fileName) {
        InputStreamReader inputReader = null;
        BufferedReader bufReader = null;
        try {
            inputReader = new InputStreamReader(context.getResources().getAssets().open(fileName));
            bufReader = new BufferedReader(inputReader);
            String line = "";
            StringBuilder result = new StringBuilder();
            while ((line = bufReader.readLine()) != null)
                result.append(line);
            return result.toString();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputReader != null) {
                    inputReader.close();
                }
                if (bufReader != null) {
                    bufReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return "";
    }
}

列表是少不了适配器的 新建一个FootAdapter继RecyclerView.Adapter<RecyclerView.ViewHolder>

public class FootAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private List<String> mDataList = new ArrayList<>();

    public FootAdapter(List<String> dataList) {
        this.mDataList = dataList;
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recycler_layout, parent, false));
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        if (holder instanceof ViewHolder) {
            ((ViewHolder) holder).textView.setText(mDataList.get(position));
        }
    }

    @Override
    public int getItemCount() {
        return mDataList.size();
    }

    private static class ViewHolder extends RecyclerView.ViewHolder {
        TextView textView;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            textView = itemView.findViewById(R.id.item_title);
        }
    }
}

布局文件也简单 就一个TextView
 

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/item_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:minHeight="60dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

最后就是MainActivity了 布局文件

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="5dp"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/bg_recycer"
        android:overScrollMode="never"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

这里主要就是定义 7 个 List,其中mDataList为整体List ,mWeeks用以加载星期展示,其余的就是列表展开下的餐段以及熟普信息具体参考上述 Json可视化 展示图片 ,然后通络循环数据列表依次加载到相应的List 上

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "Demo-MainActivity";
    private List<String> mBreakfastList1 = new ArrayList<>();
    private List<String> mBreakfastList2 = new ArrayList<>();
    private List<String> mLunch1 = new ArrayList<>();
    private List<String> mLunch2 = new ArrayList<>();
    private List<String> mDinner = new ArrayList<>();
    private List<String> mWeeks = new ArrayList<>();

    private List<String> mDataList = new ArrayList<>();
    private RecyclerView mRecyclerView;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        String data = Utils.getFromAssets(this, "data.json");

        getDataList(data);

        mDataList.addAll(mWeeks);
        mDataList.add("早餐");
        mDataList.addAll(mBreakfastList1);
        mDataList.add("早点");
        mDataList.addAll(mBreakfastList2);
        mDataList.add("午餐");
        mDataList.addAll(mLunch1);
        mDataList.add("午点");
        mDataList.addAll(mLunch1);
        mDataList.add("晚餐");
        mDataList.addAll(mDinner);


        mRecyclerView = findViewById(R.id.recycler_view);
        mRecyclerView.setLayoutManager(new GridLayoutManager(this, mWeeks.size()));
        mRecyclerView.setHasFixedSize(true);
        mRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.BOTH_SET, 3, getColor(R.color.colorAccent)));
        FootAdapter adapter = new FootAdapter(mDataList);
        mRecyclerView.setAdapter(adapter);

    }

    private void getDataList(String data) {
        FootBean bean = new Gson().fromJson(data, FootBean.class);
        Log.d(TAG, "bean = " + bean.toString());
        mWeeks.add("");
        for (FootBean.DataBean dataBean : bean.getData()) {
//            if (dataBean.getWeek().equals("星期六") || dataBean.getWeek().equals("星期日")) {
//                return;
//            }
            mWeeks.add(dataBean.getWeek());
            for (FootBean.DataBean.DinnerBean dinnerBean : dataBean.getDinner()) {
                if (dinnerBean.getDinnername().equals("早餐")) {
                    StringBuilder sb = new StringBuilder();
                    for (int i = 0; i < dinnerBean.getList().size(); i++) {
                        sb.append(dinnerBean.getList().get(i).getDishName());
                        if (i != dinnerBean.getList().size() - 1) {
                            sb.append("\n");
                        }
                    }
                    mBreakfastList1.add(sb.toString());
                }

                if (dinnerBean.getDinnername().equals("早点")) {
                    StringBuilder sb = new StringBuilder();
                    for (int i = 0; i < dinnerBean.getList().size(); i++) {
                        sb.append(dinnerBean.getList().get(i).getDishName());
                        if (i != dinnerBean.getList().size() - 1) {
                            sb.append("\n");
                        }
                    }
                    mBreakfastList2.add(sb.toString());
                }

                if (dinnerBean.getDinnername().equals("午餐")) {
                    StringBuilder sb = new StringBuilder();
                    for (int i = 0; i < dinnerBean.getList().size(); i++) {
                        sb.append(dinnerBean.getList().get(i).getDishName());
                        if (i != dinnerBean.getList().size() - 1) {
                            sb.append("\n");
                        }
                    }
                    mLunch1.add(sb.toString());
                }

                if (dinnerBean.getDinnername().equals("午点")) {
                    StringBuilder sb = new StringBuilder();
                    for (int i = 0; i < dinnerBean.getList().size(); i++) {
                        sb.append(dinnerBean.getList().get(i).getDishName());
                        if (i != dinnerBean.getList().size() - 1) {
                            sb.append("\n");
                        }
                    }
                    mLunch2.add(sb.toString());
                }

                if (dinnerBean.getDinnername().equals("晚餐")) {
                    StringBuilder sb = new StringBuilder();
                    for (int i = 0; i < dinnerBean.getList().size(); i++) {
                        sb.append(dinnerBean.getList().get(i).getDishName());
                        if (i != dinnerBean.getList().size() - 1) {
                            sb.append("\n");
                        }
                    }
                    mDinner.add(sb.toString());
                }
            }
        }

    }
}

源码后续会上传,有什么问题评论区留言,第一时间回复。

 

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值