0

Adapter的getViewTypeCount和getItemViewType 使用

通过本篇文章,让你掌握新的技巧,请不用只看看一点,希望能够看完,让你很快明白不同的使用场景

ListView 和 Adapter 的基础

工作原理:

  1. ListView 针对List中每个item,要求 adapter “给我一个视图” (getView)。
  2. 一个新的视图被返回并显示

如果我们有上亿个项目要显示怎么办?为每个项目创建一个新视图?NO!这不可能!

实际上Android为你缓存了视图。

Android中有个叫做Recycler的构件,下图是他的工作原理:

  1. 如果你有10亿个项目(item),其中只有可见的项目存在内存中,其他的在Recycler中。
  2. ListView先请求一个type1视图(getView)然后请求其他可见的项目。convertView在getView中是空(null)的。
  3. 当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 对于所有的可见项目是空值(如下)

 

  1. 02-05 13:47:32.559: INFO/System.out(947): getView 0 null
  2. 02-05 13:47:32.570: INFO/System.out(947): getView 1 null
  3. 02-05 13:47:32.589: INFO/System.out(947): getView 2 null
  4. 02-05 13:47:32.599: INFO/System.out(947): getView 3 null
  5. 02-05 13:47:32.619: INFO/System.out(947): getView 4 null
  6. 02-05 13:47:32.629: INFO/System.out(947): getView 5 null
  7. 02-05 13:47:32.708: INFO/System.out(947): getView 6 null
  8. 02-05 13:47:32.719: INFO/System.out(947): getView 7 null
  9. 02-05 13:47:32.729: INFO/System.out(947): getView 8 null

然后稍微向下滚动List,直到item10出现:

 

convertView仍然是空值,因为recycler中没有视图(item1的边缘仍然可见,在顶端)

  1. 02-05 13:48:25.169: INFO/System.out(947): getView 9 null

再滚动List

 

convertView不是空值了!item1离开屏幕到Recycler中去了,然后item11被创建

  1. 02-05 13:48:42.879: INFO/System.out(947): getView 10 android.widget.LinearLayout@437430f8

再滚动:

  1. 02-05 14:01:31.069: INFO/System.out(947): getView 11 android.widget.LinearLayout@437447d0
  2. 02-05 14:01:31.142: INFO/System.out(947): getView 12 android.widget.LinearLayout@43744ff8
  3. 02-05 14:01:31.279: INFO/System.out(947): getView 13 android.widget.LinearLayout@43743fa8
  4. 02-05 14:01:31.350: INFO/System.out(947): getView 14 android.widget.LinearLayout@43745820
  5. 02-05 14:01:31.429: INFO/System.out(947): getView 15 android.widget.LinearLayout@43746048
  6. 02-05 14:01:31.550: INFO/System.out(947): getView 16 android.widget.LinearLayout@43746870
  7. 02-05 14:01:31.669: INFO/System.out(947): getView 17 android.widget.LinearLayout@43747098
  8. 02-05 14:01:31.839: INFO/System.out(947): getView 18 android.widget.LinearLayout@437478c0
  9. 02-05 14:03:30.900: INFO/System.out(947): getView 19 android.widget.LinearLayout@43748df0
  10. 02-05 14:03:32.069: INFO/System.out(947): getView 20 android.widget.LinearLayout@437430f8

convertView 如我们所期待的非空了,在item11离开屏幕之后,它的视图(@437430f8)作为convertView容纳item21了

不同的项目布局(item layout)

我们再举一个稍微复杂的例子,在上例的list中加入一些分隔线

你需要做这些:

  1. 重(@Override)写 getViewTypeCount() – 返回你有多少个不同的布局
  2. 重写 getItemViewType(int) – 由position返回view type id
  3. 根据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都是空的

  1. 02-05 15:19:03.080: INFO/System.out(1035): getView 0 null type = 0
  2. 02-05 15:19:03.112: INFO/System.out(1035): getView 1 null type = 0
  3. 02-05 15:19:03.130: INFO/System.out(1035): getView 2 null type = 0
  4. 02-05 15:19:03.141: INFO/System.out(1035): getView 3 null type = 0
  5. 02-05 15:19:03.160: INFO/System.out(1035): getView 4 null type = 1
  6. 02-05 15:19:03.170: INFO/System.out(1035): getView 5 null type = 0
  7. 02-05 15:19:03.180: INFO/System.out(1035): getView 6 null type = 0
  8. 02-05 15:19:03.190: INFO/System.out(1035): getView 7 null type = 0
  9. 02-05 15:19:03.210: INFO/System.out(1035): getView 8 null type = 0
  10. 02-05 15:19:03.210: INFO/System.out(1035): getView 9 null type = 1

