ListView控件功能全解析
来源:互联网 发布:软件性能指标描述 编辑:程序博客网 时间:2024/06/15 13:34
ListView是Android手机系统中使用非常频繁的控件,以垂直列表的形式显示数据项,ListView的用法也较为复杂,下面将详细介绍ListView相关的各种用法。
1、创建使用ListView
有2种方式创建ListView
- 在布局文件中使用ListView创建
<ListView android:id="@+id/list" android:layout_width="match_parent" android:layout_height="match_parent"/>
在代码中获取到实例后为ListView设置Adapter
listView = (ListView)findViewById(R.id.list); listView.setAdapter(adapter);
- Activity继承ListActivity
继承ListActivity后,不需要设置布局文件,直接使用setListAdapter()为ListView设置Adapter即可
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setListAdapter(adapter); }
2、自定义ListView的item格式
新建布局文件item.xml,代码如下:
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:textColor="#F00" android:textSize="16sp"/>
将文字设为居中显示,设置了文本的颜色和大小,构造Adapter时将布局文件传入
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.item, datas); listView.setAdapter(adapter);
3、设置ListView的点击事件
为ListView的item设置点击事件
listView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // TODO Auto-generated method stub Toast.makeText(MainActivity.this, "Item " + datas.get(position) + " clicked", Toast.LENGTH_LONG).show(); } });
运行程序,点击列表项
4、ListView数据改变,更新ListView
修改ListView的点击事件,点击item后删除List中的数据项,数据改变后,页面不会自动刷新,需要调用adapter的notifyDataSetChanged()方法通知列表更新
listView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // TODO Auto-generated method stub Toast.makeText(MainActivity.this, "Item " + datas.get(position) + " deleted", Toast.LENGTH_LONG).show(); datas.remove(position); adapter.notifyDataSetChanged(); } });
运行程序,点击任意列表项
5、ListView显示多种类型的item
上面的例子都是显示的一个类型的列表项,只包含一个TextView,有时会需要显示几种不同的item,包含图片、文本、按钮等元素,这就需要为ListView定义几个不同的布局文件,一个布局文件代表一种类型的item
布局文件item1.xml
<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" > <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_centerVertical="true" android:src="@drawable/ic_launcher"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:textSize="20sp"/></RelativeLayout>
布局文件item2
<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" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:textSize="20sp"/></RelativeLayout>
布局文件item3
<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" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:textSize="20sp"/> <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true"/></RelativeLayout>
布局文件1中定义了一个ImageView和一个TextView,布局文件2中只有一个TextView,布局文件3中定义了一个TextView和一个CheckBox,下面是MainActivity.java文件
public class MainActivity extends Activity { ListView listView; List<String> datas = new ArrayList<String>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); listView = (ListView)findViewById(R.id.list); initData(); adapter = new ArrayAdapter<String>(this, R.layout.item, datas); listView.setAdapter(new ListAdapter()); } class ListAdapter extends BaseAdapter { public final int TYPE_1 = 0; public final int TYPE_2 = 1; public final int TYPE_3 = 2; @Override public int getCount() { // TODO Auto-generated method stub return datas.size(); } @Override public long getItemId(int position) { // TODO Auto-generated method stub return position; } @Override public Object getItem(int position) { // TODO Auto-generated met hod stub return datas.get(position); } @Override public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub int viewType = getItemViewType(position); View view = null; TextView text = null; switch(viewType) { case TYPE_1: view = View.inflate(MainActivity.this, R.layout.item1, null); text = (TextView)view.findViewById(R.id.text1); break; case TYPE_2: view = View.inflate(MainActivity.this, R.layout.item2, null); text = (TextView)view.findViewById(R.id.text2); break; case TYPE_3: view = View.inflate(MainActivity.this, R.layout.item3, null); text = (TextView)view.findViewById(R.id.text3); break; } text.setText(datas.get(position) + ""); return view; } @Override public int getViewTypeCount() { // TODO Auto-generated method stub return 3; } @Override public int getItemViewType(int position) { // TODO Auto-generated method stub if(position < 5) { return TYPE_1; } else if(position < 10) { return TYPE_2; } else { return TYPE_3; } } } public void initData() { for(int i = 1; i <= 10; i++) { datas.add(i + ""); } }}
重写BaseAdapter的getViewTypeCount()和getItemViewType()方法,定义了3种不同的布局,getViewTypeCount中返回item类型的数目,在getItemViewType中根据位置返回布局类型,getView中根据不同的type加载不同的布局文件,最后,将Adapter设置到ListView中
运行效果:
6、ListView显示优化
ListView在使用过程经常会遇到滑动卡顿的性能问题,所以如何优化ListView的加载就显得尤为重要,ListView的性能优化主要包括几个方面
- 1、利用convertView复用之前的缓存布局,减少加载布局的次数
ListView在滚动的过程中,每加载一个View都会重新加载一遍布局文件,如果ListView中的条目非常多,在快速滑动时,就会导致列表卡顿。在getView()方法中有一个convertView参数,向上滑动列表时,隐藏的view会缓存在这个convertView中,新显示的view可以复用隐藏的convertView,这样就不会每次都加载一遍布局
示例代码
@Override public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub if(convertView == null) { convertView = View.inflate(MainActivity.this, R.layout.item, null); } TextView text = (TextView)convertView.findViewById(R.id.text1); text.setText(datas.get(position)); return convertView; }
在加载布局时,判断convertView是否为空,为空时再去加载布局文件,这样能最大的减少加载布局的次数,提升效率
- 2、使用自定义ViewHolder类,减少findViewById的次数
在解析布局文件查找控件时,需要不断的解析每个节点,如果可以减少查找控件的时间,会对ListView的加载性能提升不少。
示例代码
@Override public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub ViewHolder viewHolder; if(convertView == null) { viewHolder = new ViewHolder(); convertView = View.inflate(MainActivity.this, R.layout.item, null); viewHolder.textView = (TextView)convertView.findViewById(R.id.text); convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder)convertView.getTag(); } viewHolder.textView.setText(datas.get(position)); return convertView; }//自定义ViewHolder类class ViewHolder { TextView textView; //定义item中显示的所有控件 }
在第一次创建View的时候,查找到item中的控件,并存储到ViewHolder类中,通过setTag()方式将ViewHolder设置到convertView中,下次再加载View的时候直接从convertView中获取
- 3、利用ListView的分页加载功能,一次只加载一页条目
如果ListView的条目相当多,在显示ListView的时候一次把所有的条目全部加载完,势必会很耗时间,也会导致列表相响应慢,这时可以考虑来分批加载数据
下面利用ListView的OnScrollListener接口实现ListView的滑动加载功能
主布局文件只有一个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.example.testlistview.MainActivity" > <ListView android:id="@+id/list" android:layout_width="match_parent" android:layout_height="match_parent"/></RelativeLayout>
在列表底部有一个Button,点击Button可以加载更多条目,向上滑动到底部也会自动加载更多条目,加载时Button隐藏,需要显示一个加载的进度条
底部布局文件
<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"> <Button android:id="@+id/load" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="加载更多"/> <LinearLayout android:id="@+id/load_progress" android:layout_width="wrap_content" android:layout_height="20dp" android:orientation="horizontal" android:layout_gravity="center" android:visibility="gone"> <ProgressBar android:layout_width="wrap_content" android:layout_height="wrap_content"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="正在加载..."/> </LinearLayout></LinearLayout>
ListView的滑动加载功能需要实现OnScrollListener接口,滑动加载只关注静止时的状态,当ListView滑动到最底部并且可见的条目数等于adapter的数目,自动加载。当加载的条目和最大条目数相等时,移除底部的View,同时在提示用户没有更多数据可以加载了
MainActivity.java
public class MainActivity extends ActionBarActivity { ListView listView; View footerView; Button load; View loadProgress; List<String> datas = new ArrayList<String>(); ArrayAdapter<String> adapter; public final int MAX_NUM = 50; //最大的条目数 int lastVisibleItem; //最后一个可见的item Handler handler = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); listView = (ListView)findViewById(R.id.list); footerView = getLayoutInflater().inflate(R.layout.load_more, null); load = (Button)footerView.findViewById(R.id.load); loadProgress = footerView.findViewById(R.id.load_progress); initData(); listView.addFooterView(footerView); //将底部布局添加到ListView中 adapter = new ArrayAdapter<String>(this, R.layout.item, datas); listView.setAdapter(adapter); listView.setOnScrollListener(new OnScrollListener() { @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // TODO Auto-generated method stub lastVisibleItem = firstVisibleItem + visibleItemCount -1; //数据加载完毕,移除底部View if(totalItemCount == MAX_NUM + 1) { listView.removeFooterView(footerView); Toast.makeText(MainActivity.this, "没有更多数据可加载", Toast.LENGTH_LONG).show(); } } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { // TODO Auto-generated method stub //当滑动状态为静止并且可见条目数目等于adapter的数目时,加载条目 if(scrollState == OnScrollListener.SCROLL_STATE_IDLE && lastVisibleItem == adapter.getCount()) { load.setVisibility(View.GONE); loadProgress.setVisibility(View.VISIBLE); handler.post(new Runnable() { public void run() { loadData(); adapter.notifyDataSetChanged(); load.setVisibility(View.VISIBLE); loadProgress.setVisibility(View.GONE); }; }); } } }); load.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub //加载更多数据 } }); } public void loadData() { try { Thread.sleep(3000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } int count = adapter.getCount(); for(int i = count + 1; i < count + 6; i++) { datas.add(i + ""); } } public void initData() { for(int i = 1; i <= 15; i++) { datas.add(i + ""); } }}
在代码中为listView添加了滚动监听,主要是实现OnScrollListener接口中的两个方法:onScroll()和onScrollStateChange(),在onScroll方法中计算最后一个可见条目的索引,在onScrollStateChanged中判断滑动状态,如果是静止状态,并且并且最后一个可见条目的索引等于adapter中的条目数,这时开始加载数据。
运行程序
ListView初始化时显示15条数据,向上滑动ListView
在程序中,启动一个线程,线程中等待3秒模拟耗时加载数据,每次加载5条数据,50条数据加载完毕后,底部View被移除,再继续向上滑动时,会给出提示,没有更多数据可以加载
7、ListView的选中模式
ListView具有4种选中模式:
- ListView.CHOICE_MODE_NONE:默认模式
- ListView.CHOICE_MODE_SINGLE:单选模式
- ListView.CHOICE_MODE_MULTIPLE:多选模式,无排除性,会响应onClick事件
- ListView.CHOICE_MODE_MULTIPLE_MODAL:多选模式,有排除性,不响应onClick事件
ListView的默认模式为不会选中任何一个列表项,单选模式只能选中一个列表项,多选模式可以选中多个列表项,ListView默认不会选中任何项
选中的列表项被存在一个SparseBooleanArray中,存储的方式为键值对存储,键存储的是item的索引,值为选中的状态,true或者false,默认模式下SparseBooleanArray为空,单选模式下存的是选中的item项,多选模式下存储所有选中过的项及选中状态
设置ListView为单选模式,在ListView的OnItemClick事件中获取列表项的选择状态
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);listView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // TODO Auto-generated method stub SparseBooleanArray array = listView.getCheckedItemPositions(); for(int i = 0; i < array.size(); i++) { Log.i(TAG, "选中项:" + array.keyAt(i) + ",选中状态:" + array.get(array.keyAt(i))); } } });
点击多个列表项
我按顺序点击了1到4的列表项,从打印的log看出SparseBooleanArray中只保存了一个选中的item状态
修改ListView的选中模式为多选模式
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
运行程序,依次点击1-3的列表项
从log中看出,点击第一个列表项时,SparseBooleanArray中只保存了一条数据,点击多个列表项,会在SparseBooleanArray中保存所有点击过的item,再次点击1-3的列表项
再次点击选中的item项,SparseBooleanArray中依然保存点击过的列表项,只是把选中状态置为了false
根据ListView多选状态的特性,可以在开发过程中遇到需要突出显示选中列表项的时候,利用item的选中状态来改变选中item的颜色,下篇博客中包含这个例子和ListView的另一个多选状态
ListView的两种多选模式:http://blog.csdn.net/zh175578809/article/details/72802461
- ListView控件功能全解析
- iOS设计控件全解析
- android控件之ListView 全教程
- ListView控件目前最全的资料
- 使用ListView控件使用数据报表功能
- 向 ListView 控件添加搜索功能
- C#中加强ListView控件的功能
- C#中加强ListView控件的功能
- C#中加强ListView控件的功能
- c#中listview控件实现排序功能
- C#中加强ListView控件的功能
- C#中加强ListView控件的功能
- 为 ListView 控件增加动态编辑功能
- C# 向 ListView 控件添加搜索功能
- ListView控件的简单查询功能
- C#中加强ListView控件的功能
- 带有下拉刷新功能的Listview控件
- 带有下拉刷新功能的Listview控件
- Python 开发环境搭建实战
- C#泛型集合类(3)
- 同步于中国天气网的第三方天气API(Json,XML)/可套用Demo in Java
- CentOS下SSH配置无密码登录
- 程序设计你不知道的几个原则
- ListView控件功能全解析
- mybatis入门
- 【Android学习笔记系列】AsyncTask的使用和介绍(获取网络图片与进度条实例)
- Two big numbers to multiply
- 安卓RecyclerView万能适配器之baserecyclerviewadapterhelper详解
- Linux开机启动程序详解
- CodeChef SnakeDown2017 E解题报告
- JAVA代码块非静态代码块与静态代码块构造函数比较
- Effective Objective-C 2.0 总结(四)