[置顶] Android_ListView_Adapter使用和数据动态加载

 

[置顶] Android_ListView_Adapter使用和数据动态加载

标签: AndroidAdapterListViewBaseAdapteritem
  8009人阅读  评论(3)  收藏  举报
  分类:

目录(?)[+]

1.ListView概述

引用:ListView是比较常用的控件,其存在的最根本的原因在于它的高效,ListView通过对象的复用从而减少内存的消耗,也减少了对象的创建从而也减少的cpu的消耗(在Androidk中创建View对象经常伴随着解析xml)。ListView的本质是一张bitmap(当然所有的控件文字等在屏幕上看到的最终都会变成bitmap),ListView会按照需求,根据Adapter提供的信息把需要的Item画出来显示在屏幕上,当屏幕滚动的时候会重新计算Item的位置并绘制出新的bitmap显示在屏幕上。这样听起来感觉可能不是很高效,但这样带的好处就是,每用为一第个Item 创建一个View对象,样式一样的对象可以共用一个View对象,减少了内存的消耗。而且ListView是事件驱动的,只有当需要的时候才会重新绘制,并且只会 绘制当前屏幕上所显示的Items.

2.ListView有关的三要素

(1).ListVeiw

用来展示列表的View,如果继承了ListActivity则使用getListView()得到listView对象

(2).Adapter

用来把数据映射到ListView上的中介,常用的有ArrayAdapter,SimpleAdapter,CursorAdapter和SimpleCursorAdapter.BaseAdapter

1).ArrayAdapter

构造函数objects接收一个集合或者数组,将会按照集合的元素(或者数组的子集)逐条在ListView列出,一个一行;

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. //ArrayAdapter Constructor  
  2. {  
  3.     new ArrayAdapter(Context context, //The current context.  
  4.             int resource,   /* 
  5.                                 The resource ID for a layout file containing a layout to use when instantiating views 
  6.                                 例如:android.R.layout.simple_list_item_single_choice 
  7.                             */  
  8.             List<T> objects)      //数据对象  
  9. }  

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样式文件,达到自定义的列表文件

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. //SimpleAdapter Constructor  
  2. {  
  3.     SimpleAdapter (Context context, // 上下文  
  4.             List<? extends Map<String, ?>> data,  
  5.                             /* 
  6.                              * A List of Maps.每个list元素就是一列 
  7.                              * Map集合中的key集合,应该包含后面参数(from)中每个元素 
  8.                              */  
  9.             int resource,   //为view layout定义的一列,其中应该包含后面参数(to)中每个元素  
  10.             String[] from,  //从data引出的某些列名  
  11.             int[] to)       //从resource对应的layout文件中,找到相应的id而组成的数组  
  12. }  

4).SimpleCursorAdapter

可以理解为SimpleAdapter对数据库的简单结合,可以方面的把数据库的内容以列表的形式展示出来。

5).BaseAdapter

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * 自定义适配器,常见将Cursor或者其他的数据存储在一个集合中,效率提高! 
  3.  * 
  4.  */  
  5. private class MyAdapter extends BaseAdapter {  
  6.     private List<String> list;  
  7.     //为该适配器设置展现的数据,当我们为该适配器指定了新的数据后,应该调用notifyDataSetChanged()方法后才能完成数据的动态加载  
  8.     public void setList( List<String> list){  
  9.         this.list = list;  
  10.     }  
  11.     @Override  
  12.     public int getCount() {  
  13.         return list.size();  
  14.     }  
  15.   
  16.     @Override  
  17.     public Object getItem(int position) {  
  18.         return list.get(position);  
  19.     }  
  20.   
  21.     @Override  
  22.     public long getItemId(int position) {  
  23.         return position;  
  24.     }  
  25.   
  26.     //重要方法,用于展现该listview的item条目  
  27.     @Override  
  28.     public View getView(int position, View convertView, ViewGroup parent) {  
  29.         /* 
  30.          * convertView为一个item条目对象,每个item滑过之后,创建的item对象有被重新销毁,为此安卓提供一种机制, 
  31.          * 创建的条目不被销毁而是供后来展现的item重复使用,大大提高了效率 
  32.          */  
  33.         TextView textView = null;  
  34.         if (convertView == null) {  //如果没有可供重复使用的item View对象  
  35.             textView = new TextView(getActivity());  
  36.             /* 
  37.              * 里面包含一种高效的处理机制,当每个item比较复杂,如果重复的执行类型转换、查找控件等操作,将会很大浪费, 
  38.              * 可以定义一个类,专门用来存放控件对象的引用。后面的例子键会介绍到 
  39.              */  
  40.         } else {  
  41.             textView = (TextView) convertView;  //如果已经加载将重复使用  
  42.         }  
  43.         textView.setText(list.get(position));  
  44.         textView.setPadding(20202020);  
  45.         textView.setBackgroundColor(Color.GREEN);  
  46.         return textView;  
  47.     }  
  48. }  