滚动list:

  1. 02-05 15:19:54.160: INFO/System.out(1035): getView 10 null type = 0
  2. 02-05 15:19:57.440: INFO/System.out(1035): getView 11 android.widget.LinearLayout@43744528 type = 0
  3. 02-05 15:20:01.310: INFO/System.out(1035): getView 12 android.widget.LinearLayout@43744eb0 type = 0
  4. 02-05 15:20:01.880: INFO/System.out(1035): getView 13 android.widget.LinearLayout@437456d8 type = 0
  5. 02-05 15:20:02.869: INFO/System.out(1035): getView 14 null type = 1
  6. 02-05 15:20:06.489: INFO/System.out(1035): getView 15 android.widget.LinearLayout@43745f00 type = 0
  7. 02-05 15:20:07.749: INFO/System.out(1035): getView 16 android.widget.LinearLayout@43747170 type = 0
  8. 02-05 15:20:10.250: INFO/System.out(1035): getView 17 android.widget.LinearLayout@43747998 type = 0
  9. 02-05 15:20:11.661: INFO/System.out(1035): getView 18 android.widget.LinearLayout@437481c0 type = 0
  10. 02-05 15:20:13.180: INFO/System.out(1035): getView 19 android.widget.LinearLayout@437468a0 type = 1
  11. 02-05 15:20:16.900: INFO/System.out(1035): getView 20 android.widget.LinearLayout@437489e8 type = 0
  12. 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

  1. public View getView(int position, View convertView, ViewGroupparent){
  2.     convertView = LayoutInflater.from(mContext).inflate(R.layout.item_view,null);
  3.     //dosomething…  
  4.     return converView;
  5. }

Do

  1. public View getView(int position, View convertView, ViewGroupparent){
  2.      if (convertView ==null) {
  3.            convertView =LayoutInflater.from(mContext).inflate(R.layout.item_view, null);
  4.      }
  5.     //dosomething…  
  6.     return converView;
  7. }

ViewHolder的作用

之前所说的Recycler模式是为了解决重复inflate时候造成的View资源浪费,还哪有什么方法何可再次优化我们的性能吗?答案是Yes。

我们还是从getView中的每一个方法调用去查看,发现其实我们拿到convertView的时候,每次都会根据这个布局去findViewById。如下,使我们通常的写法:

findViewById是在解析layout.xml布局那种其中的子View,解析xml是一个力气活,所以Google也建议我们将这个费力不讨好的活优化起来,所以提出了ViewHolder的概念。

即,使用一个静态类,保存xml中的各个子View的引用关系,这样就不必要每次都去解析xml了。如下:就是针对上面代码写的一个ViewHolder

  1. if (convertView == null) {
  2.    convertView = mInflater.inflate(R.layout.item_view, null);
  3. }
  4. TextView titleTextView = (TextView) convertView.findViewById(R.id.text));
  5. ImageView iconImageView = (ImageView)convertView.findViewButId( R.id.icon));
  6. //DoSomething…  

findViewById是在解析layout.xml布局那种其中的子View,解析xml是一个力气活,所以Google也建议我们将这个费力不讨好的活优化起来,所以提出了ViewHolder的概念。

即,使用一个静态类,保存xml中的各个子View的引用关系,这样就不必要每次都去解析xml了。如下:就是针对上面代码写的一个ViewHolder

  1. static class ViewHolder {
  2.     TextView titleTextView;
  3.     ImageView iconImageView;
  4. }

但是,在getView方法中我们只能拿到三个参数,position、convertView、viewGroup是拿不到我们自定义的ViewHolder的。所以,我们希望通过convertView拿到ViewHolder只能将其放在tag里。

