基于Android平台的通用Adapter适配器的设计与实现

2016-06-16 20:22武伟
电脑知识与技术 2016年10期
关键词:适配器

武伟

摘要:Android开发中,ListView列表是使用率最高的UI组件,传统开发模式中,ListView的代码编写比较繁琐,项目中充斥着大量的Adapter适配器,该文在分析ListView开发原理的基础上,设计并实现一个可以通用于各种ListView场景开发的Adapter适配器,使得无论多少ListView,只需一个Adapter适配器,从而解决项目开发中代码高冗余、高复杂、管理麻烦等实际问题。

关键词:Android;ListView;Adapter;适配器

中图分类号:TP393 文献标识码:A 文章编号:1009-3044(2016)10-0099-03

移动开发方兴未艾,如火如荼,各种App使我们的生活和工作变得快捷方便,Android平台作为两大移动操作系统之一,占据了移动开发的大半壁江山。

在Android App的开发实践中,ListView列表是使用最为频繁的UI组件,也是最为重要的内容呈现方式,几乎所有的App中都使用了数量不等的各种列表。众所周知,Android开发采用经典的MVC模式,因为ListView的这种重要性和普适性,Android系统在设计上使用MVC对其进行了精心的设计,一个ListView由三部分组成:

数据源:显示在界面上的具体数据,由数组或数据库等提供,对应于MVC中的M(模型)

Adapter适配器:将数据绑定到显示组件,并提供对数据的操作,对应于MVC中的C(控制器)

ListView组件:显示数据,响应用户输入,对应于MVC中的V(视图)

三者的关系如下图:

这种模式中,Adapter适配器作为数据和显示之间的桥梁,是最为关键的一部分,每当需要显示列表中的某一项时,都会调用Adapter的getView方法返回一个View。在具体的开发中,对于每一个列表页面,我们通常要编写4个文件:

1)Activity的布局文件

2) Activity的类文件

3)ListView中Item的布局文件

4) Adapter的类文件

当项目比较简单时,这种设计有利于视图层和业务层分离解耦,从而提高灵活性和复用性,但是当项目中列表页面比较多时,每一个列表都需要一个Adapter文件,很容易导致源文件数量比较多,同时大量的Adapter文件中包含了很多相似代码,从而使得代码管理的复杂度和冗余度增大,进而大大降低这种设计所带来的好处。如果可以设计实现一个通用的Adapter可以适配各种不同的列表,则可以将这种副作用降到最低,从而提高我们的代码质量。

考虑以下几种常见的列表页面:

列表1的Item包含2行文字,列表2的Item包含一个图片和2行文字,其布局不同,Adapter的处理也不尽相同,为了适用各种不同的布局和数据,我们可以使用Java的泛型技术来实现,下面是Adapter的实现代码:

1

2 public abstract class CommonAdapter extends BaseAdapter

3 {

4 protected Context iContext;// 上下文

5 protected List iListData;// 列表要呈现的数据

6 private int iLayoutId;// 列表Item的布局id

7

8 public CommonAdapter( Context aContext, List aListData, int aLayoutId )

9 {

10 iContext = aContext;

11 iListData = aListData;

12 iLayoutId = aLayoutId;

13 }

14

15 @Override

16 public int getCount()

17 {

18 return iListData.size();

19 }

20

21 @Override

22 public T getItem(int position)

23 {

24 return iListData.get(position);

25 }

26

27 @Override

28 public long getItemId(int position)

29 {

30 return position;

31 }

32

33 @Override

34 public View getView( int position, View convertView, ViewGroup parent )

35 {

36 ViewHolder holder = ViewHolder.get( iContext, convertView, parent, 37 iLayoutId, position 1);

38 convert( holder, getItem(position) );

39 return holder.getConvertView();

40 }

41

42 public abstract void convert( ViewHolder holder, T t );

43 }

这段代码与我们平时所写的Adapter十分相似,主要有以下区别:

1) 行2,该类声明为抽象类,并使用泛型声明;

2) 行5和行8,列表数据使用泛型声明,与行2结合,则可适配各种列表数据类型;

3) 行34的getView()方法与平时有所区别,这里使用了自定义的ViewHolder类来返回每一行的视图,一方面可以优化显示效率,另一方面通过该类完成通用适配的主要内容;

4) 行42,定义了一个convert() 抽象方法,我们在使用通用Adapter进行具体开发时,必须通过重载此方法来设置Item的数据。

CommonAdapter中使用的ViewHolder是实现通用Adapter的重点部分,其代码如下:

1 public class ViewHolder

