一、概述
在项目的需求中,有一处需要显示一个交易记录的列表,这个列表很容易让人联想到用listview来实现,但是这个列表又有稍许不同的地方,那就是它里面的item并不是一样的布局,其中某些部分显示的是消费的记录,而有些地方显示的是充值的记录,也就对应了不同的item布局。而且,这两处地方都是从服务端获取数据的,这两个item的数据对应的类内容也各不相同,该怎么处理呢?
下面来一步步实现这个效果。
二、先看效果图
三、实现步骤
实现的原理就是listview的adapter中的一个关键的方法就是getItemViewType(getItemViewType),这个方法有一个参数是position,有了这个position我们就可以对list集合中的不同位置的数据进行不同的处理,进而标识不同的type,将list中的数据进行分类处理。
首先进行,数据的准备:
在这个项目中,数据源是从服务端获取的json数据,数据的格式如下:
01.
{
02.
"status_code"
:
"0"
,
03.
"result"
: [
04.
{
05.
"mr_content"
: {
06.
"point"
:
"10"
,
07.
"member_money"
:
"100"
,
08.
"pay_money"
:
"300"
,
09.
"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
01.
public
class
ComsumAndChargeRecordBean {
02.
private
String branch_name;
03.
private
String pay_money;
04.
private
String coupon;
//使用特权
05.
private
String activities;
06.
private
String member_money;
07.
private
String cash;
08.
private
String point;
09.
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中进行显示:
布局文件:
01.
<?xml version=
"1.0"
encoding=
"utf-8"
?>
02.
<LinearLayout xmlns:android=
"http://schemas.android.com/apk/res/android"
03.
android:layout_width=
"match_parent"
04.
android:layout_height=
"match_parent"
05.
android:orientation=
"vertical"
>
06.
07.
<include
08.
android:id=
"@+id/traderecord_layout"
09.
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的布局文件就省略了,相信大家都会,这个没什么难度
下面是主界面代码:
01.
<pre
class
=
"java"
name=
"code"
>
protected
void
onCreate(Bundle savedInstanceState) {
02.
// TODO Auto-generated method stub
03.
super
.onCreate(savedInstanceState);
04.
setContentView(R.layout.activity_trade_record);
05.
06.
mListView = (ListView) findViewById(R.id.lv_my_traderecord);
07.
E_TempTradeRecordAdapter adapter =
new
E_TempTradeRecordAdapter(
08.
E_TradeRecordActivity.
this
, myModel.tradeRecordList);
09.
mListView.setAdapter(adapter);
10.
adapter.notifyDataSetChanged();
11.
}
1.
下面是adapter适配器的一部分代码:
字段和构造函数:
01.
private
static
final
String TAG =
"E_TradeRecordAdapter"
;
02.
private
static
final
int
TYPE_COUNT =
2
;
//item类型的总数
03.
private
static
final
int
TYPE_COMSUM =
0
;
//消费类型
04.
private
static
final
int
TYPE_CHARGE =
1
;
//充值类型
05.
private
ArrayList<TradeRecordBean> dataList =
new
ArrayList<TradeRecordBean>();
//数据集合
06.
private
Context mContext;
07.
private
int
currentType;
//当前item类型
08.
09.
public
E_TempTradeRecordAdapter(Context mContext,
10.
ArrayList<TradeRecordBean> dataList) {
11.
super
();
12.
this
.dataList = dataList;
13.
this
.mContext = mContext;
14.
}
几个重要方法:
01.
@Override
02.
public
int
getCount() {
03.
// TODO Auto-generated method stub
04.
return
dataList.size();
05.
}
06.
07.
@Override
08.
public
Object getItem(
int
position) {
09.
// 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。
01.
@Override
02.
public
int
getItemViewType(
int
position) {
03.
// TODO Auto-generated method stub
04.
if
(
"0"
.equals(dataList.get(position).getMr_type())) {
05.
return
TYPE_COMSUM;
// 消费类型
06.
}
else
if
(
"1"
.equals(dataList.get(position).getMr_type())) {
07.
return
TYPE_CHARGE;
// 充值类型
08.
}
else
{
09.
return
100
;
10.
}
11.
}
12.
13.
@Override
14.
public
int
getViewTypeCount() {
15.
return
TYPE_COUNT;
16.
}
viewholder:缓存这几个textview控件
01.
/**
02.
* 消费记录
03.
* @author yl
04.
*
05.
*/
06.
class
ComsumViewHolder {
07.
TextView branchnameCom;
08.
TextView comsumemoney;
09.
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方法:其中有一个关键的方法
1.
currentType = getItemViewType(position);
这个方法获取到当前position的类型,也就是在前面的getItemViewType方法设置的类型。
其中对convertView进行了复用和holder的使用,算是对listview的优化吧。
当currentType == TYPE_COMSUM,消费类型时,加载comsumView = LayoutInflater.from(mContext).inflate( R.layout.traderecord_item_comsume, null);消费类型的布局文件。反之,加载充值类型的布局文件。这样就可以达到为不同的item设置不同的布局文件了。
01.
public
View getView(
int
position, View convertView, ViewGroup parent) {
02.
// TODO Auto-generated method stub
03.
View comsumView =
null
;
04.
View chargeView =
null
;
05.
06.
ComsumAndChargeRecordBean record = (ComsumAndChargeRecordBean) dataList
07.
.get(position).getMr_content();
08.
09.
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没有很大区别。
转自:http://www.it165.net/pro/html/201406/16181.html
另外一篇简单的例子:http://blog.csdn.net/jdsjlzx/article/details/8273661