连接AdapterView视图和数据源的桥梁:Adapter适配器(2)
BaseAdapter是一种原生态的适配器,它是一个抽象类,一般使用它来实现自定义的适配器,当需要适配大量的数据时,为了节省手机的内存,往往会定义一个类继承它,从而优化内存的使用,使用在ListView、Spinner的内存效率优化。
查询Android官方文档可知BaseAdapter的类定义
BaseAdapter是直接继承Java中顶级类Object类,同时它实现了ListAdapter和SpinnerAdapter接口。BaseAdapter的常用直接子类有ArrayAdapter<T>、CursorAdapter(抽象类)和SimpleAdapter,常用间接子类有ResourceCursorAdapter(抽象类)和SimpleCursorAdapter。
一般定义一个类,它继承了BaseAdapter,同时会重写BaseAdapter的四个重要方法:
1.public int getCount() 返回BaseAdapter适配的数据项个数
2.public Object getItem(int position) 返回BaseAdapter适配的第postion个数据项的数据
3.public long getItemId(int position) 返回BaseAdapter开始适配数据项的序号
4.public View getView(int position, View convertView, ViewGroup parent) 返回BaseAdapter创建的某一个数据项
BaseAdapter的使用一般在于它的优化,即重写getView()方法采用的不用方式,一般有三种写法:
1.逗比式:未进行任何的优化,按一般思路来重写,当数据量不大,没有什么大问题,一旦数据量比较大时,严重浪费内存同时效率特别低。
源码:
1 @Override 2 public View getView(int position, View convertView, ViewGroup parent) { 3 View view = null; 4 5 // 将布局文件转化为View对象,通过View对象获取需要设置数据的UI组件对象(两种方法) 6 // view =LayoutInflater.from(context).inflate(R.layout.item_layout,null); 7 view = LayoutInflater.from(context).inflate(R.layout.item_layout, 8 parent, false); 9 ImageView image = (ImageView) view.findViewById(R.id.image); 10 TextView title = (TextView) view.findViewById(R.id.title); 11 TextView content = (TextView) view.findViewById(R.id.content); 12 13 // 获取该position数据项要显示的数据 14 Map<String, Object> map = (Map<String, Object>) getItem(position); 15 16 // 设置数据 17 image.setImageResource((Integer) map.get("image")); 18 title.setText(map.get("title").toString()); 19 content.setText(map.get("content").toString()); 20 21 // 返回第position项的数据项视图 22 return view; 23 }
2.一般式:在1的基础上进行优化,主要优化点在于,利用了方法中的convertView参数,这个参数是系统缓存的View视图,每次调用getView()方法创建View对象时,先判断是否convertView对象是否为空,当不为空时,不在创建View对象,之间将View指向convertView对象的地址,当为空时,采取和1一样的操作,即将布局文件转化为View对象。
1 //方式2:利用缓存的convertView对象,不用每次都重新创建View对象,减少了内存空间的分配 2 @Override 3 public View getView(int position, View convertView, ViewGroup parent) { 4 View view = null; 5 6 if(convertView!=null) 7 view = convertView; 8 else{ 9 // 将布局文件转化为View对象,通过View对象获取需要设置数据的UI组件对象(两种方法) 10 // view =LayoutInflater.from(context).inflate(R.layout.item_layout,null); 11 view = LayoutInflater.from(context).inflate(R.layout.item_layout, 12 parent, false); 13 } 14 15 ImageView image = (ImageView) view.findViewById(R.id.image); 16 TextView title = (TextView) view.findViewById(R.id.title); 17 TextView content = (TextView) view.findViewById(R.id.content); 18 19 // 获取该position数据项要显示的数据 20 Map<String, Object> map = (Map<String, Object>) getItem(position); 21 22 // 设置数据 23 image.setImageResource((Integer) map.get("image")); 24 title.setText(map.get("title").toString()); 25 content.setText(map.get("content").toString()); 26 27 // 返回第position项的数据项视图 28 return view; 29 }
3.文艺式:在2的基础上进行优化,首先创建一个内部类,然后利用new View.setTag(Object object)和new View.getTag()方法,减少findViewById(int)的次数,当数据巨大时,可以发挥显著的效果。
1 //方式三:在方式2的基础上,利用一个内部类,同时调用view.setTag(Object object)和view.getTag()方法 2 //减少了findViewById(int)的次数 3 @Override 4 public View getView(int position, View convertView, ViewGroup parent) { 5 View view = null; 6 7 if(convertView!=null) 8 view = convertView; 9 else{ 10 // 将布局文件转化为View对象,通过View对象获取需要设置数据的UI组件对象(两种方法) 11 // view =LayoutInflater.from(context).inflate(R.layout.item_layout,null); 12 view = LayoutInflater.from(context).inflate(R.layout.item_layout, 13 parent, false); 14 ViewHolder holder = new ViewHolder(); 15 holder.image = (ImageView) view.findViewById(R.id.image); 16 holder.title = (TextView) view.findViewById(R.id.title); 17 holder.content = (TextView) view.findViewById(R.id.content); 18 view.setTag(holder); 19 } 20 21 ViewHolder viewHolder = (ViewHolder) view.getTag(); 22 23 // 获取该position数据项要显示的数据 24 Map<String, Object> map = (Map<String, Object>) getItem(position); 25 26 // 设置数据 27 viewHolder.image.setImageResource((Integer) map.get("image")); 28 viewHolder.title.setText(map.get("title").toString()); 29 viewHolder.content.setText(map.get("content").toString()); 30 31 // 返回第position项的数据项视图 32 return view; 33 } 34 class ViewHolder{ 35 ImageView image; 36 TextView title; 37 TextView content; 38 }
注意:
1.内部类ViewHolder是一个自定义类,它的类名是自定义的,我们可以取不同的名字,而不一定非要为ViewHolder。
2.参数列表中的参数View convertView,它缓存了使用过的视图,例如当AdapterView是ListView时,上滑动ListView,顶部的数据项View视图会不见,这时就缓存为convertView,
上滑动多次,这时可能有多个convertView对象
3.new View().setTag(Object object)和new View.getTag()方法,可以看作是当封装实体类时的setXXX()和getXXX()方法类似,便于理解和记忆。
扩展:
可以在一个方法中创建BaseAdapter的匿名内部类,也可以将自定义继承了BaseAdapter的类作为某个类的内部类,也可以如上单独定义一个外部类继承BaseAdapter,它们根据情景的不同可以灵活的使用。例如将自定义继承了BaseAdapter的类作为某个类的内部类,如果这个外部类中存在数据源,那么就可以直接访问外部类的数据源,就不需要像上面的例子样传递上下文对象和数据源了,有时候方便多了。
示例:
需求:MainActivity界面利用ListView显示10条数据,自定义适配器BaseAdapter,采用上述三种重写getView方法。
MainActivity源码:
1 package com.my.day21_my_listview3; 2 3 import java.util.ArrayList; 4 import java.util.HashMap; 5 import java.util.List; 6 import java.util.Map; 7 8 import com.my.day21_my_listview3.adapter.MyAdapter; 9 10 import android.os.Bundle; 11 import android.widget.ListView; 12 import android.app.Activity; 13 14 public class MainActivity extends Activity { 15 private ListView listView; 16 private MyAdapter adapter; 17 private List<Map<String, Object>> data; 18 19 @Override 20 protected void onCreate(Bundle savedInstanceState) { 21 super.onCreate(savedInstanceState); 22 setContentView(R.layout.activity_main); 23 24 //获取ListView组件对象 25 listView = (ListView) findViewById(R.id.listView); 26 27 //初始化数据源 28 data = new ArrayList<Map<String,Object>>(); 29 for(int i=1;i<=10;i++){ 30 Map<String, Object> map = new HashMap<String, Object>(); 31 map.put("image", R.drawable.ic_launcher); 32 map.put("title", "这是第"+i+"个数据项的标题"); 33 map.put("content", "这是第"+i+"个数据项的内容"); 34 data.add(map); 35 } 36 37 //创建适配器对象 38 adapter = new MyAdapter(this,data); 39 40 //绑定适配器 41 listView.setAdapter(adapter); 42 } 43 }
MyAdapter源码:
1 package com.my.day21_my_listview3.adapter; 2 3 import java.util.List; 4 import java.util.Map; 5 6 import com.my.day21_my_listview3.R; 7 8 import android.content.Context; 9 import android.view.LayoutInflater; 10 import android.view.View; 11 import android.view.ViewGroup; 12 import android.widget.BaseAdapter; 13 import android.widget.ImageView; 14 import android.widget.TextView; 15 16 public class MyAdapter extends BaseAdapter { 17 private Context context; 18 private List<Map<String, Object>> data; 19 20 public MyAdapter(Context context,List<Map<String, Object>> data){ 21 this.context = context; 22 this.data = data; 23 } 24 25 @Override 26 public int getCount() { 27 int count = 0; 28 if (data != null) 29 count = data.size(); 30 return count; 31 } 32 33 @Override 34 public Map<String, Object> getItem(int position) { 35 return data.get(position); 36 } 37 38 @Override 39 public long getItemId(int position) { 40 return position; 41 } 42 43 /*@Override 44 public View getView(int position, View convertView, ViewGroup parent) { 45 View view = null; 46 47 // 将布局文件转化为View对象,通过View对象获取需要设置数据的UI组件对象(两种方法) 48 // view =LayoutInflater.from(context).inflate(R.layout.item_layout,null); 49 view = LayoutInflater.from(context).inflate(R.layout.item_layout, 50 parent, false); 51 ImageView image = (ImageView) view.findViewById(R.id.image); 52 TextView title = (TextView) view.findViewById(R.id.title); 53 TextView content = (TextView) view.findViewById(R.id.content); 54 55 // 获取该position数据项要显示的数据 56 Map<String, Object> map = (Map<String, Object>) getItem(position); 57 58 // 设置数据 59 image.setImageResource((Integer) map.get("image")); 60 title.setText(map.get("title").toString()); 61 content.setText(map.get("content").toString()); 62 63 // 返回第position项的数据项视图 64 return view; 65 }*/ 66 67 /*//方式2:利用缓存的convertView对象,不用每次都重新创建View对象,减少了内存空间的分配 68 @Override 69 public View getView(int position, View convertView, ViewGroup parent) { 70 View view = null; 71 72 if(convertView!=null) 73 view = convertView; 74 else{ 75 // 将布局文件转化为View对象,通过View对象获取需要设置数据的UI组件对象(两种方法) 76 // view =LayoutInflater.from(context).inflate(R.layout.item_layout,null); 77 view = LayoutInflater.from(context).inflate(R.layout.item_layout, 78 parent, false); 79 } 80 81 ImageView image = (ImageView) view.findViewById(R.id.image); 82 TextView title = (TextView) view.findViewById(R.id.title); 83 TextView content = (TextView) view.findViewById(R.id.content); 84 85 // 获取该position数据项要显示的数据 86 Map<String, Object> map = (Map<String, Object>) getItem(position); 87 88 // 设置数据 89 image.setImageResource((Integer) map.get("image")); 90 title.setText(map.get("title").toString()); 91 content.setText(map.get("content").toString()); 92 93 // 返回第position项的数据项视图 94 return view; 95 }*/ 96 97 //方式三:在方式2的基础上,利用一个内部类,同时调用view.setTag(Object object)和view.getTag()方法 98 //减少了findViewById(int)的次数 99 @Override 100 public View getView(int position, View convertView, ViewGroup parent) { 101 View view = null; 102 103 if(convertView!=null) 104 view = convertView; 105 else{ 106 // 将布局文件转化为View对象,通过View对象获取需要设置数据的UI组件对象(两种方法) 107 // view =LayoutInflater.from(context).inflate(R.layout.item_layout,null); 108 view = LayoutInflater.from(context).inflate(R.layout.item_layout, 109 parent, false); 110 ViewHolder holder = new ViewHolder(); 111 holder.image = (ImageView) view.findViewById(R.id.image); 112 holder.title = (TextView) view.findViewById(R.id.title); 113 holder.content = (TextView) view.findViewById(R.id.content); 114 view.setTag(holder); 115 } 116 117 ViewHolder viewHolder = (ViewHolder) view.getTag(); 118 119 // 获取该position数据项要显示的数据 120 Map<String, Object> map = (Map<String, Object>) getItem(position); 121 122 // 设置数据 123 viewHolder.image.setImageResource((Integer) map.get("image")); 124 viewHolder.title.setText(map.get("title").toString()); 125 viewHolder.content.setText(map.get("content").toString()); 126 127 // 返回第position项的数据项视图 128 return view; 129 } 130 class ViewHolder{ 131 ImageView image; 132 TextView title; 133 TextView content; 134 } 135 }
activity_main文件:
1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:paddingBottom="@dimen/activity_vertical_margin" 6 android:paddingLeft="@dimen/activity_horizontal_margin" 7 android:paddingRight="@dimen/activity_horizontal_margin" 8 android:paddingTop="@dimen/activity_vertical_margin" 9 tools:context=".MainActivity" > 10 11 <ListView 12 android:id="@+id/listView" 13 android:layout_width="match_parent" 14 android:layout_height="match_parent" > 15 </ListView> 16 17 </RelativeLayout>
列表项布局item_layout文件:
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="horizontal" 6 android:padding="10dp" > 7 8 <ImageView 9 android:id="@+id/image" 10 android:layout_width="80dp" 11 android:layout_height="80dp" 12 android:scaleType="fitXY" /> 13 14 <LinearLayout 15 android:layout_width="match_parent" 16 android:layout_height="80dp" 17 android:orientation="vertical" > 18 19 <TextView 20 android:id="@+id/title" 21 android:layout_width="match_parent" 22 android:layout_height="wrap_content" 23 android:gravity="center" 24 android:textSize="20sp" 25 android:textStyle="bold" /> 26 27 <TextView 28 android:id="@+id/content" 29 android:layout_width="match_parent" 30 android:layout_height="0dp" 31 android:layout_marginLeft="20dp" 32 android:layout_weight="1" 33 android:gravity="center_horizontal" > 34 </TextView> 35 </LinearLayout> 36 37 </LinearLayout>
效果图:
未完,待续。