(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方法

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. listView.setOnScrollListener(new AbsListView.OnScrollListener() {  
  2.       
  3.     @Override  
  4.     public void onScrollStateChanged(AbsListView view, int scrollState) {  
  5.         //监听listView的状态是否改变  
  6.     }  
  7.       
  8.     @Override  
  9.     public void onScroll(AbsListView view, int firstVisibleItem,  
  10.             int visibleItemCount, int totalItemCount) {  
  11.         //在滚动的过程中会触发该事件  
  12.     }  
  13. });  

(3).下拉刷新底部

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. listView.setOnScrollListener(new OnScrollListener() {  
  2.     private boolean isBottom = false;   //用于标记是否到达顶端  
  3.     //listview的状态发送改变时执行  
  4.     @Override  
  5.     public void onScrollStateChanged(AbsListView view, int scrollState) {  
  6.         System.out.println("scrollState:"+scrollState);  
  7.         if(isBottom && scrollState==OnScrollListener.SCROLL_STATE_IDLE){  
  8.             System.out.println("数据加载");  
  9.             //简单模拟数据加载过程  
  10.             for(int j=0; j<10; j++){  
  11.                 Map<String, String> map = new HashMap<String, String>();  
  12.                 map.put("id", String.valueOf((i++)));  
  13.                 map.put("name""newName="+i);  
  14.                 map.put("age", String.valueOf(i));  
  15.                 adapter.getList().add(map);  
  16.             }  
  17.             adapter.notifyDataSetChanged();  
  18.             isBottom = false;  
  19.         }  
  20.     }  
  21.     //在滚动的过程中不断执行  
  22.     @Override  
  23.     public void onScroll(AbsListView view, int firstVisibleItem,  
  24.             int visibleItemCount, int totalItemCount) {  
  25.         System.out.println(firstVisibleItem+":"+visibleItemCount+":"+totalItemCount);  
  26.         if(firstVisibleItem+visibleItemCount == totalItemCount){  
  27.             isBottom = true;  
  28.         }else{  
  29.             isBottom = false;  
  30.         }  
  31.     }  
  32. });  

5.ListView数据的动态刷新

(1).通过重启loader

ListView动态更新应用很广,比如执行添加或者删除后,数据的动态更新展现。为了达到这种目的,不得不提到适配器的notifyDataSetChanged()方法,有时可能读者在使用的过程中,会发现即便调用了该方法数据并没有实时的更新,先别急,后面将分析原因。

下面一个应用,在主Activity中EditText中添加一条数据,在其内部的ListFragment动态更新数据。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public class MainActivity extends Activity {  
  2.   
  3.     private FragmentManager manager;  
  4.     private Button button;  
  5.     @Override  
  6.     protected void onCreate(Bundle savedInstanceState) {  
  7.         super.onCreate(savedInstanceState);  
  8.         setContentView(R.layout.activity_main);  
  9.         manager = getFragmentManager();  
  10.         button = (Button) findViewById(R.id.confirm);  
  11.   
  12.         //将ListFragment添加进主Activity中,如果没有使用布局加载一个ListView对象  
  13.         if (manager.findFragmentByTag("right") == null) {  
  14.             manager.beginTransaction()  
  15.                 .replace(R.id.right, new RightFrag(), "right")  
  16.                 .addToBackStack("right")  
  17.             .commit();  //事务提交  
  18.         }  
  19.           
  20.         button.setOnClickListener(new View.OnClickListener() {  
  21.             @Override  
  22.             public void onClick(View v) {  
  23.                 //使用内容提供者,添加数据  
  24.                 Uri uri = Uri.parse("content://com.baidu.provider/music");  
  25.                 ContentValues values = new ContentValues();  
  26.                 values.put("name", ((EditText) findViewById(R.id.name)).getText().toString());  
  27.                 Uri u = getContentResolver().insert(uri, values);  
  28.                   
  29.                 //判断数据是否添加成功  
  30.                 if (u != null) {  
  31.                     Toast.makeText(MainActivity.this"添加成功!"1).show();  
  32.                     /* 
  33.                      * 重启loader对象,完成数据的动态加载, 
  34.                      *  读者可能遇到一个问题,数据时重新加载了,但是listView的item重新回到了第一个item位置, 
  35.                      *  这是因为我们在重启的过程中,又为该listView设置了适配器,只要不重新设置,item的位置不会改变 
  36.                      */  
  37.                     getLoaderManager().restartLoader(  
  38.                             1,              //该loaderManger管理下的Id为1的loader  
  39.                             null,           //传递参数bundle,常为null  
  40.                             (RightFrag) (getFragmentManager().findFragmentByTag("right")));  
  41.                             //LoaderManager.LoaderCallbacks的回掉函数,由于该ListFragment实现了该接口  
  42.                 } else {  
  43.                     Toast.makeText(MainActivity.this"添加失败"1).show();  
  44.                 }  
  45.             }  
  46.         });  
  47.     }  
  48. }  
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public class RightFrag extends ListFragment implements LoaderCallbacks<Cursor> {  
  2.     private LoaderManager manager;  
  3.     private MyAdapter adapter;  
  4.     private List<String> list;    //用于存放数据库数据,这里每个item只简单展现一条文本数据  
  5.   
  6.     //完成一些初始化操作  
  7.     @Override  
  8.     public void onCreate(Bundle savedInstanceState) {  
  9.         super.onCreate(savedInstanceState);  
  10.         manager = getLoaderManager();  
  11.         list = new ArrayList<String>();  
  12.           
  13.         adapter = new MyAdapter();  //创建自定义适配器  
  14.         /* 
  15.          * 为该listView设置adapter,如果普通的listView使用setAdapter()方法 
  16.          * 如果该方法在Cursor数据查询完成后设置,listView的将会查询回到第一个。 
  17.          */  
  18.         setListAdapter(adapter);      
  19.         manager.initLoader(1nullthis);  //初始化一个loader对象  
  20.     }  
  21.     /*完成layout布局文件的加载并返回, 
  22.      * 该方法不用重写,由于ListFragment已经默认返回一个listView对象 
  23.     @Override 
  24.     public View onCreateView(LayoutInflater inflater, ViewGroup container, 
  25.             Bundle savedInstanceState) { 
  26.         View view = inflater.inflate(R.layout.list, null); 
  27.         return view; 
  28.     }*/  
  29.   
  30.     //使用内容提供者,返回loader对象  
  31.     @Override  
  32.     public Loader<Cursor> onCreateLoader(int id, Bundle args) {  
  33.         Uri uri = Uri.parse("content://com.baidu.provider/music");  
  34.         //CursorLoader是AsyncTaskLoader的子类,异步查询  
  35.         return new CursorLoader(getActivity(), uri, new String[] { "name" },  
  36.                 nullnullnull);  
  37.     }  
  38.   
  39.     @Override  
  40.     public void onLoadFinished(Loader<Cursor> loader, Cursor data) {  
  41.         /* 
  42.          * 将list数据清空 
  43.          * 如果数据量很大,这种操作就有问题,如果只有增加,我们就不要清空, 
  44.          *  只需要记录上次查询的id,这次查询满足_id > id即可,提高效率 
  45.          */  
  46.         list.clear();  
  47.         while (data.moveToNext()) {  
  48.             list.add(data.getString(data.getColumnIndex("name")));  
  49.         }  
  50.         /* 
  51.          * 使用该方法应该注意,如果数据库的数据发送改变,listView不能动态更新, 
  52.          *  只有适配器的对应的list数据发生了改变,调用该方法才能达到更新的效果 
  53.          */  
  54.         adapter.notifyDataSetChanged();  
  55.     }  
  56.   
  57.     @Override  
  58.     public void onLoaderReset(Loader<Cursor> loader) {  
  59.     }  
  60.     /** 
  61.      * 自定义适配器,常见将Cursor或者其他的数据存储在一个集合中,效率提高! 
  62.      * 
  63.      */  
  64.     private class MyAdapter extends BaseAdapter {  
  65.         @Override  
  66.         public int getCount() {  
  67.             return list.size();  
  68.         }  
  69.       
  70.         @Override  
  71.         public Object getItem(int position) {  
  72.             return list.get(position);  
  73.         }  
  74.       
  75.         @Override  
  76.         public long getItemId(int position) {  
  77.             return position;  
  78.         }  
  79.       
  80.         @Override  
  81.         public View getView(int position, View convertView, ViewGroup parent) {  
  82.             /* 
  83.              * convertView为一个item条目对象,每个item滑过之后,创建的item对象有被重新销毁,为此安卓提供一种机制, 
  84.              * 创建的条目不被销毁而是供后来展现的item重复使用,大大提高了效率 
  85.              */  
  86.             View view = null;  
  87.             if (convertView == null) {  //如果没有可供重复使用的item View对象  
  88.                 view = getActivity().getLayoutInflater().inflate(R.layout.item, null);  
  89.                 //如果view的布局很复杂,可以将内部的控件保存下来  
  90.                 TextView textView = (TextView) view.findViewById(R.id.text);  
  91.                   
  92.                 ViewSet set = new ViewSet();  
  93.                 set.textView = textView;  
  94.                 view.setTag(set);  
  95.             } else {  
  96.                 view = convertView; //如果已经加载将重复使用  
  97.             }  
  98.             //不用重复的查找控件  
  99.             ViewSet views = (ViewSet) view.getTag();  
  100.             views.textView.setText(list.get(position));  
  101.             return view;  
  102.         }  
  103.     }  
  104.     //保存item控件  
  105.     static class ViewSet{  
  106.         TextView textView;  
  107.     }  
  108. }  

(2).使用观察者模式

利用观察者模式,用于观察数据的变化。在设计内容提供者时,查询数据应该注册一个观察者,观察数据的变化。当在增删改时,再通知这种改变。该方法比上面重启loader要高效,应为使用的是同一个cursor对象。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public void setNotificationUri(ContentResolver cr, Uri notifyUri) {    
  2.     mContentResolver = cr;    
  3.     mNotifyUri = notifyUri;    
  4.     mSelfObserver = new SelfContentObserver(this);   
  5.     //内部实际上也是注册了一个观察者  
  6.     mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver);    
  7. }  
