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. 数据分类,从数据中可以看出,主要分为两类数据:
    1)isUse=1是当前可用
    2)isUse=2是不满足条件
  2. 对应数据分类,使用ViewType来区分为两种item视图进行绘制:
    1)优惠券的视图;
    2)分类title的视图;
  3. 视图绘制,大家都知道使用ViewType都是用来显示参差不齐的数据显示(也就是无序),而我们是要分类有序的显示,所以我们对数据根据isUse排序分类显示,但是在每个分类的前都是title类,所以只要我们的分类Title排序后正好在每个对应类型的前面就行了;至此麻烦来了,在数据里是没有关于title的数据,所以我们还要添加对应分类Title假数据在对其进行排序,由于两类数据都是根据isUse来分类的且值分别为1、2,我们可以创建两个对应的假数据将其isUser设置为0、1.5然后在用Float.compare()实现来对应进行排序,在对其进行适配绘制就完成了。
二、编码

上面对其进行里简单的分析,下面开始进行愉快的编码吧

  1. 解析数据
    分析完数据我们就开始创建对应的实体类:
    1)响应基类BaseResponse让其实现序列化接口Serializable,赋予范型T成为范型类,T可以对应我们任何类型的响应数据,可以是List<T>Object

    public class BaseResponse<T> 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<List<Item>>,所以我们接下来创建我们Item类-CouponBean且实现序列化接口Serializable,最终需要解析的数据模型为:BaseRespons<List<CouponBean>>

    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;
        }
    }
    
    
  2. 处理数据,之前分析我们不仅要添加对应的类型假数据,还需要根据isUse对数据进行类别的排序,这样我们才能保证ListView绘制出来是有效有序的分类列表。
    1)利用gson解析json数据,首先获取对应类型type:
    Type type = new TypeToken<BaseResponse<List<CouponBean>>>() { }.getType();
    再者根据gson提供的api:public <T> T fromJson(String json, Type typeOfT)对其进行解析:BaseResponse<List<CouponBean>> 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<CouponBean> requestDateFromServerByViewType() {
            BaseResponse<List<CouponBean>> baseResponse = new Gson().fromJson(json_data, new TypeToken<BaseResponse<List<CouponBean>>>() {
            }.getType());
            List<CouponBean> 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;
        }
    
  3. 布局文件(没啥好说的直接贴吧)
    1)activity布局文件:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout 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"
        tools:context=".yunfei.ListViewActivity">
    
        <ListView
            android:id="@+id/listView"
            android:divider="@null"
            android:layout_width="match_parent"
            android:background="@color/main_radio_selected"
            android:layout_height="match_parent"/>
    </RelativeLayout>
    

    2)title分类item布局文件(因为和我讨论哥们叫云飞所以加了个云飞?):

    <?xml version="1.0" encoding="utf-8"?>
    <TextView xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/yunfei_title"
        android:layout_width="match_parent"
        android:gravity="center_vertical"
        android:textSize="@dimen/sp18"
        android:textColor="@color/blue"
        android:paddingLeft="@dimen/dp15"
        android:paddingTop="@dimen/dp10"
        android:layout_height="wrap_content"/>
    

    3)coupon分类item布局文件:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout 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:paddingTop="@dimen/dp15"
        android:paddingLeft="@dimen/dp15"
        android:paddingRight="@dimen/dp15"
        android:background="@color/main_radio_selected"
        android:layout_height="wrap_content">
    
        <android.support.v7.widget.CardView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:minHeight="@dimen/dp60"
            app:cardBackgroundColor="@color/white"
            app:cardElevation="0dp"
            app:cardCornerRadius="@dimen/dp20"
            app:contentPaddingLeft="@dimen/dp20"
            app:contentPaddingRight="@dimen/dp20"
            app:contentPaddingTop="@dimen/dp20"
            tools:ignore="HardcodedText,UnusedAttribute">
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">
                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="horizontal">
                    <LinearLayout
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="2"
                        android:orientation="vertical">
    
                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:ellipsize="end"
                            android:maxEms="7"
                            android:maxLines="1"
                            android:text="新用户优惠券新的生活"
                            android:textSize="@dimen/sp20" />
    
                        <TextView
                            android:id="@+id/couponDescription"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_marginTop="@dimen/dp5"
                            android:contentDescription="优惠券说明"
                            android:ellipsize="end"
                            android:maxEms="11"
                            android:maxLines="1"
                            android:text="优选商城通用,团购/抢购不可用"
                            android:textSize="@dimen/sp15" />
                    </LinearLayout>
                    <TextView
                        android:id="@+id/couponMoney"
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center"
                        android:layout_weight="1"
                        android:contentDescription="优惠券金额"
                        android:gravity="center"
                        android:text="50"
                        android:textSize="@dimen/sp50" />
                </LinearLayout>
                <View
                    android:layout_width="match_parent"
                    android:layout_height="@dimen/px1"
                    android:background="@color/black" />
    
                <RelativeLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_vertical"
                    android:paddingTop="@dimen/dp10"
                    android:paddingBottom="@dimen/dp10">
    
                    <TextView
                        android:id="@+id/indate"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:contentDescription="有效期"
                        android:text="2017-12-12至2019-09-05" />
    
                    <TextView
                        android:id="@+id/useCondition"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_alignParentRight="true"
                        android:contentDescription="满多少才能使用,使用条件"
                        android:text="满200可用" />
    
                </RelativeLayout>
    
            </LinearLayout>
        </android.support.v7.widget.CardView>
    </RelativeLayout>
    

    此分类在AndroidStudio显示效果为(因为是白色为了显示CardView,加了个绿背景色,并没有照UI图全部搭建效果,我们主要目的还是实现分类显示):
    在这里插入图片描述

  4. 编码适配器ListViewAdapter
    1)创建ListViewAdapter继承BaseAdapter,添加List<CouponBean> mCouponBeanListContext mContext属性,创建这两个参数的构造方法,先重写public int getCount()public Object getItem(int position)public long getItemId(int position)方法为:

    public class ListViewAdapter extends BaseAdapter {
    
        private List<CouponBean> mCouponBeanList;
    
        private Context mContext;
    
        public ListViewAdapter(List<CouponBean> 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的分为第一类其值返回0getItemViewType=0isUse=1或isUse=2为第二类其值返回1getItemViewType=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);
        }
    
三、测试效果
  1. 在Activity中给ListView设置适配器:
    List<CouponBean> couponBeans = LocalDataServer.requestDateFromServerByViewType();
    ListViewAdapter listViewAdapter = new ListViewAdapter(couponBeans, this);
    listView.setAdapter(listViewAdapter);
    
  2. 运行效果为:
    在这里插入图片描述

至此我们使用ListView-ViewType来实现分类显示就完成了,下二篇,我们将继续实现此功能,不过使用分别是RecyclerView-ViewTypeRecyclerView-ItemDecoration来实现。如果本篇文章有什么技术上问题还请留言讨论共同进步谢谢!
`

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值