【参考链接】
一个ListView的基本使用方式如下
activity的布局文件
<ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
item的布局文件
<?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="horizontal">
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_launcher" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Large Text"
/>
</LinearLayout>
Activity
public class BaseAdapterActivity extends Activity {
private List<String> mData;
private ListView mListView;
private MyBaseAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_listview);
initData();
mAdapter = new MyBaseAdapter(this, mData);
mListView = (ListView) findViewById(R.id.lv);
mListView.setAdapter(mAdapter);
}
private void initData(){
mData = new ArrayList<String>();
for (int i = 0; i < 20; i++) {
mData.add("" + i);
}
}
}
Adapter,通过ViewHolder,减少复用时的findViewById()
public class MyBaseAdapter extends BaseAdapter {
private List<String> mData;
private LayoutInflater mInflater;
public final class ViewHolder {
public ImageView img;
public TextView title;
}
public MyBaseAdapter(Context context, List<String> data) {
this.mData = data;
mInflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return mData.size();
}
@Override
public Object getItem(int position) {
return mData.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
// 判断是否缓存
if (convertView == null) {
holder = new ViewHolder();
// 通过LayoutInflater实例化布局
convertView = mInflater.inflate(R.layout.base_adapter_item, null);
holder.img = (ImageView)convertView.findViewById(R.id.imageView);
holder.title = (TextView)convertView.findViewById(R.id.textView);
convertView.setTag(holder);
} else {
// 通过tag找到缓存的布局
holder =(ViewHolder) convertView.getTag();
}
holder.img.setBackgroundResource(R.drawable.ic_launcher);
holder.title.setText(mData.get(position));
return convertView;
}
}
setAdapter()
系统提供了多种Adapter
ArrayAdapter
ArrayAdapter一般用于显示字符串,并且系统提供了很多内置item布局文件,
布局文件中有一个TextView的id为"@android:id/text1",会自动将数据项转换成String显示为TextView的text
public class ArrayAdapterActivity extends Activity {
private List<String> mData;
private ListView mListView;
private ArrayAdapter<String> mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_listview);
initData();
mAdapter=new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mData);
mListView = (ListView) findViewById(R.id.lv);
mListView.setAdapter(mAdapter);
}
private void initData(){
mData = new ArrayList<String>();
for (int i = 0; i < 20; i++) {
mData.add("" + i);
}
}
}
当数据项传递的非String时会转换成String
public class ArrayAdapterActivity2 extends Activity {
private List<Object> mData;
private ListView mListView;
private ArrayAdapter<Object> mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_listview);
initData();
mAdapter=new ArrayAdapter<Object>(this, android.R.layout.simple_list_item_1, mData);
mListView = (ListView) findViewById(R.id.lv);
mListView.setAdapter(mAdapter);
}
private void initData(){
mData = new ArrayList<Object>();
for (int i = 0; i < 20; i++) {
mData.add(new Object());
}
}
}
SimpleAdapter
SimpleAdapte使用两次映射的方式,通过item布局文件中View的id对应到数据项Map的key,再对应到数据项Map的value。
如果View的类型为TextView、Button等,则将value转换成String显示成text
如果View的类型为ImageView等,则将value转换成int作为id取到图片显示成src
item的布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ImageView android:id="@+id/iv_img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5px"
android:padding="10dp"
android:background="#FF0000"/>
<LinearLayout android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FFFFFFFF"
android:textSize="22px"
android:text="title"/>
<TextView android:id="@+id/tv_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FFFFFFFF"
android:textSize="13px"
android:text="info"/>
</LinearLayout>
</LinearLayout>
Activity
public class SimpleAdapterActivity extends Activity {
private List<Map<String, Object>> mData;
private ListView mListView;
private SimpleAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_listview);
initData();
mAdapter = new SimpleAdapter(this,
mData,
R.layout.simple_adapter_item,
new String[]{"title","info","img"},
new int[]{R.id.tv_title, R.id.tv_info, R.id.iv_img});
mListView = (ListView) findViewById(R.id.lv);
mListView.setAdapter(mAdapter);
}
private void initData(){
mData = new ArrayList<Map<String, Object>>();
Map<String, Object> map = new HashMap<String, Object>();
map.put("title", "G1");
map.put("info", "google 1");
map.put("img", R.drawable.img);
mData.add(map);
map = new HashMap<String, Object>();
map.put("title", "G2");
map.put("info", "google 2");
map.put("img", R.drawable.img);
mData.add(map);
map = new HashMap<String, Object>();
map.put("title", "G3");
map.put("info", "google 3");
map.put("img", R.drawable.img+" ");//会导致取不到图片了
mData.add(map);
}
}
BaseAdapter
最初的例子就是BaseAdapter的用法了
notifyDatasetChanged()
当更新数据源以后调用mAdapter.notifyDatasetChanged()即可通知ListView重绘界面
需要注意的是
1. 数据源List必须得是同一个对象
2. notifyDatasetChanged()的调用必须得在主线程中
3. new Thread(){
@Override
public void run() {
mData.add("new");
runOnUiThread(new Runnable() {
@Override
public void run() {
mAdapter.notifyDataSetChanged();
}
});
}
}.start();
setSelection()
将第N项显示在ListView顶部,N从0开始
如果要超出底部,则只滑动到底部,如果要超出顶部,则只滑动到顶部
这个显示是瞬间完成的
smoothScrollToPosition()
向上或向下滑动使第N项显示到界面顶端或底端
如果已在界面中则不进行处理
平滑滑动
getFirstVisiblePosition()、getLastVisiblePosition()
获取在当前显示界面中的第一个、最后一个item在data中的position
getChildCount()、getChildAt()
ListView的子View只包含在当前显示界面中的(包括不完全显示的)
所以注意传递给getChildAt()的参数不能是item在data的position
setOnScrollListener()
注意跟scrollTo()/scrollBy()中的setOnScrollChangeListener()方法不同
有两个回调函数
mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
});
onScrollStateChanged()中的scrollState共有3种
AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:手指在ListView中滑动时
AbsListView.OnScrollListener.SCROLL_STATE_FLING:手指离开ListView,并且ListView会因为惯性而继续滑动时。只有当滑动速度达到一定要求时才会触发,并且只回调一次
AbsListView.OnScrollListener.SCROLL_STATE_IDLE:ListView停止滑动时
一次快速滑动时的日志输出如下
onScroll()
firstVisibleItem:第一个可见的item在data中的位置
visibleItemCount:可见的item的总数
totalItemCount:data中所有的item
setOnItemClickListener()
ListView提供了方法来处理item的点击事件,后面会讲到还可以设置item的背景色
不过现在已经不建议使用这两种方式了,可以交给item的View进行处理
并且这个方法有个弊端就是
当Item当中有Button等时,因为Button会先获取到焦点,导致在item上点击时无法触发到这个方法。