[Android]如何让ListView显示不同布局的解决方式和案例

时间:2021-12-10 15:50:39

一、概述

在项目的需求中,有一处需要显示一个交易记录的列表,这个列表很容易让人联想到用listview来实现,但是这个列表又有稍许不同的地方,那就是它里面的item并不是一样的布局,其中某些部分显示的是消费的记录,而有些地方显示的是充值的记录,也就对应了不同的item布局。而且,这两处地方都是从服务端获取数据的,这两个item的数据对应的类内容也各不相同,该怎么处理呢?

下面来一步步实现这个效果。

二、先看效果图

[Android]如何让ListView显示不同布局的解决方式和案例


三、实现步骤

实现的原理就是listview的adapter中的一个关键的方法就是getItemViewType(getItemViewType),这个方法有一个参数是position,有了这个position我们就可以对list集合中的不同位置的数据进行不同的处理,进而标识不同的type,将list中的数据进行分类处理。

首先进行,数据的准备:

在这个项目中,数据源是从服务端获取的json数据,数据的格式如下:

[html]  view plain  copy  print ? [Android]如何让ListView显示不同布局的解决方式和案例 [Android]如何让ListView显示不同布局的解决方式和案例
  1. {  
  2.     "status_code": "0",  
  3.     "result": [  
  4.         {  
  5.             "mr_content": {  
  6.                 "point": "10",  
  7.                 "member_money": "100",  
  8.                 "pay_money": "300",  
  9.                 "cash": "200",  
  10.                 "bonus": "消费满200元立减50元餐券1张",  
  11.                 "activities": "三锅鸡1元任吃",  
  12.                 "coupon": "满100送50",  
  13.                 "branch_name": "四海一家"  
  14.             },  
  15.             "mr_id": "25",  
  16.             "mr_createtime": "1333333333",  
  17.             "mr_type": "0",  
  18.             "user_id": "108",  
  19.             "merchant_id": "1",  
  20.             "branch_id": "1",  
  21.             "branch_name": "ffff"  
  22.         },  
  23.         {  
  24.             "mr_content": {  
  25.                 "member_money": "300",  
  26.                 "branch_name": "四海一家"  
  27.             },  
  28.             "mr_id": "30",  
  29.             "mr_createtime": "1333333333",  
  30.             "mr_type": "1",  
  31.             "user_id": "108",  
  32.             "merchant_id": "1",  
  33.             "branch_id": "1",  
  34.             "branch_name": "fff"  
  35.         }  
  36.     ],  
  37.     "status_desc": "ok"  
  38. }  

可以看到其中mr_content这个字段,是一个自定义对象,但是两个mr_content的内容不同,这里是分别为mr_content的内容定义两个不同的类还是如何处理呢?

一开始,我是分别为两个mr_content定义不同的类,后来发现这样行不通,因为这样做的话定义外层类的时候mr_content就无法指定数据类型了。所以,最后采用某人的方法将mr_content定义为一个类,将两个不同的mr_content的字段都定义进去,解析的时候不会出现问题,没有数据会显示null

下面是我定义的mr_content字段的数据类型ComsumAndChargeRecordBean

