前言

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;@Overridepublic 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>() {@Overridepublic FootBean createFromParcel(Parcel in) {return new FootBean(in);}@Overridepublic 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;}@Overridepublic int describeContents() {return 0;}@Overridepublic 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;@Overridepublic String toString() {return "DataBean{" +"week='" + week + '\'' +", dinner=" + dinner +'}';}protected DataBean(Parcel in) {week = in.readString();}public static final Creator<DataBean> CREATOR = new Creator<DataBean>() {@Overridepublic DataBean createFromParcel(Parcel in) {return new DataBean(in);}@Overridepublic 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;}@Overridepublic int describeContents() {return 0;}@Overridepublic 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;@Overridepublic String toString() {return "DinnerBean{" +"dinnername='" + dinnername + '\'' +", list=" + list +'}';}protected DinnerBean(Parcel in) {dinnername = in.readString();}public static final Creator<DinnerBean> CREATOR = new Creator<DinnerBean>() {@Overridepublic DinnerBean createFromParcel(Parcel in) {return new DinnerBean(in);}@Overridepublic 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;}@Overridepublic int describeContents() {return 0;}@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeString(dinnername);}public static class ListBean implements Parcelable {/*** DishName : 包子*/private String DishName;@Overridepublic String toString() {return "ListBean{" +"DishName='" + DishName + '\'' +'}';}protected ListBean(Parcel in) {DishName = in.readString();}public static final Creator<ListBean> CREATOR = new Creator<ListBean>() {@Overridepublic ListBean createFromParcel(Parcel in) {return new ListBean(in);}@Overridepublic ListBean[] newArray(int size) {return new ListBean[size];}};public String getDishName() {return DishName;}public void setDishName(String DishName) {this.DishName = DishName;}@Overridepublic int describeContents() {return 0;}@Overridepublic 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;//分割线高度,默认为1pxprivate 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*/@Overridepublic 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*/@Overridepublic 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@Overridepublic RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recycler_layout, parent, false));}@Overridepublic void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {if (holder instanceof ViewHolder) {((ViewHolder) holder).textView.setText(mDataList.get(position));}}@Overridepublic 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"><TextViewandroid: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.RecyclerViewandroid: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;@Overrideprotected 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());}}}}
}

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

Android RecyclerView 实现幼儿园食谱相关推荐

  1. Android RecyclerView 基本使用

    Android RecyclerView 基本使用 概述 RecyclerView出现已经有一段时间了,相信大家肯定不陌生了,大家可以通过导入support-v7对其进行使用. 据官方的介绍,该控件用 ...

  2. Android RecyclerView添加Header头部

     Android RecyclerView添加Header头部 Android RecyclerView不像以前的ListView那样直接添加头部,如果要给RecyclerView增加头部,则需要 ...

  3. android 炫酷背景,炫酷-背景图垂直循环滚动登录页,Android RecyclerView实现

    炫酷-背景图上下循环滚动登录页,Android RecyclerView实现方法 某站的登录页背景不停循环滚动,和街边的广告箱很像,感觉不错我也心动了.决定高仿一下,参考了几篇文章后就动手了. 实现步 ...

  4. android批量删除图片,Android RecyclerView单点、批量数据元素项目item的增加、删除和移动...

    Android RecyclerView单点.批量数据元素项目item的增加.删除和移动 前文附录1,2介绍了基本的Android RecyclerView单点.批量元素项目的更新.现在给出其他比较重 ...

  5. Android RecyclerView设计通用Adapter

    RecylerView 的使用频率现在也算做是很高了吧?使用起来的确是挺方便的,也容易实现一些比较好看的效果 一.一般步骤 一般的设计流程都是如下所示 首先是需要一个 JavaBean 来承载数据,包 ...

  6. Android RecyclerView 监听滑动

    今天,简单讲讲Android 如何监听滑动. 不废话了,主要是需要做一个功能,实现RecyclerView滑动时,让一个标题栏固定显示在顶部. 基本知识: 列表的滚动一般分为两种: 手指按下 -> ...

  7. android 水平方向瀑布流,Android RecyclerView(瀑布流)水平/垂直方向分割线

     Android RecyclerView(瀑布流)水平/垂直方向分割线 Android RecyclerView不像过去的ListView那样随意的设置水平方向的分割线,如果要实现Recycle ...

  8. Android实现支付宝AR功能,Android RecyclerView 实现支付宝首页效果

    Android RecyclerView 实现支付宝首页效果 [TOC] 虽然我本人不喜欢支付宝的,但是这个网格本身其实还是不错的,项目更新中更改了一个布局为网格模式,类似支付宝.(估计是产品抄袭的= ...

  9. Android RecyclerView(九)滑动监听综述

    Android RecyclerView(九)滑动监听 1 RecyclerView 的滑动监听 1.1 RecyclerView 设置滑动监听 mRecyclerView.setOnScrollLi ...

最新文章

  1. webcdn故障处理一例
  2. android unity 关闭应用_在后台运行的Android Unity应用程序
  3. CF 529B Group Photo 2 (online mirror version)
  4. Ubuntu install of ROS Melodic
  5. php stdclass,php中new stdclass()用在什么场景
  6. vue-cli webpack 引入jquery
  7. MATLAB 动图绘制、保存
  8. Python: ModuleNotFoundError解决方案
  9. 13分钟搭建动易PHP论坛(OS:Linux)
  10. 关于STC8H8K64U单片机IAP升级过程
  11. 楼板计算塑形弹性_土木吧丨弹性与弹塑性计算差异性分析
  12. idea中加入插入当前系统日期快捷键
  13. 用python爬取堆糖图片-小白级
  14. 20_java使用谷歌邮箱发送邮件
  15. C#屏蔽Alt+F4组合键
  16. 省选LN站2023游记Day1
  17. 对26个英文字母进行huffman编码
  18. 8051单片机Proteus仿真与开发实例-光敏电阻+ADC0804仿真
  19. 【附源码】Python计算机毕业设计宁夏旅游信息管理系统
  20. Python定时任务推送微信消息

热门文章

  1. 苹果手机显示已用短信服务器发送,有iPhone手机发短信失败?很多人都不知道是这个功能在捣鬼!...
  2. 360万数据解压出错
  3. 欧盟站安全带EN12277标准CE认证流程
  4. 富文本编辑器 vue-quill-editor使用(新增,展示,修改,添加附件相关)
  5. 破解WEP密钥过程全解 (下)
  6. [循证理论与实践] Meta分析系列之六:间接比较及网状 Meta 分析
  7. EasyCVR快照跟视频流的关系是什么?快照生成具备何种机制?
  8. Deployment、ReplicaSet、Pod和Service的关系
  9. html怎么去除小数点,css中小数点的精度问题
  10. lol最克制诺手的英雄_《LOL》什么英雄克制诺手 可以克制诺手英雄介绍