【Android适配器系列】BaseAdapter学习笔记
2015-08-04 23:32
435 查看
慕客网-Android必学-BaseAdapter的使用与优化-学习笔记
优点:
将数据的来源与数据的显示进行了解耦,降低程序的耦合性,提高可扩展性。
BaseAdapter是Android各种各样适配器里最通用的适配器。
public Object getItem(int position) : 获取数据集中与指定索引对应的数据项
public long getItemId(int position) : 获取指定行对应的ID
public View getView(int position, View convertView, ViewGroup parent) :获取每一个Item的显示内容
在使用BaseAdapter时,我们需要自己创建一个类继承BaseAdapter,然后Eclipse会提醒我们实现上述四个方法,当给ListView设置了我们自己写的Adapter后,ListView内部会调用上述四个方法。
新建Android工程 MyBaseAdapter,准备界面布局
修改activity_main,将TextView改为ListView,如下:
创建单条数据项的Layout布局:item.xml
创建Bean对象,里面有三个属性与上面的Item相对应,里面封装的是单条的数据
创建MyAdapter继承BaseAdapter,实现我们需要实现的四个方法
创建测试数据,用适配器连接ListView和测试数据
总结
之所以称此种模式是逗比式,是因为在MyAdapter里,每次都是创建一个新的View, 并在View中找到相应的控件去设置相应的值,完全没用到ListView的缓存机制,造成了资源的极大浪费。
下面是普通式的getView方法代码:
普通式与逗比式的差别在于普通式使用的View是系统缓存的convertView,通过判断可以避免创建大量的View,从而大量的节省了资源。
但findViewById仍然会浪费大量的时间,这也是为什么这个方法只能称为普通式的原因。
- 创建大量的View
- findViewById查找控件
我们通过普通式解决了第一个耗时操作,下面就开始着手解决第二个耗时操作。
这个方法由2013年Google开发者大会上的某个开发者提出,具体过程分为:
1. 创建ViewHolder内部类,里面包含ListView单行界面布局的所有控件变量;
2. 在getView中先准备变量viewHolder,然后判断convertView是否为空,为空则转第3步,否则转第4步;
3. 使用Inflater创建convertView,实例化viewHolder,使用findViewById实例化viewHolder里的三个控件,接下来,就是最重要的,使用setTag方法将convertView与viewHolder关联起来(具体看代码),因为convertView会被系统缓存,所以同时ViewHolder也被缓存;
4. 使用getTag方法取出viewHolder,然后用数据填充viewHolder里的控件,而不用再先使用findViewById查找控件后赋值,从而节省了大量的时间。
下面是MyAdapter类getView方法文艺式实现的代码:
通过这种做法,就避免了大量的findViewById,从而大大地节省了时间。
什么是数据适配器
数据适配器是数据源与视图(View)之间的桥梁,建立了两者之间的适配关系。数据的来源是各种各样的,但View能显示的格式却是有一定要求的,数据适配器是把各种各样的数据源转化成为View能显示的数据格式。优点:
将数据的来源与数据的显示进行了解耦,降低程序的耦合性,提高可扩展性。
BaseAdapter是Android各种各样适配器里最通用的适配器。
BaseAdapter基本结构
public int getCount() :适配器中 数据集中 数据的个数,即ListView需要显示的数据个数public Object getItem(int position) : 获取数据集中与指定索引对应的数据项
public long getItemId(int position) : 获取指定行对应的ID
public View getView(int position, View convertView, ViewGroup parent) :获取每一个Item的显示内容
在使用BaseAdapter时,我们需要自己创建一个类继承BaseAdapter,然后Eclipse会提醒我们实现上述四个方法,当给ListView设置了我们自己写的Adapter后,ListView内部会调用上述四个方法。
BaseAdapter的逗比式
这是BaseAdapter使用的基本形式,下面是实现步骤:新建Android工程 MyBaseAdapter,准备界面布局
修改activity_main,将TextView改为ListView,如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.mybaseadapter.MainActivity" > <ListView android:id="@+id/lv_main" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_world" /> </RelativeLayout>
创建单条数据项的Layout布局:item.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > <ImageView android:id="@+id/iv_image" android:layout_width="64dp" android:layout_height="64dp" android:src="@drawable/ic_launcher" /> <TextView android:id="@+id/tv_title" android:layout_width="match_parent" android:layout_height="30dp" android:layout_toRightOf="@id/iv_image" android:text="Title" android:gravity="center" android:textSize="25sp" /> <TextView android:id="@+id/tv_content" android:layout_width="match_parent" android:layout_height="30dp" android:layout_toRightOf="@id/iv_image" android:layout_below="@id/tv_title" android:text="Content" android:gravity="center_vertical" android:textSize="20sp" /> </RelativeLayout>
创建Bean对象,里面有三个属性与上面的Item相对应,里面封装的是单条的数据
package com.mybaseadapter; public class ItemBean { public int itemImageResid; public String itemTitle; public String itemContent; public ItemBean(int itemImageResid, String itemTitle, String itemContent) { super(); this.itemImageResid = itemImageResid; this.itemTitle = itemTitle; this.itemContent = itemContent; } }
创建MyAdapter继承BaseAdapter,实现我们需要实现的四个方法
package com.mybaseadapter; import java.util.List; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; public class MyAdapter extends BaseAdapter{ private List<ItemBean> mList; private LayoutInflater mInflater; /** * 关联数据源与数据适配器 * @param list */ public MyAdapter(Context context, List<ItemBean> list){ mList = list; mInflater = LayoutInflater.from(context); } @Override public int getCount() { // TODO Auto-generated method stub return mList.size(); } @Override public Object getItem(int position) { // TODO Auto-generated method stub return mList.get(position); } @Override public long getItemId(int position) { // TODO Auto-generated method stub return position;//对应的索引项 } @Override public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub // 返回每一项的显示内容 /********* 逗比式 **************/ /** * 无视convertView和parent,没有考虑ListView的感受,没有使用缓存机制 * 每次都是创建一个新的View, 并在View中找到相应的控件去设置相应的值, * 完全没用到缓存机制,对资源造成了极大浪费 */ //创建View View view = this.mInflater.inflate(R.layout.item, null); ImageView image = (ImageView) view.findViewById(R.id.iv_image); TextView title = (TextView) view.findViewById(R.id.tv_title); TextView content = (TextView) view.findViewById(R.id.tv_title); //关联数据 image.setImageResource(mList.get(position).itemImageResid); title.setText(mList.get(position).itemTitle); content.setText(mList.get(position).itemContent); return view; } }
创建测试数据,用适配器连接ListView和测试数据
package com.mybaseadapter; import java.util.ArrayList; import java.util.List; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; import android.widget.ListView; public class MainActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //创建测试数据 List<ItemBean> itemBeanList = new ArrayList<ItemBean>(); for(int i = 0; i < 20; i++ ){ itemBeanList.add(new ItemBean( R.drawable.ic_launcher, "标题 " + i, "内容 " + i )); } ListView lv = (ListView) findViewById(R.id.lv_main); MyAdapter adapter = new MyAdapter(this, itemBeanList); lv.setAdapter(adapter); } }
总结
之所以称此种模式是逗比式,是因为在MyAdapter里,每次都是创建一个新的View, 并在View中找到相应的控件去设置相应的值,完全没用到ListView的缓存机制,造成了资源的极大浪费。
BaseAdapter的普通式
普通式是逗比式的升级版,升级的地方就是在MyAdapter的getView里,逗比式里没有使用到ListView的缓存机制,在普通式里这个缺点会被弥补。下面是普通式的getView方法代码:
public View getView(int position, View convertView, ViewGroup parent) { /**********普通方法***************/ if (convertView == null){ //判断convertView是否已被缓存,通过这个判断就避免创建大量的View,从而大大地节省了资源 convertView = this.mInflater.inflate(R.layout.item, null); } ImageView image = (ImageView) convertView.findViewById(R.id.iv_image); TextView title = (TextView) convertView.findViewById(R.id.tv_title); TextView content = (TextView) convertView.findViewById(R.id.tv_title); //关联数据 image.setImageResource(mList.get(position).itemImageResid); title.setText(mList.get(position).itemTitle); content.setText(mList.get(position).itemContent); return convertView; /********************************/ }
普通式与逗比式的差别在于普通式使用的View是系统缓存的convertView,通过判断可以避免创建大量的View,从而大量的节省了资源。
但findViewById仍然会浪费大量的时间,这也是为什么这个方法只能称为普通式的原因。
BaseAdapter的文艺式
在getView里面的两个耗时:- 创建大量的View
- findViewById查找控件
我们通过普通式解决了第一个耗时操作,下面就开始着手解决第二个耗时操作。
这个方法由2013年Google开发者大会上的某个开发者提出,具体过程分为:
1. 创建ViewHolder内部类,里面包含ListView单行界面布局的所有控件变量;
2. 在getView中先准备变量viewHolder,然后判断convertView是否为空,为空则转第3步,否则转第4步;
3. 使用Inflater创建convertView,实例化viewHolder,使用findViewById实例化viewHolder里的三个控件,接下来,就是最重要的,使用setTag方法将convertView与viewHolder关联起来(具体看代码),因为convertView会被系统缓存,所以同时ViewHolder也被缓存;
4. 使用getTag方法取出viewHolder,然后用数据填充viewHolder里的控件,而不用再先使用findViewById查找控件后赋值,从而节省了大量的时间。
下面是MyAdapter类getView方法文艺式实现的代码:
public View getView(int position, View convertView, ViewGroup parent) { /*************文艺式***************/ ViewHolder viewHolder = null; if (convertView == null){ convertView = this.mInflater.inflate(R.layout.item, null); viewHolder = new ViewHolder(); viewHolder.image = (ImageView) convertView.findViewById(R.id.iv_image); viewHolder.title = (TextView) convertView.findViewById(R.id.tv_title); viewHolder.content = (TextView) convertView.findViewById(R.id.tv_content); //将ViewHolder与convertView进行关联 convertView.setTag(viewHolder); }else{ viewHolder = (ViewHolder) convertView.getTag(); } viewHolder.image.setImageResource(mList.get(position).itemImageResid); viewHolder.title.setText(mList.get(position).itemTitle); viewHolder.content.setText(mList.get(position).itemContent); return convertView; /*********************************/ } class ViewHolder{ public ImageView image; public TextView title; public TextView content; }
通过这种做法,就避免了大量的findViewById,从而大大地节省了时间。
总结
逗比式、普通式、文艺式是循序渐进逐步优化的,我在其中可以学到了我们应该对控件的内部机制何相关设计模式有一定了解,这样才能理解那些API为什么要这样设计,这样才能做到灵活运用。另外,有时候也可尝试一下另辟蹊径,文艺式里使用setTag来缓存ViewHolder就属于此。相关文章推荐
- Android ADB调试
- Android 开源项目
- 解决错误:AndroidManifest.xml file missing
- Android Studio升级到最新v1.3版后配置GitHub提示Gradle版本不支持的问题
- Android远程服务四:远程服务service端和client端的线程关系
- Android Framework_Android系统启动过程
- Android 实现点击两个按钮,原地切换界面
- android新版本下获取屏幕宽度和高度的方法
- 在使用Android-PullToRefresh前遇到的问题
- Android Studio 使用遇到的各种问题及解决方案汇总
- android 何时使用Service 何时使用Thread
- android studio "Cannot launch SDK manager ...."
- Android权限详解
- Android 通过UDP广播建立Socket
- Android(Lollipop/5.0) Material Design(二) 入门指南
- Android(Lollipop/5.0) Material Design(二) 入门指南
- Android自定义注册页面提示,替换Toast功能
- AndroidStudio Tips
- android Looper Handler机制
- cause:Permission Denial: receiving Intent act=android.provider.Telephony.SMS_RECEIVED