前言
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());
}
}
}
}
}
源码后续会上传,有什么问题评论区留言,第一时间回复。