Android分类列表之ListView-ViewType实现
Android分类列表之ListView-ViewType实现
- 一、分析实现
- 二、编码
- 三、测试效果
前两天无意间看到朋友的一个功能要实现分类列表,也就互相简单的聊了一下,在此感觉还是挺有意思又加上有段时间没写这个了,就想着用自己的方法实现一下,下面是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}
- 数据分类,从数据中可以看出,主要分为两类数据:
1)isUse=1是当前可用
2)isUse=2是不满足条件 - 对应数据分类,使用ViewType来区分为两种item视图进行绘制:
1)优惠券的视图;
2)分类title的视图; - 视图绘制,大家都知道使用ViewType都是用来显示参差不齐的数据显示(也就是无序),而我们是要分类有序的显示,所以我们对数据根据
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
>>() { }.getType();
再者根据gson提供的api:public
对其进行解析:T fromJson(String json, Type typeOfT) 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布局文件:<?xml version="1.0" encoding="utf-8"?>
2)title分类item布局文件(因为和我讨论哥们叫云飞所以加了个云飞?):
<?xml version="1.0" encoding="utf-8"?>
3)coupon分类item布局文件:
<?xml version="1.0" encoding="utf-8"?>
此分类在AndroidStudio显示效果为(因为是白色为了显示CardView,加了个绿背景色,并没有照UI图全部搭建效果,我们主要目的还是实现分类显示):
-
编码适配器
ListViewAdapter
1)创建ListViewAdapter继承BaseAdapter,添加List
和mCouponBeanList 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); }
三、测试效果
- 在Activity中给ListView设置适配器:
List
couponBeans = LocalDataServer.requestDateFromServerByViewType();ListViewAdapter listViewAdapter = new ListViewAdapter(couponBeans, this);listView.setAdapter(listViewAdapter); - 运行效果为:
至此我们使用
ListView-ViewType
来实现分类显示就完成了,下二篇,我们将继续实现此功能,不过使用分别是RecyclerView-ViewType
和RecyclerView-ItemDecoration
来实现。如果本篇文章有什么技术上问题还请留言讨论共同进步谢谢!
`
更多相关文章
- “罗永浩抖音首秀”销售数据的可视化大屏是怎么做出来的呢?
- Nginx系列教程(三)| 一文带你读懂Nginx的负载均衡
- 不吹不黑!GitHub 上帮助人们学习编码的 12 个资源,错过血亏...
- Android——ECG心电图的绘制实现(二)
- Android(安卓)Handler的详细介绍
- unity调用android语音识别
- android 加密 SQLCipher和Conceal
- 第一篇博客——从《第一行代码》学习笔记开始
- Android中的搜索(search)概述