[java]  view plain  copy  print ? [Android]如何让ListView显示不同布局的解决方式和案例 [Android]如何让ListView显示不同布局的解决方式和案例
  1. public class ComsumAndChargeRecordBean {  
  2.     private String branch_name;  
  3.     private String pay_money;  
  4.     private String coupon;//使用特权  
  5.     private String activities;  
  6.     private String member_money;  
  7.     private String cash;  
  8.     private String point;  
  9.     private String bonus;  
  10. //  private String prestore_money;//预存款  
  11.       
  12.     public String getBranch_name() {  
  13.         return branch_name;  
  14.     }  
  15.     public void setBranch_name(String branch_name) {  
  16.         this.branch_name = branch_name;  
  17.     }  
  18.     public String getPay_money() {  
  19.         return pay_money;  
  20.     }  
  21.     public void setPay_money(String pay_money) {  
  22.         this.pay_money = pay_money;  
  23.     }  
  24.     public String getCoupon() {  
  25.         return coupon;  
  26.     }  
  27.     public void setCoupon(String coupon) {  
  28.         this.coupon = coupon;  
  29.     }  
  30.     public String getActivities() {  
  31.         return activities;  
  32.     }  
  33.     public void setActivities(String activities) {  
  34.         this.activities = activities;  
  35.     }  
  36.     public String getMember_money() {  
  37.         return member_money;  
  38.     }  
  39.     public void setMember_money(String member_money) {  
  40.         this.member_money = member_money;  
  41.     }  
  42.     public String getCash() {  
  43.         return cash;  
  44.     }  
  45.     public void setCash(String cash) {  
  46.         this.cash = cash;  
  47.     }  
  48.     public String getPoint() {  
  49.         return point;  
  50.     }  
  51.     public void setPoint(String point) {  
  52.         this.point = point;  
  53.     }  
  54.     public String getBonus() {  
  55.         return bonus;  
  56.     }  
  57.     public void setBonus(String bonus) {  
  58.         this.bonus = bonus;  
  59.     }  
  60. }  


数据准备好了,下面是传入listview中进行显示:

布局文件:

[html]  view plain  copy  print ? [Android]如何让ListView显示不同布局的解决方式和案例 [Android]如何让ListView显示不同布局的解决方式和案例
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:orientation="vertical" >  
  6.   
  7.     <include  
  8.         android:id="@+id/traderecord_layout"  
  9.         layout="@layout/topview_activity" />  
  10.   
  11.     <ListView  
  12.         android:id="@+id/lv_my_traderecord"  
  13.         android:layout_width="match_parent"  
  14.         android:layout_height="match_parent" >  
  15.     </ListView>  
  16.   
  17. </LinearLayout>  

两个不同item的布局文件就省略了,相信大家都会,这个没什么难度

下面是主界面代码:

[java]  view plain  copy  print ? [Android]如何让ListView显示不同布局的解决方式和案例 [Android]如何让ListView显示不同布局的解决方式和案例
  1. <pre class="java" name="code">    protected void onCreate(Bundle savedInstanceState) {  
  2.         // TODO Auto-generated method stub  
  3.         super.onCreate(savedInstanceState);  
  4.         setContentView(R.layout.activity_trade_record);  
  5.           
  6.         mListView = (ListView) findViewById(R.id.lv_my_traderecord);  
  7.         E_TempTradeRecordAdapter adapter = new E_TempTradeRecordAdapter(  
  8.                 E_TradeRecordActivity.this, myModel.tradeRecordList);  
  9.         mListView.setAdapter(adapter);  
  10.         adapter.notifyDataSetChanged();  
  11.     }  
 

下面是adapter适配器的一部分代码:

字段和构造函数:

[java]  view plain  copy  print ? [Android]如何让ListView显示不同布局的解决方式和案例 [Android]如何让ListView显示不同布局的解决方式和案例
  1. private static final String TAG = "E_TradeRecordAdapter";  
  2.     private static final int TYPE_COUNT = 2;//item类型的总数  
  3.     private static final int TYPE_COMSUM = 0;//消费类型  
  4.     private static final int TYPE_CHARGE = 1;//充值类型  
  5.     private ArrayList<TradeRecordBean> dataList = new ArrayList<TradeRecordBean>();//数据集合  
  6.     private Context mContext;  
  7.     private int currentType;//当前item类型  
  8.   
  9.     public E_TempTradeRecordAdapter(Context mContext,  
  10.             ArrayList<TradeRecordBean> dataList) {  
  11.         super();  
  12.         this.dataList = dataList;  
  13.         this.mContext = mContext;  
  14.     }  

几个重要方法:

[java]  view plain  copy  print ? [Android]如何让ListView显示不同布局的解决方式和案例 [Android]如何让ListView显示不同布局的解决方式和案例
  1. @Override  
  2.     public int getCount() {  
  3.         // TODO Auto-generated method stub  
  4.         return dataList.size();  
  5.     }  
  6.   
  7.     @Override  
  8.     public Object getItem(int position) {  
  9.         // TODO Auto-generated method stub  
  10.         return dataList.get(position);  
  11.     }  
  12.   
  13.     @Override  
  14.     public long getItemId(int position) {  
  15.         // TODO Auto-generated method stub  
  16.         return position;  
  17.     }  

