Android开发七:常用控件3--ListView(二)
接着上一节,上一节讲的是通过ArrayAdapter、SimpleAdapter给ListView绑定数据,它们的缺点就是在每一个item里面如果有按钮控件的话,点击是不管用的。这个时候我们就要用BaseAdapter。
这一节主要学习BaseAdapter,最后还会有使用继承ListActivity代替Activity的方法。
BaseAdapter是一个抽象类,要使用就必须要写一个类继承它,并实现它的方法。其中最重要的是getView方法。
这次我要实现的是在ListView中的每一行放上一个TextView和一个ImageButton,点击ImageButton会Toast出来点击的是哪一行。
主布局文件里面只有一个ListView控件
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="fill_parent" 4 android:layout_height="fill_parent" 5 android:orientation="vertical" > 6 7 <ListView 8 android:id="@+id/listView1" 9 android:layout_width="fill_parent" 10 android:layout_height="wrap_content" > 11 </ListView> 12 </LinearLayout>
每一列要显示的内容,新建一个xml文件,内容如下
1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="fill_parent" 4 android:layout_height="fill_parent" 5 android:orientation="horizontal" 6 android:descendantFocusability="blocksDescendants" > 7 8 <TextView 9 android:id="@+id/tvText" 10 android:layout_width="wrap_content" 11 android:layout_height="fill_parent" 12 android:gravity="left|center_vertical" 13 android:textSize="30sp" /> 14 15 16 <ImageButton 17 android:id="@+id/ibView" 18 android:layout_width="wrap_content" 19 android:layout_height="wrap_content" 20 android:layout_alignParentRight="true" 21 android:layout_alignParentTop="true" 22 android:focusable="false" 23 android:src="@android:drawable/ic_menu_view" 24 android:background="#e0000000" /> 25 26 </RelativeLayout>
这个布局文件里面要注意的是android:descendantFocusability="blocksDescendants"和android:focusable="false",设置这两项以后ListView的item才可以获得焦点,否则焦点会再ImageButton上面。
实现过程比较复杂,定义一个ViewHolder来存放控件
//定义一个类来存放控件,对应每一行的布局文件中的控件 class ViewHolder{ public TextView itemtext; public ImageButton itemview; }
然后声明一个类来继承BaseAdapter,代码里面按照我的理解做了说明
1 //声明一个类来继承BaseAdapter 2 class myAdapter extends BaseAdapter{ 3 //定义一个LayoutInflater来导入资源文件用 4 LayoutInflater inflater; 5 //用来接收要绑定的数据 6 ArrayList<HashMap<String, Object>> arrayList; 7 //要绑定的资源文件id 8 int resID; 9 public myAdapter(Context context,ArrayList<HashMap<String, Object>> arrayList2,int resId){ 10 this.inflater=LayoutInflater.from(context); 11 this.arrayList=arrayList2; 12 this.resID=resId; 13 } 14 //绑定的数据的长度,也就是ListView项的个数 15 @Override 16 public int getCount() { 17 // TODO Auto-generated method stub 18 return this.arrayList.size(); 19 } 20 21 @Override 22 public Object getItem(int position) { 23 // TODO Auto-generated method stub 24 return null; 25 } 26 27 @Override 28 public long getItemId(int position) { 29 // TODO Auto-generated method stub 30 return 0; 31 } 32 //重要的方法 33 @Override 34 public View getView(final int position, View convertView, ViewGroup parent) { 35 //创建一个ViewHolder来盛放控件 36 ViewHolder vHolder; 37 //通过判断convertView是否为空来取得ViewHolder对象 38 //这个convertView就是ListView的一行 39 if (convertView==null) { 40 //如果为null则从布局文件中导入 41 convertView=inflater.inflate(this.resID, null); 42 vHolder=new ViewHolder(); 43 vHolder.itemtext=(TextView)convertView.findViewById(R.id.tvText); 44 vHolder.itemview=(ImageButton)convertView.findViewById(R.id.ibView); 45 //setTag方法用来设置与视图关联的标签,我的理解就是把和它相关的ViewHolder存储起来,到用的时候再拿出来 46 convertView.setTag(vHolder); 47 } else { 48 //如果不为null就直接通过getTag取出来 49 vHolder=(ViewHolder)convertView.getTag(); 50 } 51 //然后给ViewHolder对象的每一项赋值 52 vHolder.itemtext.setText(this.arrayList.get(position).get("text").toString()); 53 //添加按钮的点击事件 54 vHolder.itemview.setOnClickListener(new OnClickListener(){ 55 56 @Override 57 public void onClick(View v) { 58 Toast.makeText(ListView_BaseAdapter2.this, "ImageButton "+(position+1)+"clicked!", Toast.LENGTH_SHORT).show(); 59 } 60 }); 61 return convertView; 62 } 63 }
这里定义一个方法来生成数据,我测试的是20000行
1 //定义一个生成数据的方法,因为HashMap的键对应的值可能是String或者是资源的ID,所以这里用的是HashMap<String, Object> 2 private ArrayList<HashMap<String, Object>> getData() { 3 ArrayList<HashMap<String, Object>> aList=new ArrayList<HashMap<String, Object>>(); 4 for (int i = 0; i < 20000; i++) { 5 HashMap<String, Object> map=new HashMap<String, Object>(); 6 map.put("text", "第"+(i+1)+"行"); 7 aList.add(map); 8 } 9 return aList; 10 }
最后在ListView上面绑定数据
1 //获取ListView对象 2 lView=(ListView)findViewById(R.id.listView1); 3 //使用创建的adatper对象绑定数据 4 myAdapter adapter=new myAdapter(ListView_BaseAdapter2.this, getData(), R.layout.item); 5 lView.setAdapter(adapter);
这样就实现了每一行的ImageButton可点击,弹出Toast消息,显示所在行。测试的效果还算可以,2000行数据在我的V880上面平均是2.5秒的时间,打开以后滑动十分流畅。
下面是本次的完整代码
1 package com.yyj.ListView_BaseAdapter; 2 3 import java.util.ArrayList; 4 import java.util.HashMap; 5 6 import android.app.Activity; 7 import android.content.Context; 8 import android.os.Bundle; 9 import android.view.LayoutInflater; 10 import android.view.View; 11 import android.view.View.OnClickListener; 12 import android.view.ViewGroup; 13 import android.widget.BaseAdapter; 14 import android.widget.ImageButton; 15 import android.widget.ListView; 16 import android.widget.TextView; 17 import android.widget.Toast; 18 19 public class ListView_BaseAdapter2 extends Activity { 20 ListView lView; 21 @Override 22 protected void onCreate(Bundle savedInstanceState) { 23 super.onCreate(savedInstanceState); 24 setContentView(R.layout.main); 25 //获取ListView对象 26 lView=(ListView)findViewById(R.id.listView1); 27 //使用创建的adatper对象绑定数据 28 myAdapter adapter=new myAdapter(ListView_BaseAdapter2.this, getData(), R.layout.item); 29 lView.setAdapter(adapter); 30 } 31 //定义一个生成数据的方法,因为HashMap的键对应的值可能是String或者是资源的ID,所以这里用的是HashMap<String, Object> 32 private ArrayList<HashMap<String, Object>> getData() { 33 ArrayList<HashMap<String, Object>> aList=new ArrayList<HashMap<String, Object>>(); 34 for (int i = 0; i < 20000; i++) { 35 HashMap<String, Object> map=new HashMap<String, Object>(); 36 map.put("text", "第"+(i+1)+"行"); 37 aList.add(map); 38 } 39 return aList; 40 } 41 //定义一个类来存放控件,对应每一行的布局文件中的控件 42 class ViewHolder{ 43 public TextView itemtext; 44 public ImageButton itemview; 45 } 46 //声明一个类来继承BaseAdapter 47 class myAdapter extends BaseAdapter{ 48 //定义一个LayoutInflater来导入资源文件用 49 LayoutInflater inflater; 50 //用来接收要绑定的数据 51 ArrayList<HashMap<String, Object>> arrayList; 52 //要绑定的资源文件id 53 int resID; 54 public myAdapter(Context context,ArrayList<HashMap<String, Object>> arrayList2,int resId){ 55 this.inflater=LayoutInflater.from(context); 56 this.arrayList=arrayList2; 57 this.resID=resId; 58 } 59 //绑定的数据的长度,也就是ListView项的个数 60 @Override 61 public int getCount() { 62 // TODO Auto-generated method stub 63 return this.arrayList.size(); 64 } 65 66 @Override 67 public Object getItem(int position) { 68 // TODO Auto-generated method stub 69 return null; 70 } 71 72 @Override 73 public long getItemId(int position) { 74 // TODO Auto-generated method stub 75 return 0; 76 } 77 //重要的方法 78 @Override 79 public View getView(final int position, View convertView, ViewGroup parent) { 80 //创建一个ViewHolder来盛放控件 81 ViewHolder vHolder; 82 //通过判断convertView是否为空来取得ViewHolder对象 83 //这个convertView就是ListView的一行 84 if (convertView==null) { 85 //如果为null则从布局文件中导入 86 convertView=inflater.inflate(this.resID, null); 87 vHolder=new ViewHolder(); 88 vHolder.itemtext=(TextView)convertView.findViewById(R.id.tvText); 89 vHolder.itemview=(ImageButton)convertView.findViewById(R.id.ibView); 90 //setTag方法用来设置与视图关联的标签,我的理解就是把和它相关的ViewHolder存储起来,到用的时候再拿出来 91 convertView.setTag(vHolder); 92 } else { 93 //如果不为null就直接通过getTag取出来 94 vHolder=(ViewHolder)convertView.getTag(); 95 } 96 //然后给ViewHolder对象的每一项赋值 97 vHolder.itemtext.setText(this.arrayList.get(position).get("text").toString()); 98 //添加按钮的点击事件 99 vHolder.itemview.setOnClickListener(new OnClickListener(){ 100 101 @Override 102 public void onClick(View v) { 103 Toast.makeText(ListView_BaseAdapter2.this, "ImageButton "+(position+1)+"clicked!", Toast.LENGTH_SHORT).show(); 104 } 105 }); 106 return convertView; 107 } 108 } 109 }
效果图:
最后附上源文件:ListView_BaseAdapter.zip
下面在简单的说一下,在主布局文件中只有一个ListView的时候,我们可以使用ListActivity,ListActivity继承自Activity类,默认绑定了一个ListView,并提供一些处理ListView的操作。
直接贴上代码,里面要注意的地方已经注释上了
main.xml
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="fill_parent" 4 android:layout_height="fill_parent" 5 android:orientation="vertical" > 6 7 <!-- 注意这个ListView的id写法,这个是默认的id --> 8 <ListView 9 android:id="@+id/android:list" 10 android:layout_width="fill_parent" 11 android:layout_height="wrap_content" 12 ></ListView> 13 </LinearLayout>
user.xml
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="50dp" 5 android:orientation="horizontal" > 6 7 <TextView 8 android:id="@+id/uname" 9 android:layout_width="fill_parent" 10 android:layout_height="fill_parent" 11 android:gravity="center" 12 android:layout_weight="1"/> 13 <TextView 14 android:id="@+id/uage" 15 android:layout_width="fill_parent" 16 android:layout_height="fill_parent" 17 android:gravity="center" 18 android:layout_weight="1"/> 19 </LinearLayout>
ListViewTestActivity.java
1 package com.yyj.ListViewTest; 2 3 import java.util.ArrayList; 4 import java.util.HashMap; 5 6 import android.app.ListActivity; 7 import android.os.Bundle; 8 import android.view.View; 9 import android.widget.ListView; 10 import android.widget.SimpleAdapter; 11 import android.widget.TextView; 12 import android.widget.Toast; 13 14 //注意,这里继承的是ListActivity,不是Activity 15 public class ListViewTestActivity extends ListActivity { 16 /** Called when the activity is first created. */ 17 @Override 18 public void onCreate(Bundle savedInstanceState) { 19 super.onCreate(savedInstanceState); 20 setContentView(R.layout.main); 21 22 ArrayList<HashMap<String, Object>> arrayList=new ArrayList<HashMap<String,Object>>(); 23 HashMap<String, Object> user1=new HashMap<String, Object>(); 24 user1.put("username", "小明"); 25 user1.put("userage", 24); 26 HashMap<String, Object> user2=new HashMap<String, Object>(); 27 user2.put("username", "小红"); 28 user2.put("userage", 22); 29 HashMap<String, Object> user3=new HashMap<String, Object>(); 30 user3.put("username", "小丽"); 31 user3.put("userage", 24); 32 33 for(int i=0;i++<5;){ 34 arrayList.add(user1); 35 arrayList.add(user2); 36 arrayList.add(user3); 37 } 38 39 SimpleAdapter adapter=new SimpleAdapter(ListViewTestActivity.this, arrayList, R.layout.user, new String[]{"username","userage"},new int[]{R.id.uname,R.id.uage} ); 40 //ListActivity提供的绑定数据的方法 41 setListAdapter(adapter); 42 } 43 44 //ListView每一项点击事件,覆盖父类的方法和以前用过的匿名内部类方法不同 45 @Override 46 protected void onListItemClick(ListView l, View v, int position, long id) { 47 String str=((TextView)v.findViewById(R.id.uname)).getText().toString()+"今年"+((TextView)v.findViewById(R.id.uage)).getText().toString(); 48 Toast.makeText(ListViewTestActivity.this,str, Toast.LENGTH_LONG).show(); 49 50 super.onListItemClick(l, v, position, id); 51 } 52 53 }
效果图: