当前位置: 代码迷 >> Android >> android初学者学习笔记14-Android控件(三) ListView的简单使用
  详细解决方案

android初学者学习笔记14-Android控件(三) ListView的简单使用

热度:25   发布时间:2016-04-28 00:10:31.0
android菜鸟学习笔记14----Android控件(三) ListView的简单使用

MVC模式

MVC的基本原理就是通过Controller连接View和Model。当View中所显示的数据发生变化时,会通知Controller,然后由Controller调用Model中的相关方法执行相应的数据修改操作。反之,当Model中的数据发生变化时,也会通知Controller,由Controller通知View更新显示内容。如此一来,就使得数据部分与视图部分相分离,任何一方发生改变都不会影响到另一方。

而在android中,MVC的一个常见应用就是ListView显示数据。V代表的就是显示控件;M代表的是各种数据源,可以是自己定义的List或者数组,也可以是数据库,文件等;C代表的是Adapter类,android中比较常见的adapter有:BaseAdapter,ArrayAdapter,SimpleAdapter等。

ListView通过setAdapter方法实现了其和一个Adapter对象的绑定,Adapter一般通过getView()方法返回当前列表项所要显示的View对象,完成了对Model中数据的读取。

当Model发生变化时,会调用BaseAdapter.notifyDataSetChanged()方法通知组件数据已然变化,此时Adapter就会调用getView()方法重新显示组件内容。

ListView:间接继承自抽象类AdapterView

常见方法:

void  setOnItemClickListener(AdapterView.OnItemClickListener listener) 

void  setOnItemLongClickListener(AdapterView.OnItemLongClickListener listener) 

void  setAdapter(ListAdapter adapter) 

Adapter:是一个接口

主要的方法:

abstract int  getCount()  返回要显示的item总数

abstract Object  getItem(int position)  根据item索引返回该item

abstract long  getItemId(int position)  返回item的id。

abstract View  getView(int position, View convertView, ViewGroup parent)返回一个用来展示数据源中索引为position的View对象。

BaseAdapter是一个间接实现了Adapter接口的抽象类

自定义Adapter继承BaseAdapter时,需要提供上面四个方法的实现。主要要实现的是getCount()和geView()方法。

ArrayAdapter<T>, CursorAdapter, SimpleAdapter则直接继承自BaseAdapter,实现了其抽象方法。

