1.ListView概述
引用:ListView是比较常用的控件,其存在的最根本的原因在于它的高效,ListView通过对象的复用从而减少内存的消耗,也减少了对象的创建从而也减少的cpu的消耗(在Androidk中创建View对象经常伴随着解析xml)。ListView的本质是一张bitmap(当然所有的控件文字等在屏幕上看到的最终都会变成bitmap),ListView会按照需求,根据Adapter提供的信息把需要的Item画出来显示在屏幕上,当屏幕滚动的时候会重新计算Item的位置并绘制出新的bitmap显示在屏幕上。这样听起来感觉可能不是很高效,但这样带的好处就是,每用为一第个Item 创建一个View对象,样式一样的对象可以共用一个View对象,减少了内存的消耗。而且ListView是事件驱动的,只有当需要的时候才会重新绘制,并且只会 绘制当前屏幕上所显示的Items.
2.ListView有关的三要素
![](https://img-blog.csdn.net/20130722095228921?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3RyYXdiZXJyeTIwMTM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
(1).ListVeiw
用来展示列表的View,如果继承了ListActivity则使用getListView()得到listView对象
(2).Adapter
用来把数据映射到ListView上的中介,常用的有ArrayAdapter,SimpleAdapter,CursorAdapter和SimpleCursorAdapter.BaseAdapter
1).ArrayAdapter
构造函数objects接收一个集合或者数组,将会按照集合的元素(或者数组的子集)逐条在ListView列出,一个一行;
-
- {
- new ArrayAdapter(Context context,
- int resource,
-
-
-
- List<T> objects)
- }
2).CursorAdapter
Adapter that exposes data from a Cursor to a ListView widget. The Cursor must include a column named "_id" or this class will not work;
3).SimpleAdapter
有最好的扩充性,通过自定义xml样式文件,达到自定义的列表文件
-
- {
- SimpleAdapter (Context context,
- List<? extends Map<String, ?>> data,
-
-
-
-
- int resource,
- String[] from,
- int[] to)
- }
4).SimpleCursorAdapter
可以理解为SimpleAdapter对数据库的简单结合,可以方面的把数据库的内容以列表的形式展示出来。
5).BaseAdapter
-
-
-
-
- private class MyAdapter extends BaseAdapter {
- private List<String> list;
-
- public void setList( List<String> list){
- this.list = list;
- }
- @Override
- public int getCount() {
- return list.size();
- }
-
- @Override
- public Object getItem(int position) {
- return list.get(position);
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
-
-
-
-
- TextView textView = null;
- if (convertView == null) {
- textView = new TextView(getActivity());
-
-
-
-
- } else {
- textView = (TextView) convertView;
- }
- textView.setText(list.get(position));
- textView.setPadding(20, 20, 20, 20);
- textView.setBackgroundColor(Color.GREEN);
- return textView;
- }
- }
(3).Data数据
List集合或者一个Cursor实例对象。但是如果查询数据库,将会很耗时,因此将使用loaderManager进行统一的管理,异步完成Cursor到数据集合的转化,可参考使用LoaderManager管理Loader实现异步动态加载数据一文。
3.ListView的id说明
(1).ListActivity子类
如果java类继承了ListActivity,那么表明该类只能存在一个ListView控件,可以通过getListView()方法取得默认的控件,但是在相应的xml文件中,定义的ListView必须指明固定的id,android:id="@android:id/list"
(2).Activity子类
如果Java类继承了Activity,那么id就没有特定的要求,xml支持自定义id
4.ListView的滚动事件
(1).AbsListView.OnScrollListener事件类型
事件常量 | 常量值 | 描述 |
SCROLL_STATE_IDLE | 0 | 当前item处于空闲状态,没有发生滚动 |
SCROLL_STATE_TOUCH_SCROLL | 1 | item正在滚动,但是手指还停留在屏幕上 |
SCROLL_STATE_FLING | 2 | 类似惯性,当前item正在滚动 |
(2).OnScrollListener方法
- listView.setOnScrollListener(new AbsListView.OnScrollListener() {
-
- @Override
- public void onScrollStateChanged(AbsListView view, int scrollState) {
-
- }
-
- @Override
- public void onScroll(AbsListView view, int firstVisibleItem,
- int visibleItemCount, int totalItemCount) {
-
- }
- });
(3).下拉刷新底部
- listView.setOnScrollListener(new OnScrollListener() {
- private boolean isBottom = false;
-
- @Override
- public void onScrollStateChanged(AbsListView view, int scrollState) {
- System.out.println("scrollState:"+scrollState);
- if(isBottom && scrollState==OnScrollListener.SCROLL_STATE_IDLE){
- System.out.println("数据加载");
-
- for(int j=0; j<10; j++){
- Map<String, String> map = new HashMap<String, String>();
- map.put("id", String.valueOf((i++)));
- map.put("name", "newName="+i);
- map.put("age", String.valueOf(i));
- adapter.getList().add(map);
- }
- adapter.notifyDataSetChanged();
- isBottom = false;
- }
- }
-
- @Override
- public void onScroll(AbsListView view, int firstVisibleItem,
- int visibleItemCount, int totalItemCount) {
- System.out.println(firstVisibleItem+":"+visibleItemCount+":"+totalItemCount);
- if(firstVisibleItem+visibleItemCount == totalItemCount){
- isBottom = true;
- }else{
- isBottom = false;
- }
- }
- });
5.ListView数据的动态刷新
(1).通过重启loader
ListView动态更新应用很广,比如执行添加或者删除后,数据的动态更新展现。为了达到这种目的,不得不提到适配器的notifyDataSetChanged()方法,有时可能读者在使用的过程中,会发现即便调用了该方法数据并没有实时的更新,先别急,后面将分析原因。
下面一个应用,在主Activity中EditText中添加一条数据,在其内部的ListFragment动态更新数据。
- public class MainActivity extends Activity {
-
- private FragmentManager manager;
- private Button button;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- manager = getFragmentManager();
- button = (Button) findViewById(R.id.confirm);
-
-
- if (manager.findFragmentByTag("right") == null) {
- manager.beginTransaction()
- .replace(R.id.right, new RightFrag(), "right")
- .addToBackStack("right")
- .commit();
- }
-
- button.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
-
- Uri uri = Uri.parse("content://com.baidu.provider/music");
- ContentValues values = new ContentValues();
- values.put("name", ((EditText) findViewById(R.id.name)).getText().toString());
- Uri u = getContentResolver().insert(uri, values);
-
-
- if (u != null) {
- Toast.makeText(MainActivity.this, "添加成功!", 1).show();
-
-
-
-
-
- getLoaderManager().restartLoader(
- 1,
- null,
- (RightFrag) (getFragmentManager().findFragmentByTag("right")));
-
- } else {
- Toast.makeText(MainActivity.this, "添加失败", 1).show();
- }
- }
- });
- }
- }
- public class RightFrag extends ListFragment implements LoaderCallbacks<Cursor> {
- private LoaderManager manager;
- private MyAdapter adapter;
- private List<String> list;
-
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- manager = getLoaderManager();
- list = new ArrayList<String>();
-
- adapter = new MyAdapter();
-
-
-
-
- setListAdapter(adapter);
- manager.initLoader(1, null, this);
- }
-
-
-
-
-
-
-
-
-
-
- @Override
- public Loader<Cursor> onCreateLoader(int id, Bundle args) {
- Uri uri = Uri.parse("content://com.baidu.provider/music");
-
- return new CursorLoader(getActivity(), uri, new String[] { "name" },
- null, null, null);
- }
-
- @Override
- public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
-
-
-
-
-
- list.clear();
- while (data.moveToNext()) {
- list.add(data.getString(data.getColumnIndex("name")));
- }
-
-
-
-
- adapter.notifyDataSetChanged();
- }
-
- @Override
- public void onLoaderReset(Loader<Cursor> loader) {
- }
-
-
-
-
- private class MyAdapter extends BaseAdapter {
- @Override
- public int getCount() {
- return list.size();
- }
-
- @Override
- public Object getItem(int position) {
- return list.get(position);
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
-
-
-
-
- View view = null;
- if (convertView == null) {
- view = getActivity().getLayoutInflater().inflate(R.layout.item, null);
-
- TextView textView = (TextView) view.findViewById(R.id.text);
-
- ViewSet set = new ViewSet();
- set.textView = textView;
- view.setTag(set);
- } else {
- view = convertView;
- }
-
- ViewSet views = (ViewSet) view.getTag();
- views.textView.setText(list.get(position));
- return view;
- }
- }
-
- static class ViewSet{
- TextView textView;
- }
- }
(2).使用观察者模式
利用观察者模式,用于观察数据的变化。在设计内容提供者时,查询数据应该注册一个观察者,观察数据的变化。当在增删改时,再通知这种改变。该方法比上面重启loader要高效,应为使用的是同一个cursor对象。
- public void setNotificationUri(ContentResolver cr, Uri notifyUri) {
- mContentResolver = cr;
- mNotifyUri = notifyUri;
- mSelfObserver = new SelfContentObserver(this);
-
- mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver);
- }
上面方法时cursor.setNotificationUri() 内部实现,即注册了一个观察者,当数据发生改版时,再通知。
- public class DBProvider extends ContentProvider{
- private ContentResolver contentResolver;
- @Override
- public Cursor query(Uri uri, String[] projection, String selection,
- String[] selectionArgs, String sortOrder) {
- switch (MATCHER.match(uri)) {
- case 2:
- SQLiteDatabase db = myDB.getWritableDatabase();
- Cursor cursor = db.query("music", projection, selection, null, null, null, null);
-
-
-
-
- cursor.setNotificationUri(contentResolver, uri);
- return cursor;
- }
- return null;
- }
- @Override
- public Uri insert(Uri uri, ContentValues values) {
- switch (MATCHER.match(uri)) {
- case 2:
- SQLiteDatabase db = myDB.getWritableDatabase();
- long id = db.insert("music", null, values);
- if(id > 0){
-
- contentResolver.notifyChange(uri, null);
- }
- return ContentUris.withAppendedId(uri, id);
- }
- return null;
- }
-
- }
6.简单适配器的使用
-
-
-
-
-
- public class MainActivity extends ListActivity {
- private ListView listView;
- private List<Map<String, String>> resource;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
-
- get();
- listView = this.getListView();
-
-
- listView.setAdapter(new SimpleAdapter(MainActivity.this, resource,
- R.layout.item, new String[] { "name", "info" }, new int[] {
- R.id.name, R.id.info }));
- listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
-
- listView.setAdapter(new ArrayAdapter<String>(this,
- android.R.layout.simple_list_item_single_choice, new String[] {
- "河南", "湖北", "北京", "上海", "河南", "湖北", "北京", "上海", "河南",
- "湖北", "北京", "上海", "河南", "湖北", "北京", "上海" }));
- listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
- }
- public void get() {
- resource = new ArrayList<Map<String, String>>();
-
- Map<String, String> map1 = new HashMap<String, String>();
- map1.put("name", "小李飞刀");
- map1.put("info", "good!");
- resource.add(map1);
-
- Map<String, String> map2 = new HashMap<String, String>();
- map2.put("name", "北京爱情故事");
- map2.put("info", "ok!");
- resource.add(map2);
-
- Map<String, String> map3 = new HashMap<String, String>();
- map3.put("name", "亮剑");
- map3.put("info", "22~~~~~~~~");
- resource.add(map3);
- }
- }
-
- <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" >
-
- <LinearLayout android:orientation="horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <TextView android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:layout_weight="1"
- android:text="@string/name"
- android:layout_marginLeft="3sp"/>
- <TextView android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:layout_weight="1"
- android:text="@string/age"
- android:layout_marginLeft="3sp"/>
- </LinearLayout>
-
- <ListView
- android:id="@android:id/list"
- android:layout_width="fill_parent"
- android:layout_height="match_parent"
- />
- </LinearLayout>
-
- <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" >
-
- <TextView
- android:id="@+id/name"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="20sp"
- android:layout_marginLeft="3sp" />
-
- <TextView
- android:id="@+id/info"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="15sp"
- android:layout_marginLeft="10sp" />
-
- </LinearLayout>