下面是一个完整的ViewHolder使用exmaple:

  1. public View getView(int position, View convertView, ViewGroup parent) {
  2.     ViewHolder holder;
  3.     if (convertView == null) {
  4.         convertView = mInflater.inflate(R.layout.item_view, null);
  5.         holder = new ViewHolder();
  6.         holder.titleTextView = (TextView) convertView.findViewById(R.id.text);
  7.         holder.iconImageView = (ImageView) convertView.findViewById(R.id.icon);
  8.         convertView.setTag(holder);
  9.     } else {
  10.         holder = (ViewHolder) convertView.getTag();
  11.     }
  12.     holder.titleTextView.setText(DATA[pos].title);
  13.     holder.iconImageView.setImageBitmap(DATA[pos].bitmap);
  14.     return convertView;
  15. }
  16. static class ViewHolder {
  17.     TextView titleTextView;
  18.     ImageView iconImageView;
  19. }

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:

  1. @Override
  2. public intgetItemViewType(int position) {
  3.     if (DATA[pos].type == 0) {
  4.         return 0;
  5.     } else {
  6.         return 1;
  7.     }
  8. }
  9. @Override
  10. public int getViewTypeCount() {
  11.     return 2;
  12. }
  13. @Override
  14. public View getView(int position, View convertView, ViewGroup arg2) {
  15.     TitleViewHolder titleHolder;
  16.     InfoViewHolder infoHolder;
  17.     int type = getItemViewType(position);
  18.     if (convertView == null) {
  19.         switch (type) {
  20.         case 0:
  21.             convertView = mInflater.inflate(R.layout.item_view, null);
  22.             titleHolder = new TitleViewHolder();
  23.             titleHolder.titleTextView = (TextView) convertView.findViewById(R.id.text);
  24.             titleHolder.iconImageView = (ImageView) convertView.findViewById(R.id.icon);
  25.             convertView.setTag(titleHolder);
  26.             break;
  27.         case 1:
  28.             convertView = mInflater.inflate(R.layout.item_view2, null);
  29.             infoHolder = new InfoViewHolder();
  30.             infoHolder.titleTextView = (TextView) convertView.findViewById(R.id.text);
  31.             convertView.setTag(infoHolder);
  32.             break;
  33.         }
  34.     } else {
  35.         switch (type) {
  36.         case 0:
  37.             titleHolder = (TitleViewHolder) convertView.getTag();
  38.             break;
  39.         case 1:
  40.             infoHolder = (InfoViewHolder) convertView.getTag();
  41.             break;
  42.         }
  43.     }
  44.     switch (type) {
  45.     case 0:
  46.         titleHolder.titleTextView.setText(DATA[pos].title);
  47.         break;
  48.     case 1:
  49.         infoHolder.titleTextView.setText(DATA[pos].title);
  50.         infoHolder.iconImageView.setImageBitmap(DATA[pos].bitmap);
  51.         break;
  52.     }
  53.     return convertView;
  54. }
  55. static class TitleViewHolder {
  56.     public ImageView iconImageView;
  57.     public TextView titleTextView;
  58. }
  59. static class InfoViewHolder {
  60.     TextView titleTextView;
  61.     ImageView iconImageView;
  62. }

NotifyDataSetChanged刷新机制

当ListView中的数据发生了改变,我们希望刷新ListView中的View时,我们一般会调用NotifyDataSetChanged来刷新ListView。看一下它的源码:

  1. public void notifyChanged() {
  2.     synchronized (mObservers) {
  3.         // 向每一个子View发送onChanged  
  4.         for (int i = mObservers.size() – 1; i >= 0; i–) {
  5.             mObservers.get(i).onChanged();
  6.         }
  7.     }
  8. }

发 现它针对每一个子View都做了刷新,当然,如果我们的数据都变量还可以理解。但是,一般条件下,我们需要更新的View不多。频繁的调用 NotifyDataSetChanged方法,刷新整个界面不合适。这样会把界面上显示的所有item都全部重绘一次,即使只有一个view的内容发生 了变化。

所以,我们可以写一个update的方法,来单独刷新一个View

  1. private void updateView(int itemIndex){
  2.     intvisiblePosition = yourListView.getFirstVisiblePosition();
  3.     Viewv = yourListView.getChildAt(itemIndex – visiblePosition);
  4.          ViewHolder viewHolder =(ViewHolder)v.getTag();
  5.          if(viewHolder!= null){
  6.                viewHolder.titleTextView.setText(“我更新了”);
  7.          }
  8. }

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.}

下面是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

天边的星星