获取子item的类型 获取类型的数量  这里是根据字段Mr_type来确定的,json数据里面是根据这个字段来确定消费记录的类型的。总之,在为item设置不同的布局的时候肯定有一个标记用来区分不同的item,你可以用这个作为判断的标记,来设置不同的type。

[java]  view plain  copy  print ? [Android]如何让ListView显示不同布局的解决方式和案例 [Android]如何让ListView显示不同布局的解决方式和案例
  1. @Override  
  2.     public int getItemViewType(int position) {  
  3.         // TODO Auto-generated method stub  
  4.         if ("0".equals(dataList.get(position).getMr_type())) {  
  5.             return TYPE_COMSUM;// 消费类型  
  6.         } else if ("1".equals(dataList.get(position).getMr_type())) {  
  7.             return TYPE_CHARGE;// 充值类型  
  8.         } else {  
  9.             return 100;  
  10.         }  
  11.     }  
  12.   
  13.     @Override  
  14.     public int getViewTypeCount() {  
  15.         return TYPE_COUNT;  
  16.     }  

viewholder:缓存这几个textview控件

[java]  view plain  copy  print ? [Android]如何让ListView显示不同布局的解决方式和案例 [Android]如何让ListView显示不同布局的解决方式和案例
  1. /** 
  2.      * 消费记录 
  3.      * @author yl 
  4.      * 
  5.      */  
  6.     class ComsumViewHolder {  
  7.         TextView branchnameCom;  
  8.         TextView comsumemoney;  
  9.         TextView useprevillage;  
  10.         TextView yuezhifu;  
  11.         TextView cash;  
  12.         TextView thisscore;  
  13.         TextView extrareward;  
  14.         TextView prestoremoney;  
  15.     }  
  16.       
  17.     /** 
  18.      * 充值记录 
  19.      * @author yl 
  20.      * 
  21.      */  
  22.     class ChargeViewHolder {  
  23.         TextView branchnameCha;  
  24.         TextView prestoremoney;  
  25.         TextView extrasmoney;  
  26.         TextView totalmoney;  
  27.     }  


最后是getview方法:其中有一个关键的方法

[java]  view plain  copy  print ? [Android]如何让ListView显示不同布局的解决方式和案例 [Android]如何让ListView显示不同布局的解决方式和案例
  1. currentType = getItemViewType(position);  

这个方法获取到当前position的类型,也就是在前面的getItemViewType方法设置的类型。

其中对convertView进行了复用和holder的使用,算是对listview的优化吧。

当currentType == TYPE_COMSUM,消费类型时,加载comsumView = LayoutInflater.from(mContext).inflate(  R.layout.traderecord_item_comsume, null);消费类型的布局文件。反之,加载充值类型的布局文件。这样就可以达到为不同的item设置不同的布局文件了。

