ListView就是列表组件,一般通过继承ListActivity使用系统提供的ListView.
所有的AdapterView组件都需要有一个对应的Adapter作为适配器来显示列表中元素的布局方式
见思维导图
ArrayAdapter: 数组或集合的适配器。
例:
private final String[] mous = {
"郭嘉",
"荀攸",
"荀彧",
"程昱",
"戏志才",
"徐庶"
};
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,mous);
也可以在XML文件中用android:entries属性指定绑定的数组资源文件
资源文件存放在value文件夹下
如:
android:entries="@array/book"
<string-array name="book" >
<item >1</item>
<item >2</item>
<item >3</item>
<item >3</item>
</string-array>
例子
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="horizontal" > <ListView android:id="@+id/lv" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
package com.light.android.study; import android.app.Activity; import android.os.Bundle; import android.widget.ArrayAdapter; import android.widget.ListView; public class MainActivity extends Activity { private final String[] mous = { "郭嘉", "荀攸", "荀彧", "程昱", "戏志才", "徐庶" }; private ListView lv; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); initAdapter(); } private void init(){ lv = (ListView) findViewById(R.id.lv); } private void initAdapter(){ ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,mous); lv.setAdapter(adapter); } }
效果:
SimpleAdapter:简单的Adapter实现
SimpleAdapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to)
第一个参数是Context对象
第二个参数是保存有每一行数据的Map构成的List对象,也就是说,每一行数据里的每一个属性都由它的名字和它的值构成一个键值对,多个属性有多个键值对,每一行的多个键值对构成这一行的一个Map对象,这一行的Map对象对应到这个List中.
第三个参数布局文件Id
第四个参数Map对象中的多个键值对的key值
第五个参数布局文件中用来显示内容的组件ID
SimpleAdapter绑定数据到视图分两个阶段
1.首先,如果设置了SimpleAdapter.ViewBinder,那么这个设置的ViewBinder的setViewValue(android.view.View, Object, String)将被调用.如果setViewValue的返回值是true,则表示绑定已经完成,将不再调用系统默认的绑定实现.
2.如果返回值为false,视图将按以下顺序绑定数据:
•如果View实现了Checkable(例如CheckBox),期望绑定值是一个布尔类型.
•TextView.期望绑定值是一个字符串类型,通过调用setViewText(TextView, String)绑定.
•ImageView,期望绑定值是一个资源id或者一个字符串,通过调用setViewImage(ImageView, int) 或 setViewImage(ImageView, String)绑定数据.
如果没有一个合适的绑定发生将会抛出IllegalStateException.
例子:
布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <!-- 定义一个List --> <ListView android:id="@+id/mylist" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </LinearLayout>
<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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:orientation="vertical" > <!-- 定义一个ImageView,用于作为列表项的一部分。 --> <ImageView android:id="@+id/header" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingLeft="10dp" tools:ignore="ContentDescription" /> <!-- 定义一个TextView,用于作为列表项的一部分。 --> <TextView android:id="@+id/name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center_vertical" android:paddingLeft="10dp" android:textSize="16sp" /> </LinearLayout>
package com.light.android.study; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import android.app.Activity; import android.os.Bundle; import android.widget.ListView; import android.widget.SimpleAdapter; public class MainActivity extends Activity { private String[] names = new String[] { "杜甫", "弄玉", "清照", "李白" }; private int[] imageIds = new int[] { R.drawable.tiger, R.drawable.nongyu, R.drawable.qingzhao, R.drawable.libai }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); List<Map<String, Object>> listItems = new ArrayList<Map<String, Object>>(); for (int i = 0; i < names.length; i++) { Map<String, Object> map = new HashMap<String, Object>(); map.put("header", imageIds[i]); map.put("personName", names[i]); listItems.add(map); } //創建SimpleAdapter SimpleAdapter simpleAdapter = new SimpleAdapter(this, listItems, R.layout.list_item_layout, new String[] { "personName", "header" }, new int[] { R.id.name, R.id.header }); ListView list = (ListView) findViewById(R.id.mylist); list.setAdapter(simpleAdapter); } }
效果:
CursorAdapter:
Cursor型集合的适配器,与数据库连接的桥梁
其中游标携带的结果集中必须有列名为“_id”的列,否则这个类无法工作.
getView()
每一项显示的设置
bindView()
把数据和已经生成的View绑定在一起
newView()
新建一个View
CursorAdapter中源码 getView的实现
public View getView(int position, View convertView, ViewGroup parent) {
View v;
if (convertView == null) {
v = newView(mContext, mCursor, parent);
} else {
v = convertView;
}
bindView(v, mContext, mCursor);
return v;
}
getView首先使用已经存在的View,如果没有就调用newView 产生一个,然后把数据bind上去
我们一般使用其子类SimpleCursorAdapter来实现查询出来的数据List的Adapter
SimpleCursorAdapter(Context context,int layout,Cursor c,String[] from,int[] to,int flags)
context:当前Context对象
layout:布局文件Id
c:集合
from:需要绑定的集合中的列的名字
to:显示项目的View的id集合,在layout布局文件中定义
flags:用来判断适配器的行为标志
建议使用CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER
适配器会在游标上注册一个内容观测器,当通知到达时会调用 onContentChanged() 方法.使用该标志位时要注意:在注册观察器时需要先解除当前游标与适配器的关联,防止发生泄漏.
Android 3.0引入了CursorLoader实现异步加载数据,为了避免同步查询数据库时阻塞UI线程的问题。所以一般先将SimpleCursorAdapter中的Cursor对象放空,然后用CursorLoader对象加载数据,再放入适配器。
例子:
布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="horizontal" > <ListView android:id="@android:id/list" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
Item布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="64dip" android:orientation="horizontal" android:gravity="center_vertical" android:paddingLeft="8dip"> <TextView android:id="@android:id/text1" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="18sp" android:gravity="center_vertical" android:singleLine="true" android:fadingEdge="horizontal" android:fadingEdgeLength="3dip" android:ellipsize="marquee" /> </LinearLayout>
package com.light.android.study; import android.app.ListActivity; import android.app.LoaderManager.LoaderCallbacks; import android.content.Context; import android.content.CursorLoader; import android.content.Loader; import android.database.Cursor; import android.os.Bundle; import android.provider.ContactsContract; import android.widget.SimpleCursorAdapter; //實現接口作為CursorLoader public class MainActivity extends ListActivity implements LoaderCallbacks<Cursor> { private SimpleCursorAdapter mAdapter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Create an empty adapter we will use to display the loaded data. mAdapter = new SimpleCursorAdapter(MainActivity.this, R.layout.contacts_list_item, null, new String[] { ContactsContract.Contacts.DISPLAY_NAME }, new int[] { android.R.id.text1 }, 0); getListView().setAdapter(mAdapter); //初始化Loader //第一個參數 為這個Loader對象指定唯一的标识ID,第二个可选参数,用来支持Loader的构造方法, //第三个参数是LoaderCallbacks接口类型 getLoaderManager().initLoader(0, null, this); } public Loader<Cursor> onCreateLoader(int id, Bundle args) { // 去数据库读取数据等要消耗大量时间的操作放在 // 自定义 CursorLoader 的 onLoadInBackground return new MyCursorLoader(getApplicationContext()); } public void onLoadFinished(Loader<Cursor> arg0, Cursor cursor) { // Swap the new cursor in. (The framework will take care of closing the // old cursor once we return.) mAdapter.swapCursor(cursor); } public void onLoaderReset(Loader<Cursor> arg0) { // This is called when the last Cursor provided to onLoadFinished() // above is about to be closed. We need to make sure we are no // longer using it. mAdapter.swapCursor(null); } public static class MyCursorLoader extends CursorLoader { String[] mContactProjection = { ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME }; private Context mContext; public MyCursorLoader(Context context) { super(context); mContext = context; } /** * 4.自定义 CursorLoader 的 onLoadInBackground * 会返回一个Cursor,这里给SimpleCursorAdapter用 * 来填充数据。查询数据等操作放在这里执行 */ @Override protected Cursor onLoadInBackground() { Cursor cursor = mContext.getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, mContactProjection, null, null, null); return cursor; } } }
效果:
最后看下自定义BaseAdapter
需要实现下列方法:
abstract Object getItem(int position)
得到位于position位置的项
abstract int getCount()
得到项目总数
abstract long getItemId(int position)
得到位于position位置的项的Id
abstract int getItemViewType(int position)
得到位于position位置的项的类型
abstract View getView(int position,View convertVeiw,ViewGroup parent)
每一项显示的设置
在getView方法中需要缓存加载View优化ListView
官方建议使用ViewHolder,其实就是单例
例:
@Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.list_item_icon_text, null); holder = new ViewHolder(); holder.icon = (ImageView) convertView.findViewById(R.id.icon); holder.text = (TextView) convertView.findViewById(R.id.text); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.icon.setImageResource(R.drawable.icon); holder.text.setText(mData[position]); return convertView; } static class ViewHolder { ImageView icon; TextView text; }