一般情况下,要将网络请求后的数据显示到界面上,至少要经过以下步骤:
Http 网络请求 → 请求解析读取数据格式为 json → 解析 json 转换成 JavaBean → 编写列表项布局 → 编写 ViewHolder class → 编写适配器 Adapter → 交由列表显示到界面
这个过程是否能够简化一下?而 BaseJson 的适配器生成方案就可以轻松解决这个问题,甚至在最为理想的情况下,能够做到以下的步骤:
Http 网络请求 → 请求解析读取数据格式为 json → 根据数据生成 JsonListAdapter → 交由列表显示到界面
要实现这点就必须得做到能够由 jsonArray 的数据与列表项界面元素 View 的对应关系,又得能省去编写 ViewHolder 和 Adapter 的过程,最后得保证不能因为数据的变化造成 app 闪退崩溃,要做到这点,首先尽可能的需要 json 的解析本身能够转换成 List 的子类,这样才能被 Adapter 直接接受,BaseJson 就可以轻松完成这点,BaseJson 可以轻松将 jsonObject 转换为一个 Map 对象 JsonMap,而 JsonArray 则可以转换为一个 List 数据对象 JsonList 可以轻松满足适配器的构建条件,其次 BaseJson 默认支持针对空指针的软处理,也就意味着无论是 JsonMap 还是 JsonList,get 任何不存在或者类型错误的值都会返回空值而非空指针,软件顶多无法显示内容而不会导致崩溃,这也从一定程度上保障了 app 的可靠性,具体的可以移步 BaseJson Github 阅读 ReadMe。
构建 JsonListAdapter
首先需要解析 Json,你可以使用 BaseOkHttpV3 直接支持解析返回值为 BaseJson,也可以自行转换 json 文本为一个 JsonList:
//解析一段 json 文本为 JsonList
JsonList list = new JsonList(jsonStr);
解析结果不会为空,这是 BaseJson 的基础,若 list.isEmpty() 则是解析失败。
首先需要确保要构造 JsonListAdapter 的 JsonList 必须是一个内容全部为 [{jsonObject},{jsonObject}] 形式的Json 集合对象,其内部的 jsonObject 会被解析为 JsonMap。
然后通过以下方法构建 JsonListAdapter:
//通过 createAdapter 方法创建 JsonListAdapter
JsonListAdapter adapter = list.createAdapter(me, R.layout.item_my_order_list);
//或者,通过构造方法创建 JsonListAdapter
JsonListAdapter adapter = new JsonListAdapter(me, R.layout.item_my_order_list, orderList);
生成 JsonListAdapter 后,可以直接设置给 listView 进行显示:
listView.setAdapter(adapter);
要使 Json 中的内容能够正确显示到了列表项的 UI 中,请在列表项的 xml 布局中,对需要呈现内容对应的 View 增加 android:tag 属性标签,内容和 jsonObject 的 key 对应即可。
例如对于一个 json:
[ { "name": "zhangsan", "avatar": "https://www.kongzue.com/example/avatar1.png", "tip": "normal" }, { "name": "lisi", "avatar": "https://www.kongzue.com/example/avatar2.png", "tip": "vip" }]
对应要显示的列表项布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<com.facebook.drawee.view.SimpleDraweeView
android:layout_width="55dp"
android:layout_height="55dp"
android:tag="avatar"
android:layout_marginHorizontal="15dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_weight="1"
android:tag="name"
android:gravity="center"
android:textSize="20dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="100dp"
android:gravity="center"
android:tag="tip"
android:textSize="20dp" />
</LinearLayout>
那么列表会在显示时自动适配显示数据到对应的界面 View 中。
TextView 自动绑定: 当数据为 String 时自动设置 setText;当数据为 int 时自动调用并设置为文本资源
ImageView 自动绑定: 当数据为 int 时自动调用并设置为图像资源;当数据为 String 时自动设置图像 Uri
这也就意味着单纯的呈现 jsonArray 数据到列表界面将几乎不需要再写任何 JavaBean、ViewHolder 和 Adapter 的复杂业务代码。
有列表项内的事务要处理
此时又有一个问题,比如我的列表项内部有一些事务需要单独处理,例如有一个 Button 需要单独绑定 click 操作,此时可以通过重写 JsonListAdapter#bindEvents 事件,亦或者通过 jsonListAdapter.setEvents(JsonListAdapterEvents#bindEvents) 接口来实现,例如:
JsonListAdapter adapter = new JsonListAdapter(me, R.layout.item_my_order_list, orderList) {
/**
* 绑定事件
* 除非你要在列表项中处理某些控件的事务,例如单独指定 setOnClickListener,不然不建议使用
* 原则就是能尽量少的干涉执行过程就尽量少干涉,能提前预处理好就提前预处理好,
* 别搁运行过程中呼啦啦搞一大堆的逻辑处理,不然不卡你卡谁
*
* @param context 上下文
* @param itemView 列表项根布局
* @param viewHolder 所有子布局
* @param data 数据
* @param index 索引
* @param dataList 数据集
*/
@Override
public void bindEvents(Context context, View itemView, JsonListAdapter.ViewHolder viewHolder, JsonMap data, int index, JsonList dataList) {
//...
}
};
请求下来的数据不符合界面呈现需要
若你的接口返回的数据可能需要预处理,个人建议在网络请求的异步线程提前进行数据的预处理,而不要在 adapter 中进行,因为列表滑动的过程中会频繁调去 adapter 的相关事务可能造成掉帧卡顿,因此更推荐提前在异步线程做好准备工作,例如:
//当 JsonList 的数据全都是 JsonMap,且需要对 JsonMap 的数据内容进行预处理,那么可以通过以下方法实现:
JsonList list = ...;
list.preprocessedJsonMapData(new JsonMapPreprocessingEvents() {
@Override
public JsonMap processingData(JsonMap originData) {
originData.set("goods_price_str", "¥" + originData.getString("goods_price"));
return originData;
}
});
如果确实需要在适配器中进行数据的转换处理,可以使用以下方法实现:
JsonListAdapter adapter = new JsonListAdapter(me, R.layout.item_my_order_list, orderList) {
/**
* 预处理数据
* 但不建议使用,更推荐在数据加载后,在网络请求或异步线程中完成所有数据的预处理,
* 否则可能带来列表滑动过程的卡顿和性能问题。
*
* @param context 上下文
* @param itemView 列表项根布局
* @param viewHolder 所有子布局
* @param data 数据
* @param index 索引
* @param dataList 数据集
* @return 处理完的数据
*/
@Override
public JsonMap preprocessedData(Context context, View itemView, JsonListAdapter.ViewHolder viewHolder, JsonMap data, int index, JsonList dataList){
//...
}
};
我有自定义布局,要通过自己的方法设置数据
BaseJson 已经默认实现了针对 TextView 和 ImageView 的数据适配相关事务,但若你有自己的需要,可以通过实现以下接口自行实现一些特殊布局的数据适配工作:
JsonListAdapter adapter = new JsonListAdapter(me, R.layout.item_my_order_list, orderList) {
/**
* 设置数据
* 我们已经预设了两种数据自动绑定的场景:例如,TextView 自动绑定:
* {当数据为 String 时自动设置 setText;当数据为 int 时自动调用并设置为文本资源}
* ImageView 自动绑定:
* {当数据为 int 时自动调用并设置为图像资源;当数据为 String 时自动设置图像 Uri;}
* 上述过程都是自动支持的,如果你有特殊需求或者其它组件需要设置,可以重写以下方法进行处理
*
* @param context 上下文
* @param tag 子布局 view 的标签
* @param view 子布局
* @param data 数据
* @param index 索引
* @param dataList 数据集
*/
@Override
public void setData(Context context, String tag, View view, JsonMap data, int index, JsonList dataList){
//...
}
};
尾巴
BaseJson 旨在节省工作量,快速完成从网络请求、解析到界面数据填充适配的过程,可能还有不完善的地方也将在后续版本更新继续改善,如有建议或问题也可前往 Github 提交 issues,期待与大家的沟通和交流,愿世间没有 bug 和难写的代码。