上面方法时cursor.setNotificationUri() 内部实现,即注册了一个观察者,当数据发生改版时,再通知。
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public class DBProvider extends ContentProvider{  
  2.     private ContentResolver contentResolver;  
  3.     @Override  
  4.     public Cursor query(Uri uri, String[] projection, String selection,  
  5.             String[] selectionArgs, String sortOrder) {  
  6.         switch (MATCHER.match(uri)) {  
  7.         case 2:  
  8.             SQLiteDatabase db = myDB.getWritableDatabase();  
  9.             Cursor cursor = db.query("music", projection, selection, nullnullnullnull);  
  10.             /* 
  11.              * cursor设置uri监听数据变化,使用了监听者模式 
  12.              * 内部实际上为contentResolver注册了一个观察者 
  13.              */  
  14.             cursor.setNotificationUri(contentResolver, uri);  
  15.             return cursor;  
  16.         }  
  17.         return null;  
  18.     }     
  19.     @Override  
  20.     public Uri insert(Uri uri, ContentValues values) {  
  21.         switch (MATCHER.match(uri)) {  
  22.         case 2:  
  23.             SQLiteDatabase db = myDB.getWritableDatabase();  
  24.             long id = db.insert("music"null, values);  
  25.             if(id > 0){  
  26.                 //数据发生改变,发出通知  
  27.                 contentResolver.notifyChange(uri, null);  
  28.             }  
  29.             return ContentUris.withAppendedId(uri, id);  
  30.         }  
  31.         return null;  
  32.     }  
  33.     //...  
  34. }  

 6.简单适配器的使用

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * ListViewTest.java 
  3.  * @author Administrator 
  4.  * 
  5.  */  
  6. public class MainActivity extends ListActivity {  
  7.     private ListView listView;  
  8.     private List<Map<String, String>> resource;  
  9.   
  10.     @Override  
  11.     protected void onCreate(Bundle savedInstanceState) {  
  12.         super.onCreate(savedInstanceState);  
  13.         setContentView(R.layout.activity_main);  
  14.           
  15.         get();  
  16.         listView = this.getListView();//由于继承了ListActivity,可以直接得到listView控件,但是前提xml文件中需指定默认id  
  17.           
  18. method1()     
  19.         listView.setAdapter(new SimpleAdapter(MainActivity.this, resource,  
  20.                 R.layout.item, new String[] { "name""info" }, new int[] {  
  21.                         R.id.name, R.id.info }));  
  22.         listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);    //设定后才可选中  
  23. method2()  
  24.         listView.setAdapter(new ArrayAdapter<String>(this,  
  25.                 android.R.layout.simple_list_item_single_choice, new String[] {  
  26.                         "河南""湖北""北京""上海""河南""湖北""北京""上海""河南",  
  27.                         "湖北""北京""上海""河南""湖北""北京""上海" }));  
  28.         listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);  
  29.     }  
  30.     public void get() {  
  31.         resource = new ArrayList<Map<String, String>>();  
  32.   
  33.         Map<String, String> map1 = new HashMap<String, String>();  
  34.         map1.put("name""小李飞刀");  
  35.         map1.put("info""good!");  
  36.         resource.add(map1);  
  37.   
  38.         Map<String, String> map2 = new HashMap<String, String>();  
  39.         map2.put("name""北京爱情故事");  
  40.         map2.put("info""ok!");  
  41.         resource.add(map2);  
  42.   
  43.         Map<String, String> map3 = new HashMap<String, String>();  
  44.         map3.put("name""亮剑");  
  45.         map3.put("info""22~~~~~~~~");  
  46.         resource.add(map3);  
  47.     }  
  48. }  