2 {

3 private SparseArray iViews;

4 private int iPosition;

5 private View iConvertView;

6 private Context iContext;

7 private int iLayoutId;

8

9 public ViewHolder( Context aContext, ViewGroup aParent, int aLayoutId, int

10 aPosition )

11 {

12 iContext = aContext;

13 iLayoutId = aLayoutId;

14 iPosition = aPosition;

15 iViews = new SparseArray();

16 iConvertView = LayoutInflater.from( aContext).inflate(aLayoutId, aParent,

17 false );

18 iConvertView.setTag(this);

19 }

20

21 public static ViewHolder get( Context aContext, View convertView, ViewGroup

22 aParent, int aLayoutId, int aPosition )

23 {

24 if (convertView == null)

25 return new ViewHolder( aContext, aParent, aLayoutId, aPosition );

26 else{

27 ViewHolder holder = (ViewHolder)convertView.getTag();

28 holder.iPosition = aPosition;

29 return holder;

30 }

31 }

32

33 public T getView( int aViewId )

34 {

35 View view = iViews.get( aViewId );

36 if (view == null)

37 {

38 view = iConvertView.findViewById( aViewId );

39 iViews.put( aViewId, view );

40 }

41 return (T) view;

42 }

43

44 public View getConvertView()

45 {

46 return iConvertView;

47 }

48

49 public ViewHolder setText( int aViewId, String aText )

50 {

51 TextView tv = getView( aViewId );

52 tv.setText( aText );

53 return this;

54 }

55

56 public ViewHolder setImageDrawable( int aViewId, Drawable aDrawable )

57 {

58 ImageView view = getView( aViewId );

59 view.setImageDrawable( aDrawable );

60 return this;

61 }

62 }

其中重点的部分是:

1) 行3,使用稀疏数组保存View,因为实际使用时各种列表的Item布局是不同的,为了达到通用的目的,我们需要一个容器来保存Item布局中的所有控件,并且可以通过控件Id找到对应控件,这个容器也可以使用Map,不过出于性能考虑,这里使用稀疏数组;

2) 行18,使用setTag()方法将convertView与ViewHolder对象进行绑定,当返回convertView时,使用getTag()方法拿到布局中的控件,从而提高效率;

3) 行33,getView()方法是ViewHolder的重点方法,同样使用泛型,与CommonAdapter的泛型结合,从而适配各种不同的Item布局;

4) 行49开始的setText()和setImageDrawable()方法:实践中,绝大多数情况下,列表Item的布局中所使用到的控件,不过就是文本、图片、按钮、单选/复选等几种,所以我们可以在ViewHolder中将设置文本/图片/属性/事件处理等功能进行封装,限于篇幅,这里只封装了设置文本和图片,读者可根据需要自行添加其他功能。

有了CommonAdapter和ViewHolder这2个类,一个通用的Adapter适配器就可以完成了,无论项目中使用了多少列表,我们也无需几十个Adapter满天飞了;使用该通用Adapter来完成前例中“列表2”的具体代码如下:

1 protected void onCreate(Bundle savedInstanceState) {

2 super.onCreate(savedInstanceState);

3 setContentView(R.layout.activity_list2);

4

5 InitListData(); // 初始化列表数据,可静态赋值或数据库读取

6 listView = (ListView)findViewById( R.id.list2 );

7 listView.setAdapter( new CommonAdapter

8 getApplicationContext(), listData, R.layout.layout_item2 )

9 {

10 @Override

11 public void convert( ViewHolder helper, List2Node item )

12 {

13 helper.setText( R.id.tv_name, item.line1 );

14 helper.setText( R.id.tv_ename, item.line2 );

15 helper.setImageDrawable( R.id.image, item.image );

16 }

17 });

18 }

我们只需要根据Item布局重载convert()方法并设置数据,代码简洁明了,神清气爽!

通过以上的实践,我们利用泛型技术结合面向对象思想,设计实现了一个可用于各种列表的通用Adapter,并在实际项目中成功地应用,使用该技术,可大大减少代码冗余,提高代码复用性,增强代码质量,降低项目管理的难度,具有较强的实用性。

参考文献:

[1] Android Developers[EB/Ol].http://developer.android.com.

[2] Bruce Eckel. Java编程思想[M]. 4版.北京: 机械工业出版社, 2007.

[3] 邓文渊. Android开发基础教程[M]. 北京: 人民邮电出版社, 2014.

[4] Bill Phillips, Brian Hardy. Android编程权威指南[M]. 北京: 人民邮电出版社, 2014.

[5] 李继勇. Android UI设计[M]. 北京: 机械工业出版社, 2015.

猜你喜欢
适配器
基于超声相控阵的卫星适配器缺陷检测方法研究
适配器模式及其应用
基于3D打印的轻型导弹适配器
新型水文测验GPS适配器设计与应用
基于蓝牙串口适配器的GPS接收机与AutoCAD的实时无线通信