ListView示例1:展示一个字符串数组中的各个字符串。

 1 main_layout.xml: 2  3 <?xml version="1.0" encoding="utf-8"?> 4  5 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 6  7     android:layout_width="match_parent" 8  9     android:layout_height="match_parent"10 11     android:orientation="vertical" >12 13     <ListView14 15         android:id="@+id/listview"16 17         android:layout_width="wrap_content"18 19         android:layout_height="wrap_content"20 21         />22 23 </LinearLayout>

 MainActivity.java:

 1 public class MainActivity extends Activity { 2  3       4  5       private String[] strs; 6  7       @Override 8  9       protected void onCreate(Bundle savedInstanceState) {10 11            // TODO Auto-generated method stub12 13            super.onCreate(savedInstanceState);14 15            setContentView(R.layout.main_layout);16 17            strs = new String[]{"aaa","bbb","ccc","ddd","eee","fff","ggg","hhh","iii"};18 19            ListView lv = (ListView) findViewById(R.id.listview);20 21            lv.setAdapter(new MyBaseAdapter());22 23           24 25       }26 27       class MyBaseAdapter extends BaseAdapter{28 29            @Override30 31            public int getCount() {32 33                  return strs.length;34 35            }36 37            @Override38 39            public Object getItem(int position) {40 41                  // TODO Auto-generated method stub42 43                  return null;44 45            }46 47            @Override48 49            public long getItemId(int position) {50 51                  return 0;52 53            }54 55            @Override56 57            public View getView(int position, View convertView, ViewGroup parent) {58 59                  TextView tv = null;60 61                  tv = new TextView(MainActivity.this);62 63                  Log.i("listview", position+"get view");64 65                  tv.setText(strs[position]);66 67                  tv.setTextSize(40);68 69                  tv.setTextColor(Color.RED);70 71                  return tv;72 73            }74 75       }76 77 }

 运行结果:

 

注意到,每次滚动屏幕显示新的item,或将滚出屏幕上方的item重新显示出来都会调用getItem()方法。而上面的代码中每次都会新建一个TextView,这样做是存在问题的。

getView(int position, View convertView, ViewGroup parent),其中参数convertView在允许的情况下,会保存旧的View实例,以便拿来复用。所以,可以利用该参数来优化上面的getView()实现。

代码修改如下:

 1 public View getView(int position, View convertView, ViewGroup parent) { 2  3   4  5                  TextView tv = null; 6  7                  Log.i("listview", position+"get view"); 8  9                  if(convertView == null){10 11                       tv = new TextView(MainActivity.this);12 13                  }14 15                  else{16 17                       tv = (TextView) convertView;18 19                  }20 21                  tv.setText(strs[position]);22 23                  tv.setTextSize(40);24 25                  tv.setTextColor(Color.RED);26 27                  return tv;28 29 }

 

其实,若只是显示数组中的内容,直接使用ArrayAdapter会比较方便:

MainActivity.java修改如下:

 1 private String[] strs; 2  3       @Override 4  5       protected void onCreate(Bundle savedInstanceState) { 6  7            // TODO Auto-generated method stub 8  9            super.onCreate(savedInstanceState);10 11            setContentView(R.layout.main_layout);12 13            strs = new String[]{"aaa","bbb","ccc","ddd","eee","fff","ggg","hhh","iii"};14 15            ListView lv = (ListView) findViewById(R.id.listview);16 17            ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, strs);18 19            lv.setAdapter(adapter);20 21           22 23       }

运行结果:

 

其中android.R.layout.simple_list_item_1是系统自带的一个布局id。

一般来说,在ListView中显示的东西可能更加复杂点,只靠一个TextView肯定解决不了,这时,就可以为ListView中的item根据需要编写一个布局文件。

如:item_layout.xml:

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2  3     android:layout_width="match_parent" 4  5     android:layout_height="match_parent" 6  7     android:orientation="horizontal" > 8  9     <ImageView10 11         android:layout_width="wrap_content"12 13         android:layout_height="wrap_content"14 15         android:src="@drawable/hero"16 17         />18 19     <TextView20 21         android:id="@+id/tv_name"22 23         android:layout_width="wrap_content"24 25         android:layout_height="wrap_content"26 27         android:textSize="35sp"28 29         />30 31 </LinearLayout>

 用于在线性布局中水平显示一个图片和一个字符串。

修改MyAdapter内部类的getView():

 1 public View getView(int position, View convertView, ViewGroup parent) { 2  3   4  5                  View view = null; 6  7                  Log.i("listview", position+"get view"); 8  9                  if(convertView == null){10 11                       view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_layout, null);12 13                  }14 15                  else{16 17                       view =  convertView;18 19                  }20 21                  TextView tv = (TextView) view.findViewById(R.id.tv_name);22 23                  tv.setText(strs[position]);24 25                  return view;26 27            }

 运行结果:

 

另一种可能常用的Adapter是SimpleAdapter。

构造函数:SimpleAdapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to)

context指定上下文

data指定存放数据的list对象,list中存放的是Map对象。

resource指定item的布局文件id

from和to是有序的,两者中的元素必须一一对应,from中存放的是list中每个map对象中的key值,to存放的是显示对应key获取的值的控件的id。

如:item_layout.xml:

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2  3     android:layout_width="match_parent" 4  5     android:layout_height="match_parent" 6  7     android:orientation="horizontal" > 8  9     <ImageView10 11         android:id="@+id/iv"12 13         android:layout_width="wrap_content"14 15         android:layout_height="wrap_content"16 17         />18 19     <TextView20 21         android:id="@+id/tv_name"22 23         android:layout_width="wrap_content"24 25         android:layout_height="wrap_content"26 27         android:textSize="35sp"28 29         />30 31 </LinearLayout>

MainActivity.java中修改如下:

 1 protected void onCreate(Bundle savedInstanceState) { 2  3            // TODO Auto-generated method stub 4  5            super.onCreate(savedInstanceState); 6  7            setContentView(R.layout.main_layout); 8  9            ListView lv = (ListView) findViewById(R.id.listview);10 11            List<Map<String,Object>> data = new ArrayList<Map<String,Object>>();12 13            Map<String,Object> map = new HashMap<String, Object>();14 15            map.put("img", R.drawable.hero);16 17            map.put("str", "aaa");18 19            data.add(map);20 21            map = new HashMap<String, Object>();22 23            map.put("img", R.drawable.hero);24 25            map.put("str", "bbb");26 27            data.add(map);28 29            map = new HashMap<String, Object>();30 31            map.put("img", R.drawable.hero);32 33            map.put("str", "ccc");34 35            data.add(map);36 37            map = new HashMap<String, Object>();38 39            map.put("img", R.drawable.hero);40 41            map.put("str", "ddd");42 43            data.add(map);44 45            map = new HashMap<String, Object>();46 47            map.put("img", R.drawable.hero);48 49            map.put("str", "eee");50 51            data.add(map);52 53           54 55            SimpleAdapter sa = new SimpleAdapter(this, data, R.layout.item_layout, new String[]{"img","str"}, new int[]{R.id.iv,R.id.tv_name});56 57            lv.setAdapter(sa);58 59 }

 

显示结果:

 

关于ListView的事件监听:

AdapterView中定义了几个设置事件监听的方法,如:

void  setOnItemClickListener(AdapterView.OnItemClickListener listener) 

void  setOnItemLongClickListener(AdapterView.OnItemLongClickListener listener) 

ListView间接继承了AdapterView,所以,要处理事件监听时,可以调用ListView中相关的方法。

注:如果调用了ListView的setOnClickListener()方法,会报如下错误:

java.lang.RuntimeException: Don't call setOnClickListener for an AdapterView. You probably want setOnItemClickListener instead

setOnItemClickListener()方法示例:

 1 ListView lv = (ListView) findViewById(R.id.listview); 2  3            lv.setOnItemClickListener(new OnItemClickListener() { 4  5                  @Override 6  7                  public void onItemClick(AdapterView<?> parent, View view, 8  9                             int position, long id) {10 11                       Log.i("listview","onClick");12 13                       Log.i("listview",view.toString());14 15                       Log.i("listview",position+"");16 17                       Log.i("listview",id+"");18 19                       TextView tv = (TextView) view.findViewById(R.id.tv_name);20 21                       Toast.makeText(MainActivity.this, tv.getText(), Toast.LENGTH_LONG).show();22 23                  }24 25            });

运行结果:

 

ListView先学到这,等学了数据库操作,也许会用到CursorAdapter等,用到时再学。

  相关解决方案