[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <!-- activity_main.xml -->  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     xmlns:tools="http://schemas.android.com/tools"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent"  
  6.     android:orientation="vertical" >  
  7.       
  8.     <LinearLayout android:orientation="horizontal"  
  9.         android:layout_width="match_parent"  
  10.         android:layout_height="wrap_content">  
  11.         <TextView android:layout_height="wrap_content"  
  12.             android:layout_width="match_parent"  
  13.             android:layout_weight="1"  
  14.             android:text="@string/name"  
  15.             android:layout_marginLeft="3sp"/>  
  16.         <TextView android:layout_height="wrap_content"  
  17.             android:layout_width="match_parent"  
  18.             android:layout_weight="1"  
  19.             android:text="@string/age"  
  20.             android:layout_marginLeft="3sp"/>  
  21.     </LinearLayout>  
  22.         <!-- 一定要注意ListView空间的id,详细说明要看本页说明 -->  
  23.     <ListView  
  24.         android:id="@android:id/list"     
  25.         android:layout_width="fill_parent"  
  26.         android:layout_height="match_parent"  
  27.         />  
  28. </LinearLayout>  
[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <!-- item.xml -->  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     xmlns:tools="http://schemas.android.com/tools"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent"  
  6.     android:orientation="vertical" >  
  7.   
  8.     <TextView  
  9.         android:id="@+id/name"  
  10.         android:layout_width="match_parent"  
  11.         android:layout_height="wrap_content"  
  12.         android:textSize="20sp"  
  13.         android:layout_marginLeft="3sp" />  
  14.   
  15.     <TextView  
  16.         android:id="@+id/info"  
  17.         android:layout_width="match_parent"  
  18.         android:layout_height="wrap_content"  
  19.         android:textSize="15sp"  
  20.         android:layout_marginLeft="10sp" />  
  21.   
  22. </LinearLayout>  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值