1.ListView自定义适配器adapter
注:Android适配器是数据和视图之间的桥梁,以便于数据在View上显示。适配器就像显示器,把复杂的东西按人可以接受的方式来展现。
(1)首先将适配器的View视图表现出来,使用ListView为例:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><ListView
android:id="@+id/listview"android:layout_width="match_parent"android:layout_height="match_parent"></ListView></LinearLayout>
(2)把调用适配器的代码写好,之后再自定义适配器。
public class MainActivity extends AppCompatActivity {private ListView listView;private MyAdapter adapter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.adapter);listView = (ListView) findViewById(R.id.listview);adapter = new MyAdapter(getData());listView.setAdapter(adapter);}
}
(3)现在开始自定义适配器,名字命名为MyAdapter,继承BaseAdapter,并重载这个抽象类的方法,实现其功能:
public class MyAdapter extends BaseAdapter{private List<String> list;//构造函数使其属性本地化,方便加载数据public MyAdapter(List<String> list){this.list = list;}//获取数据的长度@Overridepublic int getCount() {return list.size();}//获取某一位置的数据@Overridepublic Object getItem(int position) {return list.get(position);}//用户获取数据的位置@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {TextView textView = new TextView(MainActivity.this);textView.setText(list.get(position));return textView;}}
(4)因为自定义适配器中用到了List集合,所以新建立一个getData方法,用于获取和存储数据。然后就可以通过适配器按照我们自定义的形式展示出来。
public List<String> getData(){List<String> list = new ArrayList<String>();for (int i = 0; i<10;i++){list.add("xyy"+i);}return list;}
2.使用BaseAdapter优化ListView
注:(1)其中BaseAdapter需要重写的方法:getCount(),getItem(int position),getItemId(int position),getView(int position, View convertView, ViewGroup parent)listView在开始绘制的时候,系统首先调用getCount()函数,根据他的返回值得到 listView的长度,然后根据这个长度,调用getView()逐一绘制每一行。
当手动完成适配时,必须手动映射数据,这需要重写getView()方 法。系统在绘制列表的每一行的时候将调用此方法。getView()有三个参数,position表示将显示的是第几行,covertView是从布局文 件中inflate来的布局。我们用LayoutInflater的方法将定义好的item.xml文件提取成View实例用来显示。然后将xml文件中 的各个组件实例化(简单的findViewById()方法)。这样便可以将数据对应到各个组件上了。
现在让我们回过头从新审视这个过程。系统要绘制ListView了,他首先获得要 绘制的这个列表的长度,然后开始绘制第一行,怎么绘制呢?调用getView()函数。在这个函数里面首先获得一个View(实际上是一个 ViewGroup),然后再实例并设置各个组件,显示之。好了,绘制完这一行了。那再绘制下一行,直到绘完为止。
(2)在实际开发中LayoutInflater这个类的作用类似findViewById()。不同点是LayoutInflater是用来找res/layout/下的xml布局文件,并且实例化;而findViewById()是找xml布局文件下的具体widget控件(如Button、TextView等)。对于一个没有被载入或者想要动态载入的界面,都需要使用LayoutInflater.inflate()来载入。
代码实例:
publicclass MyListViewBase extends Activity {private ListView lv;//定义一个动态数组 ArrayList<HashMap<String, Object>>listItem;/** Called when the activity is first created. */@Overridepublicvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);lv = (ListView) findViewById(R.id.lv);MyAdapter mAdapter = new MyAdapter(this);//得到一个MyAdapter对象lv.setAdapter(mAdapter);//为ListView绑定Adapter/**为ListView添加点击事件*/lv.setOnItemClickListener(new OnItemClickListener() {@Overridepublicvoid onItemClick(AdapterView<?> arg0, View arg1, int arg2,long arg3) {Log.v("MyListViewBase", "你点击了ListView条目" + arg2);//在LogCat中输出信息 }});}/**添加一个得到数据的方法,方便使用*/private ArrayList<HashMap<String, Object>> getDate(){ArrayList<HashMap<String, Object>> listItem = new ArrayList<HashMap<String,Object>>();/**为动态数组添加数据*/ for(int i=0;i<30;i++) { HashMap<String, Object> map = new HashMap<String, Object>(); map.put("ItemTitle", "第"+i+"行"); map.put("ItemText", "这是第"+i+"行"); listItem.add(map); }return listItem;}/** 新建一个类继承BaseAdapter,实现视图与数据的绑定*/privateclass MyAdapter extends BaseAdapter {private LayoutInflater mInflater;//得到一个LayoutInfalter对象用来导入布局 /**构造函数*/public MyAdapter(Context context) {this.mInflater = LayoutInflater.from(context);}@Overridepublicint getCount() {return getDate().size();//返回数组的长度 }@Overridepublic Object getItem(int position) {return null;}@Overridepubliclong getItemId(int position) {return 0;}/**书中详细解释该方法*/ @Overridepublic View getView(finalint position, View convertView, ViewGroup parent) {ViewHolder holder;//观察convertView随ListView滚动情况 Log.v("MyListViewBase", "getView " + position + " " + convertView);if (convertView == null) {convertView = mInflater.inflate(R.layout.item,null);holder = new ViewHolder();/**得到各个控件的对象*/ holder.title = (TextView) convertView.findViewById(R.id.ItemTitle);holder.text = (TextView) convertView.findViewById(R.id.ItemText);holder.bt = (Button) convertView.findViewById(R.id.ItemButton);convertView.setTag(holder);//绑定ViewHolder对象 }else{holder = (ViewHolder)convertView.getTag();//取出ViewHolder对象 }/**设置TextView显示的内容,即我们存放在动态数组中的数据*/ holder.title.setText(getDate().get(position).get("ItemTitle").toString());holder.text.setText(getDate().get(position).get("ItemText").toString());/**为Button添加点击事件*/ holder.bt.setOnClickListener(new OnClickListener() {@Overridepublicvoid onClick(View v) {Log.v("MyListViewBase", "你点击了按钮" + position);//打印Button的点击信息 }});return convertView;}}/**存放控件*/publicfinal class ViewHolder{public TextView title;public TextView text;public Button bt;}
}
3.ArrayList和ListView:
列表的显示需要三个元素:
1.ListVeiw 用来展示列表的View。
2.适配器 用来把数据映射到ListView上的中介。
3.数据 具体的将被映射的字符串,图片,或者基本组件。
其中以ArrayAdapter最为简单,只能展示一行字。SimpleAdapter有最好的扩充性,可以自定义出各种效果。SimpleCursorAdapter可以认为是SimpleAdapter对数据库的简单结合,可以方便的把数据库的内容以列表的形式展示出来。
public class MyListView extends Activity {private ListView listView;//private List<String> data = new ArrayList<String>();@Overridepublic void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);listView = new ListView(this);listView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_expandable_list_item_1,getData()));setContentView(listView);}private List<String> getData(){List<String> data = new ArrayList<String>();data.add("测试数据1");data.add("测试数据2");data.add("测试数据3");data.add("测试数据4");return data;}
}
注:上面代码使用了ArrayAdapter(Context context, int textViewResourceId, List objects)来装配数据,要装配这些数据就需要一个连接ListView视图对象和数组数据的适配器来两者的适配工作,ArrayAdapter的构造需要三个参数,依次为this,布局文件(注意这里的布局文件描述的是列表的每一行的布局android.R.layout.simple_list_item_1是系统定义好的布局文件只显示一行文字,数据源(一个List集合)。同时用setAdapter()完成适配的最后工作。
运行后的现实结构如下图:
4.ListView之BaseAdapter的基本使用及两种优化模式:
BaseAdapter与其他Adapter有些不一样,其他的Adapter可以直接在其构造方法中进行数据的设置,比如:
SimpleAdapter adapter = new SimpleAdapter(this, getData(), R.layout.list_item, new String[]{"img","title","info"},new int[]{R.id.img, R.id.title, R.id.info});
但是在BaseAdapter中需要实现一个继承自BaseAdapter的类,并且重写里面的很多方法,例如:
class MyAdapter extends BaseAdapter{private Context context;public MyAdapter(Context context){this.context = context;}@Overridepublic int getCount() {// (在此适配器中所代表的数据集中的条目数)return length;}@Overridepublic Object getItem(int position) {// (获取数据集中与指定索引对应的数据项)return list[position];}@Overridepublic long getItemId(int position) {// (取在列表中与指定索引对应的行id)return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {// Get a View that displays the data at the specified position in the data set.return null;}}
getView处理方式:
第一种方法:没有任何处理,不建议这样写。如果数据量少看将就,但是如果列表项数据量很大的时候,会每次都重新创建View,设置资源,严重影响性能,所以从一开始就不要用这种方式。
public View getView(int position, View convertView, ViewGroup parent) {View item = mInflater.inflate(R.layout.list_item, null);ImageView img = (ImageView)item.findViewById(R.id.img)TextView title = (TextView)item.findViewById(R.id.title);TextView info = (TextView)item.findViewById(R.id.info);img.setImageResource(R.drawable.ic_launcher);title.setText("Hello");info.setText("world");return item;}
第二种方法:通过缓存convertView,这种利用缓存contentView的方式可以判断如果缓存中不存在View才创建View,如果已经存在可以利用缓存中的View,提升了性能。
public View getView(int position, View convertView, ViewGroup parent) {if(convertView == null){convertView = mInflater.inflate(R.layout.list_item, null);}ImageView img = (ImageView)convertView.findViewById(R.id.img)TextView title = (TextView)convertView.findViewById(R.id.title);TextView info = (TextView)ConvertView.findViewById(R.id.info);img.setImageResource(R.drawable.ic_launcher);title.setText("Hello");info.setText("world");return convertView;}
第三种方法:通过convertView+ViewHolder来实现,ViewHolder就是一个静态类,使用 ViewHolder 的关键好处是缓存了显示数据的视图(View),加快了 UI 的响应速度。
注:当我们判断 convertView == null 的时候,如果为空,就会根据设计好的List的Item布局(XML),来为convertView赋值,并生成一个viewHolder来绑定converView里面的各个View控件(XML布局里面的那些控件)。再用convertView的setTag将viewHolder设置到Tag中,以便系统第二次绘制ListView时从Tag中取出。(看下面代码中)
如果convertView不为空的时候,就会直接用convertView的getTag(),来获得一个ViewHolder。
//在外面先定义,ViewHolder静态类
static class ViewHolder
{public ImageView img;public TextView title;public TextView info;
}
//然后重写getView
@Override
public View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder;if(convertView == null){holder = new ViewHolder();convertView = mInflater.inflate(R.layout.list_item, null);holder.img = (ImageView)item.findViewById(R.id.img)holder.title = (TextView)item.findViewById(R.id.title);holder.info = (TextView)item.findViewById(R.id.info);convertView.setTag(holder);}else{holder = (ViewHolder)convertView.getTag();}holder.img.setImageResource(R.drawable.ic_launcher);holder.title.setText("Hello");holder.info.setText("World");}return convertView;
}
ViewHolder类的作用:
ViewHolder模式通过getView()方法返回的视图的标签(Tag)中存储一个数据结构,这个数据结构包含了指向我们要绑定数据的视图的引用,从而避免每次调用getView()的时候调用findViewById()。
实例:用BaseAdapter来自定义ListView布局:
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="fill_parent"android:layout_height="fill_parent"android:orientation="vertical" ><ListView
android:id="@+id/lv"android:layout_width="fill_parent"android:layout_height="wrap_content"android:fastScrollEnabled="true"/>
</LinearLayout>
list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="horizontal" ><ImageView
android:id="@+id/img"android:layout_width="wrap_content"android:layout_height="wrap_content"/><LinearLayout
android:layout_width="fill_parent"android:layout_height="wrap_content"android:orientation="vertical"><TextView
android:id="@+id/tv"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="20sp"/><TextView
android:id="@+id/info"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="14sp"/></LinearLayout></LinearLayout>
Activity
package com.loulijun.demo17;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
public class Demo17Activity extends Activity {private ListView lv;private List<Map<String, Object>> data;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);lv = (ListView)findViewById(R.id.lv);//获取将要绑定的数据设置到data中data = getData();MyAdapter adapter = new MyAdapter(this);lv.setAdapter(adapter);}private List<Map<String, Object>> getData(){List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();Map<String, Object> map;for(int i=0;i<10;i++){map = new HashMap<String, Object>();map.put("img", R.drawable.ic_launcher);map.put("title", "跆拳道");map.put("info", "快乐源于生活...");list.add(map);}return list;}//ViewHolder静态类static class ViewHolder{public ImageView img;public TextView title;public TextView info;}public class MyAdapter extends BaseAdapter{ private LayoutInflater mInflater = null;private MyAdapter(Context context){//根据context上下文加载布局,这里的是Demo17Activity本身,即thisthis.mInflater = LayoutInflater.from(context);}@Overridepublic int getCount() {//How many items are in the data set represented by this Adapter.//在此适配器中所代表的数据集中的条目数return data.size();}@Overridepublic Object getItem(int position) {// Get the data item associated with the specified position in the data set.//获取数据集中与指定索引对应的数据项return position;}@Overridepublic long getItemId(int position) {//Get the row id associated with the specified position in the list.//获取在列表中与指定索引对应的行idreturn position;}//Get a View that displays the data at the specified position in the data set.//获取一个在数据集中指定索引的视图来显示数据@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder = null;//如果缓存convertView为空,则需要创建Viewif(convertView == null){holder = new ViewHolder();//根据自定义的Item布局加载布局convertView = mInflater.inflate(R.layout.list_item, null);holder.img = (ImageView)convertView.findViewById(R.id.img);holder.title = (TextView)convertView.findViewById(R.id.tv);holder.info = (TextView)convertView.findViewById(R.id.info);//将设置好的布局保存到缓存中,并将其设置在Tag里,以便后面方便取出TagconvertView.setTag(holder);}else{holder = (ViewHolder)convertView.getTag();}holder.img.setBackgroundResource((Integer)data.get(position).get("img"));holder.title.setText((String)data.get(position).get("title"));holder.info.setText((String)data.get(position).get("info"));return convertView;}}
}
最后的运行图: