通过本篇文章,让你掌握新的技巧,请不用只看看一点,希望能够看完,让你很快明白不同的使用场景
ListView 和 Adapter 的基础
工作原理:
- ListView 针对List中每个item,要求 adapter “给我一个视图” (getView)。
- 一个新的视图被返回并显示
如果我们有上亿个项目要显示怎么办?为每个项目创建一个新视图?NO!这不可能!
实际上Android为你缓存了视图。
Android中有个叫做Recycler的构件,下图是他的工作原理:
- 如果你有10亿个项目(item),其中只有可见的项目存在内存中,其他的在Recycler中。
- ListView先请求一个type1视图(getView)然后请求其他可见的项目。convertView在getView中是空(null)的。
- 当item1滚出屏幕,并且一个新的项目从屏幕低端上来时,ListView再请求一个type1视图。convertView此时不是空值了,它的值是item1。你只需设定新的数据然后返回convertView,不必重新创建一个视图。
请看下面的示例代码,这里在getView中使用了System.out进行输出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
public class MultipleItemsListextends ListActivity { private MyCustomAdapter mAdapter; @Override public void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); mAdapter = new MyCustomAdapter(); for ( int i = 0 ; i < 50 ; i++) { mAdapter.addItem( "item " + i); } setListAdapter(mAdapter); } private class MyCustomAdapterextends BaseAdapter { private ArrayList mData = new ArrayList(); private LayoutInflater mInflater; public MyCustomAdapter() { mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); } public void addItem( final String item) { mData.add(item); notifyDataSetChanged(); } @Override public int getCount() { return mData.size(); } @Override public String getItem( int position) { return mData.get(position); } @Override public long getItemId( int position) { return position; } @Override public View getView( int position, View convertView, ViewGroup parent) { System.out.println( "getView " + position + " " + convertView); ViewHolder holder = null ; if (convertView == null ) { convertView = mInflater.inflate(R.layout.item1, null ); holder = new ViewHolder(); holder.textView = (TextView)convertView.findViewById(R.id.text); convertView.setTag(holder); } else { holder = (ViewHolder)convertView.getTag(); } holder.textView.setText(mData.get(position)); return convertView; } } public static class ViewHolder { public TextView textView; } } |
执行程序,然后在Logcat中查看日志
getView 被调用 9 次 ,convertView 对于所有的可见项目是空值(如下)
- 02-05 13:47:32.559: INFO/System.out(947): getView 0 null
- 02-05 13:47:32.570: INFO/System.out(947): getView 1 null
- 02-05 13:47:32.589: INFO/System.out(947): getView 2 null
- 02-05 13:47:32.599: INFO/System.out(947): getView 3 null
- 02-05 13:47:32.619: INFO/System.out(947): getView 4 null
- 02-05 13:47:32.629: INFO/System.out(947): getView 5 null
- 02-05 13:47:32.708: INFO/System.out(947): getView 6 null
- 02-05 13:47:32.719: INFO/System.out(947): getView 7 null
- 02-05 13:47:32.729: INFO/System.out(947): getView 8 null
然后稍微向下滚动List,直到item10出现:
convertView仍然是空值,因为recycler中没有视图(item1的边缘仍然可见,在顶端)
- 02-05 13:48:25.169: INFO/System.out(947): getView 9 null
再滚动List
convertView不是空值了!item1离开屏幕到Recycler中去了,然后item11被创建
- 02-05 13:48:42.879: INFO/System.out(947): getView 10 android.widget.LinearLayout@437430f8
再滚动:
- 02-05 14:01:31.069: INFO/System.out(947): getView 11 android.widget.LinearLayout@437447d0
- 02-05 14:01:31.142: INFO/System.out(947): getView 12 android.widget.LinearLayout@43744ff8
- 02-05 14:01:31.279: INFO/System.out(947): getView 13 android.widget.LinearLayout@43743fa8
- 02-05 14:01:31.350: INFO/System.out(947): getView 14 android.widget.LinearLayout@43745820
- 02-05 14:01:31.429: INFO/System.out(947): getView 15 android.widget.LinearLayout@43746048
- 02-05 14:01:31.550: INFO/System.out(947): getView 16 android.widget.LinearLayout@43746870
- 02-05 14:01:31.669: INFO/System.out(947): getView 17 android.widget.LinearLayout@43747098
- 02-05 14:01:31.839: INFO/System.out(947): getView 18 android.widget.LinearLayout@437478c0
- 02-05 14:03:30.900: INFO/System.out(947): getView 19 android.widget.LinearLayout@43748df0
- 02-05 14:03:32.069: INFO/System.out(947): getView 20 android.widget.LinearLayout@437430f8
convertView 如我们所期待的非空了,在item11离开屏幕之后,它的视图(@437430f8)作为convertView容纳item21了
不同的项目布局(item layout)
我们再举一个稍微复杂的例子,在上例的list中加入一些分隔线
你需要做这些:
- 重(@Override)写 getViewTypeCount() – 返回你有多少个不同的布局
- 重写 getItemViewType(int) – 由position返回view type id
- 根据view item的类型,在getView中创建正确的convertView
以下是代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
public class MultipleItemsListextends ListActivity { private MyCustomAdapter mAdapter; @Override public void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); mAdapter = new MyCustomAdapter(); for ( int i = 1 ; i < 50 ; i++) { mAdapter.addItem( "item " + i); if (i % 4 == 0 ) { mAdapter.addSeparatorItem( "separator " + i); } } setListAdapter(mAdapter); } private class MyCustomAdapterextends BaseAdapter { private static final int TYPE_ITEM = 0 ; private static final int TYPE_SEPARATOR = 1 ; private static final int TYPE_MAX_COUNT = TYPE_SEPARATOR + 1 ; private ArrayList mData = new ArrayList(); private LayoutInflater mInflater; private TreeSet mSeparatorsSet = new TreeSet(); public MyCustomAdapter() { mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); } public void addItem( final String item) { mData.add(item); notifyDataSetChanged(); } public void addSeparatorItem( final String item) { mData.add(item); // save separator position mSeparatorsSet.add(mData.size() - 1 ); notifyDataSetChanged(); } @Override public int getItemViewType( int position) { return mSeparatorsSet.contains(position) ? TYPE_SEPARATOR : TYPE_ITEM; } @Override public int getViewTypeCount() { return TYPE_MAX_COUNT; } @Override public int getCount() { return mData.size(); } @Override public String getItem( int position) { return mData.get(position); } @Override public long getItemId( int position) { return position; } @Override public View getView( int position, View convertView, ViewGroup parent) { ViewHolder holder = null ; int type = getItemViewType(position); System.out.println( "getView " + position + " " + convertView + " type = " + type); if (convertView == null ) { holder = new ViewHolder(); switch (type) { case TYPE_ITEM: convertView = mInflater.inflate(R.layout.item1, null ); holder.textView = (TextView)convertView.findViewById(R.id.text); break ; case TYPE_SEPARATOR: convertView = mInflater.inflate(R.layout.item2, null ); holder.textView = (TextView)convertView.findViewById(R.id.textSeparator); break ; } convertView.setTag(holder); } else { holder = (ViewHolder)convertView.getTag(); } holder.textView.setText(mData.get(position)); return convertView; } } public static class ViewHolder { public TextView textView; } } |
运行程序,你会看到每4个item一个分割线
看看日志,无异常,所有的convertView都是空的
- 02-05 15:19:03.080: INFO/System.out(1035): getView 0 null type = 0
- 02-05 15:19:03.112: INFO/System.out(1035): getView 1 null type = 0
- 02-05 15:19:03.130: INFO/System.out(1035): getView 2 null type = 0
- 02-05 15:19:03.141: INFO/System.out(1035): getView 3 null type = 0
- 02-05 15:19:03.160: INFO/System.out(1035): getView 4 null type = 1
- 02-05 15:19:03.170: INFO/System.out(1035): getView 5 null type = 0
- 02-05 15:19:03.180: INFO/System.out(1035): getView 6 null type = 0
- 02-05 15:19:03.190: INFO/System.out(1035): getView 7 null type = 0
- 02-05 15:19:03.210: INFO/System.out(1035): getView 8 null type = 0
- 02-05 15:19:03.210: INFO/System.out(1035): getView 9 null type = 1
滚动list:
- 02-05 15:19:54.160: INFO/System.out(1035): getView 10 null type = 0
- 02-05 15:19:57.440: INFO/System.out(1035): getView 11 android.widget.LinearLayout@43744528 type = 0
- 02-05 15:20:01.310: INFO/System.out(1035): getView 12 android.widget.LinearLayout@43744eb0 type = 0
- 02-05 15:20:01.880: INFO/System.out(1035): getView 13 android.widget.LinearLayout@437456d8 type = 0
- 02-05 15:20:02.869: INFO/System.out(1035): getView 14 null type = 1
- 02-05 15:20:06.489: INFO/System.out(1035): getView 15 android.widget.LinearLayout@43745f00 type = 0
- 02-05 15:20:07.749: INFO/System.out(1035): getView 16 android.widget.LinearLayout@43747170 type = 0
- 02-05 15:20:10.250: INFO/System.out(1035): getView 17 android.widget.LinearLayout@43747998 type = 0
- 02-05 15:20:11.661: INFO/System.out(1035): getView 18 android.widget.LinearLayout@437481c0 type = 0
- 02-05 15:20:13.180: INFO/System.out(1035): getView 19 android.widget.LinearLayout@437468a0 type = 1
- 02-05 15:20:16.900: INFO/System.out(1035): getView 20 android.widget.LinearLayout@437489e8 type = 0
- 02-05 15:20:25.690: INFO/System.out(1035): getView 21 android.widget.LinearLayout@4374a8d8 type = 0
convertView对于分割线是空的,直到第一个分割线可见,当其离开屏幕,视图去到Recycler并且convertView开始起作用。
转自:http://www.aitinan.com/3885.html
Android高手进阶:Adapter深入理解与优化
一般是针对包含多个元素的View,如ListView,GridView,ExpandableListview,的时候我们是给其设置一个Adapter。Adapter是与View之间提供数据的桥梁,也是提供每个Item的视图桥梁。
以ListView为例,其工作原理为:
● ListView针对List中每个item, adapter都会调用一个getView的方法获得布局视图
●我们一般会Inflate一个新的View,填充数据并返回显示
当然如果我们的Item很多话(比如上万个),都会新建一个View吗?很明显这样内存是接受不了的,Google也不会这么做,Android中有个叫做Recycler的构件,下图是他的工作原理:
很明显,无论数据中是多少个item,在显示上Recycler只存储其中可见的View在内存中。当向下滑动时,顶部不可见Item直接回移动到下方再次填充数据变为新增项。这样就不用每次都新建一个View了。
这个也就是我们在Adapter中常见的getView方法的调用,对应此方法我们就能看出,convertView就是每一Item在Recyler之前的布局视图。
- public View getView(int position, View convertView, ViewGrouppare
所以,Android已经给我们提供了Recycler机制了,我们就应该利用此机制,而不是每次都去inflate一个View。
Example
Don’t
- public View getView(int position, View convertView, ViewGroupparent){
- convertView = LayoutInflater.from(mContext).inflate(R.layout.item_view,null);
- //dosomething…
- return converView;
- }
Do
- public View getView(int position, View convertView, ViewGroupparent){
- if (convertView ==null) {
- convertView =LayoutInflater.from(mContext).inflate(R.layout.item_view, null);
- }
- //dosomething…
- return converView;
- }
ViewHolder的作用
之前所说的Recycler模式是为了解决重复inflate时候造成的View资源浪费,还哪有什么方法何可再次优化我们的性能吗?答案是Yes。
我们还是从getView中的每一个方法调用去查看,发现其实我们拿到convertView的时候,每次都会根据这个布局去findViewById。如下,使我们通常的写法:
findViewById是在解析layout.xml布局那种其中的子View,解析xml是一个力气活,所以Google也建议我们将这个费力不讨好的活优化起来,所以提出了ViewHolder的概念。
即,使用一个静态类,保存xml中的各个子View的引用关系,这样就不必要每次都去解析xml了。如下:就是针对上面代码写的一个ViewHolder
- if (convertView == null) {
- convertView = mInflater.inflate(R.layout.item_view, null);
- }
- TextView titleTextView = (TextView) convertView.findViewById(R.id.text));
- ImageView iconImageView = (ImageView)convertView.findViewButId( R.id.icon));
- //DoSomething…
findViewById是在解析layout.xml布局那种其中的子View,解析xml是一个力气活,所以Google也建议我们将这个费力不讨好的活优化起来,所以提出了ViewHolder的概念。
即,使用一个静态类,保存xml中的各个子View的引用关系,这样就不必要每次都去解析xml了。如下:就是针对上面代码写的一个ViewHolder
- static class ViewHolder {
- TextView titleTextView;
- ImageView iconImageView;
- }
但是,在getView方法中我们只能拿到三个参数,position、convertView、viewGroup是拿不到我们自定义的ViewHolder的。所以,我们希望通过convertView拿到ViewHolder只能将其放在tag里。
下面是一个完整的ViewHolder使用exmaple:
- public View getView(int position, View convertView, ViewGroup parent) {
- ViewHolder holder;
- if (convertView == null) {
- convertView = mInflater.inflate(R.layout.item_view, null);
- holder = new ViewHolder();
- holder.titleTextView = (TextView) convertView.findViewById(R.id.text);
- holder.iconImageView = (ImageView) convertView.findViewById(R.id.icon);
- convertView.setTag(holder);
- } else {
- holder = (ViewHolder) convertView.getTag();
- }
- holder.titleTextView.setText(DATA[pos].title);
- holder.iconImageView.setImageBitmap(DATA[pos].bitmap);
- return convertView;
- }
- static class ViewHolder {
- TextView titleTextView;
- ImageView iconImageView;
- }
Tips. Support.v7中的RecyclerView 就是采用了此思想来制作的。
多个类型的ViewType
当我们在Adapter中调用方法getView的时候,如果整个列表中的Item View如果有多种类型布局,如:
我们继续使用convertView来将数据从新填充貌似不可行了,因为每次返回的convertView类型都不一样,无法重用。
Android在设计上的时候,也想到了这点。所以,在adapter中预留的两个方法。
- public int getItemViewType(int position) ;
- public int getViewTypeCount();
只需要重新这两个方法,设置一下ItemViewType的个数和判断方法,Recycler就能有选择性的给出不同的convertView了。
Example:
- @Override
- public intgetItemViewType(int position) {
- if (DATA[pos].type == 0) {
- return 0;
- } else {
- return 1;
- }
- }
- @Override
- public int getViewTypeCount() {
- return 2;
- }
- @Override
- public View getView(int position, View convertView, ViewGroup arg2) {
- TitleViewHolder titleHolder;
- InfoViewHolder infoHolder;
- int type = getItemViewType(position);
- if (convertView == null) {
- switch (type) {
- case 0:
- convertView = mInflater.inflate(R.layout.item_view, null);
- titleHolder = new TitleViewHolder();
- titleHolder.titleTextView = (TextView) convertView.findViewById(R.id.text);
- titleHolder.iconImageView = (ImageView) convertView.findViewById(R.id.icon);
- convertView.setTag(titleHolder);
- break;
- case 1:
- convertView = mInflater.inflate(R.layout.item_view2, null);
- infoHolder = new InfoViewHolder();
- infoHolder.titleTextView = (TextView) convertView.findViewById(R.id.text);
- convertView.setTag(infoHolder);
- break;
- }
- } else {
- switch (type) {
- case 0:
- titleHolder = (TitleViewHolder) convertView.getTag();
- break;
- case 1:
- infoHolder = (InfoViewHolder) convertView.getTag();
- break;
- }
- }
- switch (type) {
- case 0:
- titleHolder.titleTextView.setText(DATA[pos].title);
- break;
- case 1:
- infoHolder.titleTextView.setText(DATA[pos].title);
- infoHolder.iconImageView.setImageBitmap(DATA[pos].bitmap);
- break;
- }
- return convertView;
- }
- static class TitleViewHolder {
- public ImageView iconImageView;
- public TextView titleTextView;
- }
- static class InfoViewHolder {
- TextView titleTextView;
- ImageView iconImageView;
- }
NotifyDataSetChanged刷新机制
当ListView中的数据发生了改变,我们希望刷新ListView中的View时,我们一般会调用NotifyDataSetChanged来刷新ListView。看一下它的源码:
- public void notifyChanged() {
- synchronized (mObservers) {
- // 向每一个子View发送onChanged
- for (int i = mObservers.size() – 1; i >= 0; i–) {
- mObservers.get(i).onChanged();
- }
- }
- }
发 现它针对每一个子View都做了刷新,当然,如果我们的数据都变量还可以理解。但是,一般条件下,我们需要更新的View不多。频繁的调用 NotifyDataSetChanged方法,刷新整个界面不合适。这样会把界面上显示的所有item都全部重绘一次,即使只有一个view的内容发生 了变化。
所以,我们可以写一个update的方法,来单独刷新一个View
- private void updateView(int itemIndex){
- intvisiblePosition = yourListView.getFirstVisiblePosition();
- Viewv = yourListView.getChildAt(itemIndex – visiblePosition);
- ViewHolder viewHolder =(ViewHolder)v.getTag();
- if(viewHolder!= null){
- viewHolder.titleTextView.setText(“我更新了”);
- }
- }
Adapter中的网络图片优化
ListView中的每一项Item基本都会带着网络图片,当item比较多的时候,过多的网络请求和过多的图片存储都会是ListView变慢变卡。
所以针对其做一下优化:
● 采用线程池进行网络图片请求,网络图片请求获取后使用本地缓存处理(LRUCache),内存+本地文件缓存。当然,为了防止内存溢出与回收不及时,需要使用弱引用(WeakReference)来存储内存中的图片。
● 对网络中取到的图片进行按比例缩放,以减少内存消耗。
● 滑动的时候不需要对网络图片进行请求。因为,网络请求一般比较耗时,某Item的图片,在请求来的时候如果被Recycler换掉,图片就会对应不上该Item。
Tips.网络请求的工具类比较多不方便举例子,但是使用比较频繁的网络图片请求工具类就是Volley了,Volley提供了一个ImageLoader的工具类和NetworkImageView的网络图片请求View
本文链接:http://www.eoeandroid.com/thread-536377-1-1.html
Android应用之利用getItemViewType为Listview的item设置不同的布局
一、概述
在项目的需求中,有一处需要显示一个交易记录的列表,这个列表很容易让人联想到用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