[java]  view plain  copy  print ? [Android]如何让ListView显示不同布局的解决方式和案例 [Android]如何让ListView显示不同布局的解决方式和案例
  1. public View getView(int position, View convertView, ViewGroup parent) {  
  2.     // TODO Auto-generated method stub  
  3.     View comsumView = null;  
  4.     View chargeView = null;  
  5.   
  6.     ComsumAndChargeRecordBean record = (ComsumAndChargeRecordBean) dataList  
  7.             .get(position).getMr_content();  
  8.   
  9.     currentType = getItemViewType(position);  
  10.     if (currentType == TYPE_COMSUM) {  
  11.         ComsumViewHolder comsumHolder = null;  
  12.         if (convertView == null) {  
  13.             comsumHolder = new ComsumViewHolder();  
  14.             comsumView = LayoutInflater.from(mContext).inflate(  
  15.                     R.layout.traderecord_item_comsume, null);  
  16.             comsumHolder.branchnameCom = (TextView) comsumView  
  17.                     .findViewById(R.id.tv_branch_name);  
  18.             comsumHolder.comsumemoney = (TextView) comsumView  
  19.                     .findViewById(R.id.tv_comsumemoney);  
  20.             comsumHolder.useprevillage = (TextView) comsumView  
  21.                     .findViewById(R.id.tv_useprevillage);  
  22.             comsumHolder.yuezhifu = (TextView) comsumView  
  23.                     .findViewById(R.id.tv_yuezhifu);  
  24.             comsumHolder.cash = (TextView) comsumView  
  25.                     .findViewById(R.id.tv_cash);  
  26.             comsumHolder.thisscore = (TextView) comsumView  
  27.                     .findViewById(R.id.tv_thisscore);  
  28.             comsumHolder.extrareward = (TextView) comsumView  
  29.                     .findViewById(R.id.tv_extrareward);  
  30.             comsumView.setTag(comsumHolder);  
  31.             convertView = comsumView;  
  32.         } else {  
  33.             comsumHolder = (ComsumViewHolder) convertView.getTag();  
  34.         }  
  35.         comsumHolder.branchnameCom.setText(DateFormatUtil.formatDate(Long  
  36.                 .valueOf(dataList.get(position).getMr_createtime()))  
  37.                 + "  "  
  38.                 + record.getBranch_name());// 消费时间和分店  
  39.         comsumHolder.comsumemoney.setText(record.getPay_money());// 消费金额  
  40.         comsumHolder.useprevillage.setText(record.getCoupon());// 使用特权  
  41.         comsumHolder.yuezhifu.setText(record.getMember_money());// 余额支付  
  42.         comsumHolder.cash.setText(record.getCash());// 现金支付  
  43.         comsumHolder.thisscore.setText(record.getPoint());// 本次积分  
  44.         comsumHolder.extrareward.setText(record.getBonus());// 额外奖励  
  45.     } else if (currentType == TYPE_CHARGE) {  
  46.         ChargeViewHolder chargeHoler = null;  
  47.         if (convertView == null) {  
  48.             chargeHoler = new ChargeViewHolder();  
  49.             chargeView = LayoutInflater.from(mContext).inflate(  
  50.                     R.layout.traderecord_item_chongzhi, null);  
  51.             chargeHoler.branchnameCha = (TextView) chargeView  
  52.                     .findViewById(R.id.tv_branchname_charge);  
  53.             chargeHoler.prestoremoney = (TextView) chargeView  
  54.                     .findViewById(R.id.tv_prestoremoney);  
  55.             chargeHoler.extrasmoney = (TextView) chargeView  
  56.                     .findViewById(R.id.tv_extrasmoney);  
  57.             chargeHoler.totalmoney = (TextView) chargeView  
  58.                     .findViewById(R.id.tv_totalmoney);  
  59.             chargeView.setTag(chargeHoler);  
  60.             convertView = chargeView;  
  61.         } else {  
  62.             chargeHoler = (ChargeViewHolder) convertView.getTag();  
  63.         }  
  64.   
  65.         chargeHoler.branchnameCha.setText(DateFormatUtil.formatDate(Long  
  66.                 .valueOf(dataList.get(position).getMr_createtime()))  
  67.                 + " "  
  68.                 + record.getBranch_name());// 消费时间和分店  
  69.         // chargeHoler.prestoremoney.setText(record.getPrestore_money() +  
  70.         // "元");// 存款  
  71.         chargeHoler.extrasmoney.setText(record.getMember_money() + "元");// 余额  
  72.         chargeHoler.totalmoney.setText(record.getMember_money() + "元");// 合计  
  73.     }  
  74.     return convertView;  
  75. }  

上面就是整个效果的实现过程


四、总结

其实为listview的item设置不同的布局文件,达到上面的效果,步骤如下;

1、为不同的item写不同的布局文件,设置统一的javabean类

2、继承BaseAdapter类,实现getItemViewType(int position)和getViewTypeCount() 方法,根据这两个方法,为item设置不同的标记,也就是不同的type

3、在getView方法中,利用getItemViewType(position)方法获取当前的type类型,然后根据不同的type类型,加载不同的item布局文件。

4、其他的一些listview的优化同一般的listview没有很大区别。