一、概述
在项目的需求中,有一处需要显示一个交易记录的列表,这个列表很容易让人联想到用listview来实现,但是这个列表又有稍许不同的地方,那就是它里面的item并不是一样的布局,其中某些部分显示的是消费的记录,而有些地方显示的是充值的记录,也就对应了不同的item布局。而且,这两处地方都是从服务端获取数据的,这两个item的数据对应的类内容也各不相同,该怎么处理呢?
下面来一步步实现这个效果。
二、先看效果图
三、实现步骤
实现的原理就是listview的adapter中的一个关键的方法就是getItemViewType(getItemViewType),这个方法有一个参数是position,有了这个position我们就可以对list集合中的不同位置的数据进行不同的处理,进而标识不同的type,将list中的数据进行分类处理。
首先进行,数据的准备:
在这个项目中,数据源是从服务端获取的json数据,数据的格式如下:
{ "status_code": "0", "result": [ { "mr_content": { "point": "10", "member_money": "100", "pay_money": "300", "cash": "200", "bonus": "消费满200元立减50元餐券1张", "activities": "三锅鸡1元任吃", "coupon": "满100送50", "branch_name": "四海一家" }, "mr_id": "25", "mr_createtime": "1333333333", "mr_type": "0", "user_id": "108", "merchant_id": "1", "branch_id": "1", "branch_name": "ffff" }, { "mr_content": { "member_money": "300", "branch_name": "四海一家" }, "mr_id": "30", "mr_createtime": "1333333333", "mr_type": "1", "user_id": "108", "merchant_id": "1", "branch_id": "1", "branch_name": "fff" } ], "status_desc": "ok" }
可以看到其中mr_content这个字段,是一个自定义对象,但是两个mr_content的内容不同,这里是分别为mr_content的内容定义两个不同的类还是如何处理呢?
一开始,我是分别为两个mr_content定义不同的类,后来发现这样行不通,因为这样做的话定义外层类的时候mr_content就无法指定数据类型了。所以,最后采用某人的方法将mr_content定义为一个类,将两个不同的mr_content的字段都定义进去,解析的时候不会出现问题,没有数据会显示null
下面是我定义的mr_content字段的数据类型ComsumAndChargeRecordBean
public class ComsumAndChargeRecordBean { private String branch_name; private String pay_money; private String coupon;//使用特权 private String activities; private String member_money; private String cash; private String point; private String bonus; // private String prestore_money;//预存款 public String getBranch_name() { return branch_name; } public void setBranch_name(String branch_name) { this.branch_name = branch_name; } public String getPay_money() { return pay_money; } public void setPay_money(String pay_money) { this.pay_money = pay_money; } public String getCoupon() { return coupon; } public void setCoupon(String coupon) { this.coupon = coupon; } public String getActivities() { return activities; } public void setActivities(String activities) { this.activities = activities; } public String getMember_money() { return member_money; } public void setMember_money(String member_money) { this.member_money = member_money; } public String getCash() { return cash; } public void setCash(String cash) { this.cash = cash; } public String getPoint() { return point; } public void setPoint(String point) { this.point = point; } public String getBonus() { return bonus; } public void setBonus(String bonus) { this.bonus = bonus; } }
数据准备好了,下面是传入listview中进行显示:
布局文件:
<?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="match_parent" android:orientation="vertical" > <include android:id="@+id/traderecord_layout" layout="@layout/topview_activity" /> <ListView android:id="@+id/lv_my_traderecord" android:layout_width="match_parent" android:layout_height="match_parent" > </ListView> </LinearLayout>
两个不同item的布局文件就省略了,相信大家都会,这个没什么难度
下面是主界面代码:
<pre class="java" name="code"> protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.activity_trade_record); mListView = (ListView) findViewById(R.id.lv_my_traderecord); E_TempTradeRecordAdapter adapter = new E_TempTradeRecordAdapter( E_TradeRecordActivity.this, myModel.tradeRecordList); mListView.setAdapter(adapter); adapter.notifyDataSetChanged(); }
下面是adapter适配器的一部分代码:
字段和构造函数:
private static final String TAG = "E_TradeRecordAdapter"; private static final int TYPE_COUNT = 2;//item类型的总数 private static final int TYPE_COMSUM = 0;//消费类型 private static final int TYPE_CHARGE = 1;//充值类型 private ArrayList<TradeRecordBean> dataList = new ArrayList<TradeRecordBean>();//数据集合 private Context mContext; private int currentType;//当前item类型 public E_TempTradeRecordAdapter(Context mContext, ArrayList<TradeRecordBean> dataList) { super(); this.dataList = dataList; this.mContext = mContext; }
几个重要方法:
@Override public int getCount() { // TODO Auto-generated method stub return dataList.size(); } @Override public Object getItem(int position) { // TODO Auto-generated method stub return dataList.get(position); } @Override public long getItemId(int position) { // TODO Auto-generated method stub return position; }
获取子item的类型 获取类型的数量 这里是根据字段Mr_type来确定的,json数据里面是根据这个字段来确定消费记录的类型的。总之,在为item设置不同的布局的时候肯定有一个标记用来区分不同的item,你可以用这个作为判断的标记,来设置不同的type。
@Override public int getItemViewType(int position) { // TODO Auto-generated method stub if ("0".equals(dataList.get(position).getMr_type())) { return TYPE_COMSUM;// 消费类型 } else if ("1".equals(dataList.get(position).getMr_type())) { return TYPE_CHARGE;// 充值类型 } else { return 100; } } @Override public int getViewTypeCount() { return TYPE_COUNT; }
viewholder:缓存这几个textview控件
/** * 消费记录 * @author yl * */ class ComsumViewHolder { TextView branchnameCom; TextView comsumemoney; TextView useprevillage; TextView yuezhifu; TextView cash; TextView thisscore; TextView extrareward; TextView prestoremoney; } /** * 充值记录 * @author yl * */ class ChargeViewHolder { TextView branchnameCha; TextView prestoremoney; TextView extrasmoney; TextView totalmoney; }
最后是getview方法:其中有一个关键的方法
currentType = getItemViewType(position);
这个方法获取到当前position的类型,也就是在前面的getItemViewType方法设置的类型。
其中对convertView进行了复用和holder的使用,算是对listview的优化吧。
当currentType == TYPE_COMSUM,消费类型时,加载comsumView = LayoutInflater.from(mContext).inflate( R.layout.traderecord_item_comsume, null);消费类型的布局文件。反之,加载充值类型的布局文件。这样就可以达到为不同的item设置不同的布局文件了。
public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub View comsumView = null; View chargeView = null; ComsumAndChargeRecordBean record = (ComsumAndChargeRecordBean) dataList .get(position).getMr_content(); currentType = getItemViewType(position); if (currentType == TYPE_COMSUM) { ComsumViewHolder comsumHolder = null; if (convertView == null) { comsumHolder = new ComsumViewHolder(); comsumView = LayoutInflater.from(mContext).inflate( R.layout.traderecord_item_comsume, null); comsumHolder.branchnameCom = (TextView) comsumView .findViewById(R.id.tv_branch_name); comsumHolder.comsumemoney = (TextView) comsumView .findViewById(R.id.tv_comsumemoney); comsumHolder.useprevillage = (TextView) comsumView .findViewById(R.id.tv_useprevillage); comsumHolder.yuezhifu = (TextView) comsumView .findViewById(R.id.tv_yuezhifu); comsumHolder.cash = (TextView) comsumView .findViewById(R.id.tv_cash); comsumHolder.thisscore = (TextView) comsumView .findViewById(R.id.tv_thisscore); comsumHolder.extrareward = (TextView) comsumView .findViewById(R.id.tv_extrareward); comsumView.setTag(comsumHolder); convertView = comsumView; } else { comsumHolder = (ComsumViewHolder) convertView.getTag(); } comsumHolder.branchnameCom.setText(DateFormatUtil.formatDate(Long .valueOf(dataList.get(position).getMr_createtime())) + " " + record.getBranch_name());// 消费时间和分店 comsumHolder.comsumemoney.setText(record.getPay_money());// 消费金额 comsumHolder.useprevillage.setText(record.getCoupon());// 使用特权 comsumHolder.yuezhifu.setText(record.getMember_money());// 余额支付 comsumHolder.cash.setText(record.getCash());// 现金支付 comsumHolder.thisscore.setText(record.getPoint());// 本次积分 comsumHolder.extrareward.setText(record.getBonus());// 额外奖励 } else if (currentType == TYPE_CHARGE) { ChargeViewHolder chargeHoler = null; if (convertView == null) { chargeHoler = new ChargeViewHolder(); chargeView = LayoutInflater.from(mContext).inflate( R.layout.traderecord_item_chongzhi, null); chargeHoler.branchnameCha = (TextView) chargeView .findViewById(R.id.tv_branchname_charge); chargeHoler.prestoremoney = (TextView) chargeView .findViewById(R.id.tv_prestoremoney); chargeHoler.extrasmoney = (TextView) chargeView .findViewById(R.id.tv_extrasmoney); chargeHoler.totalmoney = (TextView) chargeView .findViewById(R.id.tv_totalmoney); chargeView.setTag(chargeHoler); convertView = chargeView; } else { chargeHoler = (ChargeViewHolder) convertView.getTag(); } chargeHoler.branchnameCha.setText(DateFormatUtil.formatDate(Long .valueOf(dataList.get(position).getMr_createtime())) + " " + record.getBranch_name());// 消费时间和分店 // chargeHoler.prestoremoney.setText(record.getPrestore_money() + // "元");// 存款 chargeHoler.extrasmoney.setText(record.getMember_money() + "元");// 余额 chargeHoler.totalmoney.setText(record.getMember_money() + "元");// 合计 } return convertView; }
上面就是整个效果的实现过程
四、总结
其实为listview的item设置不同的布局文件,达到上面的效果,步骤如下;
1、为不同的item写不同的布局文件,设置统一的javabean类
2、继承BaseAdapter类,实现getItemViewType(int position)和getViewTypeCount() 方法,根据这两个方法,为item设置不同的标记,也就是不同的type
3、在getView方法中,利用getItemViewType(position)方法获取当前的type类型,然后根据不同的type类型,加载不同的item布局文件。
4、其他的一些listview的优化同一般的listview没有很大区别。
Android应用之——利用getItemViewType为Listview的item设置不同的布局