前两天无意间看到朋友的一个功能要实现分类列表,也就互相简单的聊了一下,在此感觉还是挺有意思又加上有段时间没写这个了,就想着用自己的方法实现一下,下面是UI效果图
其实可以直接使用
ExpandableListView
(从出来工作写代码开始算起我用到ExpandableListView
的次数不超过一个巴掌,以下就全当我不会使用这个控件吧!),不会使用这个控件而且我又只会ListView咋办呢,别急,先观察一下UI效果和数据。
{"msg":"可用优惠券列表获取成功","code":"100","data":[{"isUse":"1","useConditions":50.00,"couponUserId":2237,"couponName":"满50减15","validityDate":"2019-06-26至2019-07-31","couponUseExplain":"优选商城通用,团购/抢购不可用","denominationShow":"15","useConditionsDesc":"满50.00可用","couponId":362,"useConditionsShow":"50","denomination":15.00},{"isUse":"1","useConditions":10.00,"couponUserId":2236,"couponName":"满10减5","validityDate":"2019-06-26至2019-07-31","couponUseExplain":"优选商城通用,团购/抢购不可用","denominationShow":"5","useConditionsDesc":"满10.00可用","couponId":361,"useConditionsShow":"10","denomination":5.00},{"isUse":"1","useConditions":5.00,"couponUserId":2235,"couponName":"满5减2元","validityDate":"2019-06-26至2019-07-31","couponUseExplain":"优选商城通用,团购/抢购不可用","denominationShow":"2","useConditionsDesc":"满5.00可用","couponId":360,"useConditionsShow":"5","denomination":2.00},{"isUse":"2","useConditions":200.00,"couponUserId":2238,"couponName":"满200减70","validityDate":"2019-06-26至2019-07-31","couponUseExplain":"优选商城通用,团购/抢购不可用","denominationShow":"70","useConditionsDesc":"满200.00可用","couponId":363,"useConditionsShow":"200","denomination":70.00}],"success":true,"now":11111111111}
isUse
排序分类显示,但是在每个分类的前都是title类,所以只要我们的分类Title
排序后正好在每个对应类型的前面就行了;至此麻烦来了,在数据里是没有关于title的数据,所以我们还要添加对应分类Title
假数据在对其进行排序,由于两类数据都是根据isUse来分类的且值分别为1、2
,我们可以创建两个对应的假数据将其isUser设置为0、1.5
然后在用Float.compare()
实现来对应进行排序,在对其进行适配绘制就完成了。上面对其进行里简单的分析,下面开始进行愉快的编码吧
解析数据
分析完数据我们就开始创建对应的实体类:
1)响应基类BaseResponse
让其实现序列化接口Serializable
,赋予范型T
成为范型类,T
可以对应我们任何类型的响应数据,可以是List
、Object
等
public class BaseResponse implements Serializable {
/**
* msg : 可用优惠券列表获取成功
* code : 100
* data : items
* success : true
* now : 1561625083199
*/
private String msg;
private String code;
private boolean success;
private long now;
private T data;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public long getNow() {
return now;
}
public void setNow(long now) {
this.now = now;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
2)对此次数据类型模型应该是BaseRespons
,所以我们接下来创建我们>
Item
类-CouponBean
且实现序列化接口Serializable
,最终需要解析的数据模型为:BaseRespons
>
public class CouponBean implements Serializable {
/**
* isUse : 1
* useConditions : 50.0
* couponUserId : 2237
* couponName : 满50减15
* validityDate : 2019-06-26至2019-07-31
* couponUseExplain : 优选商城通用,团购/抢购不可用
* denominationShow : 15
* useConditionsDesc : 满50.00可用
* couponId : 362
* useConditionsShow : 50
* denomination : 15.0
*/
private String isUse;
private double useConditions;
private int couponUserId;
private String couponName;
private String validityDate;
private String couponUseExplain;
private String denominationShow;
private String useConditionsDesc;
private int couponId;
private String useConditionsShow;
private double denomination;
public String getIsUse() {
return isUse;
}
public void setIsUse(String isUse) {
this.isUse = isUse;
}
public double getUseConditions() {
return useConditions;
}
public void setUseConditions(double useConditions) {
this.useConditions = useConditions;
}
public int getCouponUserId() {
return couponUserId;
}
public void setCouponUserId(int couponUserId) {
this.couponUserId = couponUserId;
}
public String getCouponName() {
return couponName;
}
public void setCouponName(String couponName) {
this.couponName = couponName;
}
public String getValidityDate() {
return validityDate;
}
public void setValidityDate(String validityDate) {
this.validityDate = validityDate;
}
public String getCouponUseExplain() {
return couponUseExplain;
}
public void setCouponUseExplain(String couponUseExplain) {
this.couponUseExplain = couponUseExplain;
}
public String getDenominationShow() {
return denominationShow;
}
public void setDenominationShow(String denominationShow) {
this.denominationShow = denominationShow;
}
public String getUseConditionsDesc() {
return useConditionsDesc;
}
public void setUseConditionsDesc(String useConditionsDesc) {
this.useConditionsDesc = useConditionsDesc;
}
public int getCouponId() {
return couponId;
}
public void setCouponId(int couponId) {
this.couponId = couponId;
}
public String getUseConditionsShow() {
return useConditionsShow;
}
public void setUseConditionsShow(String useConditionsShow) {
this.useConditionsShow = useConditionsShow;
}
public double getDenomination() {
return denomination;
}
public void setDenomination(double denomination) {
this.denomination = denomination;
}
}
处理数据,之前分析我们不仅要添加对应的类型假数据,还需要根据isUse对数据进行类别的排序,这样我们才能保证ListView绘制出来是有效有序的分类列表。
1)利用gson解析json数据,首先获取对应类型type:
Type type = new TypeToken
再者根据gson提供的api:public
对其进行解析:BaseResponse
,这样就得到我们要的数据,然后是添加类别假数据在进行排序,在上面我们已经分析了如何进行排序,就是创建两个对应假数据> baseResponse = new Gson().fromJson(json_data, type)
CouponBean
分别将其isUse设置为0、1.5
,利用它的couponName
属性来存储我们的分类title:当前可用、不满足条件
,在用利用Collections.sort(Float.compare())
对其进行排序为Collections.sort(data, (o1, o2) -> Float.valueOf(o1.getIsUse()).compareTo(Float.valueOf(o2.getIsUse())))
,在此为了更优雅,我们模拟远程服务器(不模拟延时)创建本地服务类:LocalDataServer
如下:
public final class LocalDataServer {
static final String json_data = "{\"msg\":\"可用优惠券列表获取成功\",\"code\":\"100\",\"data\":[" +
"{\"isUse\":\"1\",\"useConditions\":50.00,\"couponUserId\":2237,\"couponName\":\"满50减15\",\"validityDate\":\"2019-06-26至2019-07-31\",\"couponUseExplain\":\"优选商城通用,团购/抢购不可用\",\"denominationShow\":\"15\",\"useConditionsDesc\":\"满50.00可用\",\"couponId\":362,\"useConditionsShow\":\"50\",\"denomination\":15.00}," +
"{\"isUse\":\"1\",\"useConditions\":10.00,\"couponUserId\":2236,\"couponName\":\"满10减5\",\"validityDate\":\"2019-06-26至2019-07-31\",\"couponUseExplain\":\"优选商城通用,团购/抢购不可用\",\"denominationShow\":\"5\",\"useConditionsDesc\":\"满10.00可用\",\"couponId\":361,\"useConditionsShow\":\"10\",\"denomination\":5.00}," +
"{\"isUse\":\"1\",\"useConditions\":5.00,\"couponUserId\":2235,\"couponName\":\"满5减2元\",\"validityDate\":\"2019-06-26至2019-07-31\",\"couponUseExplain\":\"优选商城通用,团购/抢购不可用\",\"denominationShow\":\"2\",\"useConditionsDesc\":\"满5.00可用\",\"couponId\":360,\"useConditionsShow\":\"5\",\"denomination\":2.00}," +
"{\"isUse\":\"2\",\"useConditions\":200.00,\"couponUserId\":2238,\"couponName\":\"满200减70\",\"validityDate\":\"2019-06-26至2019-07-31\",\"couponUseExplain\":\"优选商城通用,团购/抢购不可用\",\"denominationShow\":\"70\",\"useConditionsDesc\":\"满200.00可用\",\"couponId\":363,\"useConditionsShow\":\"200\",\"denomination\":70.00}]," +
"\"success\":true,\"now\":1561625083199}";
/**
* 获取根据ViewType分类显示的数据
*
* @return
*/
public static List requestDateFromServerByViewType() {
BaseResponse> baseResponse = new Gson().fromJson(json_data, new TypeToken>>() {
}.getType());
List data = baseResponse.getData();
CouponBean couponBean = new CouponBean();
couponBean.setIsUse("0");
couponBean.setCouponName("当前可用");
data.add(couponBean);
couponBean = new CouponBean();
couponBean.setIsUse("1.5");
couponBean.setCouponName("当前不满足条件");
data.add(couponBean);
Collections.sort(data, (o1, o2) -> Float.valueOf(o1.getIsUse()).compareTo(Float.valueOf(o2.getIsUse())));
return data;
}
布局文件(没啥好说的直接贴吧)
1)activity布局文件:
2)title分类item布局文件(因为和我讨论哥们叫云飞所以加了个云飞?):
3)coupon分类item布局文件:
此分类在AndroidStudio显示效果为(因为是白色为了显示CardView,加了个绿背景色,并没有照UI图全部搭建效果,我们主要目的还是实现分类显示):
编码适配器ListViewAdapter
1)创建ListViewAdapter继承BaseAdapter,添加List
和Context mContext
属性,创建这两个参数的构造方法,先重写public int getCount()
、public Object getItem(int position)
和public long getItemId(int position)
方法为:
public class ListViewAdapter extends BaseAdapter {
private List mCouponBeanList;
private Context mContext;
public ListViewAdapter(List couponBeanList, Context context) {
mCouponBeanList = couponBeanList;
mContext = context;
}
@Override
public int getCount() {
if (mCouponBeanList != null) {
return mCouponBeanList.size();
}
return 0;
}
@Override
public Object getItem(int position) {
return mCouponBeanList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return null;
}
2)因为我们是根据ViewType来进行分类显示的,所以我们重写方法public int getViewTypeCount()
和public int getItemViewType(int position)
,由于我们只有两个类型所以ViewTypeCount()
要返回2
,注意:确定返回类型数量n后,getItemViewType返回的值必须在区间[0,n-1]之间,也就是说getItemViewType
只能返回0
或者1
,我将isUse=0或isUse=1.5
的分为第一类其值返回0
即getItemViewType=0
,isUse=1或isUse=2
为第二类其值返回1
即getItemViewType=1
,在这里插入代码片
代码如下:
@Override
public int getViewTypeCount() {
return 2;
}
@Override
public int getItemViewType(int position) {
String isUse = mCouponBeanList.get(position).getIsUse();
return ("0".equals(isUse) || "1.5".equals(isUse)) ? 0 : 1;
}
3)创建视图View,根据ViewType我们有两个分类,所以需要创建填充两个视图,ViewType=0的对应title分类标题视图
;ViewType=1的对应coupon优惠券视图
,出于对内存等性能考虑,我们需要进行View的复用,所以我们还需要创建两个ViewHolder,其分别对应两个不同ViewType:创建分类标题ViewHolder:TitleViewHolder
对应标题布局文件ViewType=0
,解析数据映射到对应的控件上代码为:
static class TitleViewHolder {
private TextView title;
TitleViewHolder(View itemView) {
title = itemView.findViewById(R.id.yunfei_title);
}
void bindData(CouponBean couponBean) {
title.setText(couponBean.getCouponName());
}
}
创建分类标题CouponViewHolder:CouponViewHolder
对应优惠券布局文件ViewType=1
,解析数据映射到对应的控件上代码为:
static class CouponViewHolder {
private TextView couponDescription, couponMoney, indate, useCondition;
CouponViewHolder(View itemView) {
couponDescription = itemView.findViewById(R.id.couponDescription);
couponMoney = itemView.findViewById(R.id.couponMoney);
useCondition = itemView.findViewById(R.id.useCondition);
indate = itemView.findViewById(R.id.indate);
}
void bindData(CouponBean couponBean) {
couponDescription.setText(couponBean.getCouponUseExplain());
couponMoney.setText(couponBean.getDenominationShow());
useCondition.setText(couponBean.getUseConditionsDesc());
indate.setText(couponBean.getValidityDate());
}
}
最后重写我们的public View getView(int position, View convertView, ViewGroup parent)
方法获视图:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return getItemViewType(position) == 0 ? getTitleView(position, convertView, parent) : getCouponView(position, convertView, parent);
}
List couponBeans = LocalDataServer.requestDateFromServerByViewType();
ListViewAdapter listViewAdapter = new ListViewAdapter(couponBeans, this);
listView.setAdapter(listViewAdapter);
至此我们使用
ListView-ViewType
来实现分类显示就完成了,下二篇,我们将继续实现此功能,不过使用分别是RecyclerView-ViewType
和RecyclerView-ItemDecoration
来实现。如果本篇文章有什么技术上问题还请留言讨论共同进步谢谢!
`