ListView

一、ListView的简单用法

首先新建一个ListViewTest项目,并让ADT自动帮我们创建好活动。然后修改activity_main.xml中的代码,如下所示:

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     android:layout_width="match_parent"
 3     android:layout_height="match_parent"
 4     android:orientation="vertical" >
 5 
 6     <ListView
 7         android:id="@+id/layout_listView_one_listView1"
 8         android:layout_width="match_parent"
 9         android:layout_height="wrap_content" >
10     </ListView>
11 
12 </LinearLayout>

 

接下来修改MainActivity中的代码,如下所示:

 

 1 package com.example.mmm;
 2 
 3 import android.app.Activity;
 4 import android.os.Bundle;
 5 import android.view.Menu;
 6 import android.view.MenuItem;
 7 import android.view.Window;
 8 import android.widget.ArrayAdapter;
 9 import android.widget.ListView;
10 
11 public class MainActivity extends Activity {
12     
13     
14     private String[] data={"Apple","Banana","Orange","Watermelon","Pear","Grape","Pineapple","Strawberry","Cherry","Mango"};
15     @Override
16     protected void onCreate(Bundle savedInstanceState) {
17         
18         super.onCreate(savedInstanceState);
19         requestWindowFeature(Window.FEATURE_NO_TITLE);
20         setContentView(R.layout.listview);
21         ArrayAdapter<String> adapter =new ArrayAdapter<String>(MainActivity.this,android.R.layout.simple_list_item_1,data);
22         ListView listview=(ListView)findViewById(R.id.listView_1);
23         listview.setAdapter(adapter);
24     }
25     
26     @Override
27     public boolean onCreateOptionsMenu(Menu menu) {
28         
29         // Inflate the menu; this adds items to the action bar if it is present.
30         getMenuInflater().inflate(R.menu.main, menu);
31         return true;
32     }
33     
34     @Override
35     public boolean onOptionsItemSelected(MenuItem item) {
36         
37         // Handle action bar item clicks here. The action bar will
38         // automatically handle clicks on the Home/Up button, so long
39         // as you specify a parent activity in AndroidManifest.xml.
40         int id = item.getItemId();
41         if (id == R.id.action_settings) {
42             return true;
43         }
44         return super.onOptionsItemSelected(item);
45     }
46 }

结果:

二、定制listview界面

 

只能显示一段文本的ListView实在是单调了,我们现在就来对ListView的界面进行定制,让他可以显示更加丰富的内容。

首先需要准备好一组图片,分别对应上面提供的每一种水果,待会儿我们要让这些水果名称旁边都有一个图样。

1.接着定义一个实体类,作为ListView适配器类型。新建类Fruit,代码如下所示:

 1 package com.example.mmm;
 2 
 3 
 4 public class Fruit {
 5     private String name;
 6     private int imageId;
 7     public Fruit(String n,int im){
 8         this.name = n;
 9         this.imageId = im;
10     }
11     public String getName(){
12         return name;
13     }
14     public int getImageId(){
15         return imageId;
16     }
17 }

 

2.然后需要为ListView指向一个自定义的布局,在layout目录下新建fruit_item.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="match_parent"
 5     android:orientation="vertical" >
 6 
 7     <TextView
 8         android:id="@+id/fruit_name"
 9         android:layout_width="wrap_content"
10         android:layout_height="wrap_content"
11         android:text="TextView" />
12     <ImageView
13         android:id="@+id/fruit_image"
14         android:layout_width="wrap_content"
15         android:layout_height="wrap_content"
16         android:layout_gravity="center"
17         android:layout_marginLeft="10dip"/>
18  
19 
20 </LinearLayout>

 

3.接下来需要创建一个自定义的适配器,这个适配器继承自ArrayAdapter,并将泛型指定为Fruit类,新建一个FruitAdapter:

1)继承ArrayAdapter,并将泛型指定为Fruit类。

2)定义一个私有的整型实例变量resourceId

3)继承父类的有三个参数的构造函数,将其中整型参数的值赋值给resourceId

4)重写(Override)父类的有三个参数的getView方法,

5)在此方法中定义一个Fruit fruit,用getItem方法得到Fruit实例。

6)在此方法中定义一个View view,这对应着fruit_item.xml这个自定义布局,调用view的findViewById方法,分别把ImageView和TextView装进去,然后调用ImageView的setImageResource(),把fruit.getImageId()作为参数传入,调用TextView的setText,把fruit.getName()作为参数传入,然后将view返回。注意此方法不需要人为调用。

View的初始化:

LayoutInflater的inflate方法作用是将layout的xml布局文件实例化为View类对象,第一个参数是布局文件的地址,即刚被赋值 的resource,第二个参数是null

getContext的作用是获得上下文

总结:适配器的作用就是给布局文件插了一个吸管,将Fruit类中的数据传入到该布局文件中

 1 package com.example.mmm;
 2 
 3 import java.util.List;
 4 
 5 import android.content.Context;
 6 import android.view.LayoutInflater;
 7 import android.view.View;
 8 import android.view.ViewGroup;
 9 import android.widget.ArrayAdapter;
10 import android.widget.ImageView;
11 import android.widget.TextView;
12 
13 public class FruitAdapter extends ArrayAdapter<Fruit>{
14     private int resourceId;
15 
16     public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects)
17     {
18         super(context, textViewResourceId, objects);
19         // TODO Auto-generated constructor stub
20         resourceId=textViewResourceId;
21     }
22     public View getView(int position,View convertView,ViewGroup parent){
23         Fruit fruit=getItem(position);//获取当前项的Fruit实例
24         View view =LayoutInflater.from(getContext()).inflate(resourceId,null);//创建一个view,与自定义布局上的对应起来,然后把组装好的视图作为返回值传出去
25         ImageView fruitImage =(ImageView)view.findViewById(R.id.fruit_image);
26         TextView fruitName=(TextView)view.findViewById(R.id.fruit_name);
27         fruitImage.setImageResource(fruit.getImageId());
28         fruitName.setText(fruit.getName());
29         return view;
30         
31     }
32 
33     
34 }

 

4.修改MainActivity中的代码:

1)初始化ArrayList,泛型为Fruit,实例对象fruitList,使用了多态,为什么?

2)初始化水果数据,将Fuit的实例对象们添加到FruitList中

3)初始化FruitAdapter 的实例adapter,传入三个参数,上下文(MainActivity.this),布局文件的地址(R.layout.fruit_item)和数据源fruitList。

4)实例化ListView 对象listview,用findViewById

5)调用listview方法setAdapter,传入参数adapter

总结:当新建的适配器类FruitAdapter在主类中被初始化后,且作为到listview的setAdapter方法的参数时,数据源才开始和模板布局文件相匹配,一个个对应并且每一对作为一个整体添加到listview中,利用适配器的好处是,即使有n多个数据,也只需一个模板布局文件和一个适配器类就行了。

 1 package com.example.mmm;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 import android.app.Activity;
 7 import android.os.Bundle;
 8 import android.view.Menu;
 9 import android.view.MenuItem;
10 import android.view.Window;
11 import android.widget.ArrayAdapter;
12 import android.widget.ListView;
13 
14 public class MainActivity extends Activity {
15     
16     
17     private String[] data={"Apple","Banana","Orange","Watermelon","Pear","Grape","Pineapple","Strawberry","Cherry","Mango"};
18     private List<Fruit> fruitList =new ArrayList<Fruit>();
19     @Override
20     protected void onCreate(Bundle savedInstanceState) {
21         
22         super.onCreate(savedInstanceState);
23         requestWindowFeature(Window.FEATURE_NO_TITLE);
24         setContentView(R.layout.listview);
25         initFruits();//初始化水果数据
26         FruitAdapter adapter =new FruitAdapter (MainActivity.this,R.layout.fruit_item,fruitList);
27         ListView listview=(ListView)findViewById(R.id.listView_1);
28         listview.setAdapter(adapter);
29     }
30     public void initFruits(){
31         Fruit apple=new Fruit("apple",R.drawable.apple_pic);
32         fruitList.add(apple);
33         Fruit banana=new Fruit("banana",R.drawable.banana_pic);
34         fruitList.add(banana);
35         Fruit orange=new Fruit("orange",R.drawable.orange_pic);
36         fruitList.add(orange);
37         Fruit pear=new Fruit("pear",R.drawable.pear_pic);
38         fruitList.add(pear);
39         Fruit cherry=new Fruit("cherry",R.drawable.cherry_pic);
40         fruitList.add(cherry);
41         Fruit mango=new Fruit("mango",R.drawable.mango_pic);
42         fruitList.add(mango);
43         Fruit pineapple=new Fruit("pineapple",R.drawable.pineapple_pic);
44         fruitList.add(pineapple);
45         Fruit grape=new Fruit("grape",R.drawable.grape_pic);
46         fruitList.add(grape);
47         Fruit strawberry=new Fruit("strawberry",R.drawable.strawberry_pic);
48         fruitList.add(strawberry);
49         Fruit watermelon=new Fruit("watermelon",R.drawable.watermelon_pic);
50         fruitList.add(watermelon);
51     }
52     @Override
53     public boolean onCreateOptionsMenu(Menu menu) {
54         
55         // Inflate the menu; this adds items to the action bar if it is present.
56         getMenuInflater().inflate(R.menu.main, menu);
57         return true;
58     }
59     
60     @Override
61     public boolean onOptionsItemSelected(MenuItem item) {
62         
63         // Handle action bar item clicks here. The action bar will
64         // automatically handle clicks on the Home/Up button, so long
65         // as you specify a parent activity in AndroidManifest.xml.
66         int id = item.getItemId();
67         if (id == R.id.action_settings) {
68             return true;
69         }
70         return super.onOptionsItemSelected(item);
71     }
72 }

结果

三、提升ListView的运行效率

之所以说ListView难用,是因为他还有很多的细节可以优化,其中很重要的一点就是她的运行效率,目前我们的ListView的运行效率是很低的,因为在FruitAdapter中的getView()方法中每次都将布局重新加载了一遍,比如我们上述有10个数据源,屏幕只能一次显示3个,那么需要滚动屏幕3次,每滚动一次就重新加载一次布局,也就一共加载了4次布局,如果滚动到底端又反过来往上滚的话,由于之前的没有缓存,又要重新调用getView方法,重新加载布局。当ListView快速滚动的时候,这就成为了性能的瓶颈

仔细观察getView方法中有个convertView参数,这个参数用于将之前加载好的布局进行缓存,以便以后可以重用,修改FruitAdapter中的代码,如是:

 1 package com.example.mmm;
 2 
 3 import java.util.List;
 4 
 5 import android.content.Context;
 6 import android.view.LayoutInflater;
 7 import android.view.View;
 8 import android.view.ViewGroup;
 9 import android.widget.ArrayAdapter;
10 import android.widget.ImageView;
11 import android.widget.TextView;
12 
13 public class FruitAdapter extends ArrayAdapter<Fruit>{
14     private int resourceId;
15 
16     public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects)
17     {
18         super(context, textViewResourceId, objects);
19         // TODO Auto-generated constructor stub
20         resourceId=textViewResourceId;
21     }
22     public View getView(int position,View convertView,ViewGroup parent){//系统隐式调用,不需要认为调用
23         Fruit fruit=getItem(position);//获取当前项的Fruit实例
24         View view ;
25         if(convertView==null){
26             view=LayoutInflater.from(getContext()).inflate(resourceId,null);//创建一个view,与自定义布局上的对应起来,然后把组装好的视图作为返回值传出去
27         }else
28             view=convertView;
29         ImageView fruitImage =(ImageView)view.findViewById(R.id.fruit_image);
30         TextView fruitName=(TextView)view.findViewById(R.id.fruit_name);
31         fruitImage.setImageResource(fruit.getImageId());
32         fruitName.setText(fruit.getName());
33         return view;
34         
35     }
36 
37     
38 }

可以看到我们对convertView参数做了判断,如果convertView为空,这表明这一项数据源对应的那个VIew是第一次被加载,就把该view与布局上的模板对应起来。

如果convertView不为null,表明该项数据源对应的View已经被加载过了,直接将convertView赋值给view即可,这样就实现了,已经被加载过的view不用重新对应自定义布局,也就是V重新加载了。这样在快速滚动的时候也能表现出较好的性能。

不过,我们这个代码还是可以继续优化的,虽然现在已经不会再重复区加载布局,但是每次在getView方法中还是会调用View的findViewById方法来获取一次控件的实例。我们借助一个ViewHolder来对这部分性能进行优化,修改FruitAdapter中的代码,如是:

 1 package com.example.mmm;
 2 
 3 import java.util.List;
 4 
 5 import android.content.Context;
 6 import android.view.LayoutInflater;
 7 import android.view.View;
 8 import android.view.ViewGroup;
 9 import android.widget.ArrayAdapter;
10 import android.widget.ImageView;
11 import android.widget.TextView;
12 
13 public class FruitAdapter extends ArrayAdapter<Fruit>{
14     private int resourceId;
15 
16     public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects)
17     {
18         super(context, textViewResourceId, objects);
19         // TODO Auto-generated constructor stub
20         resourceId=textViewResourceId;
21     }
22     public View getView(int position,View convertView,ViewGroup parent){//系统隐式调用,不需要认为调用
23         Fruit fruit=getItem(position);//获取当前项的Fruit实例
24         View view ;
25         ViewHolder viewholder;
26         if(convertView==null){
27             viewholder =new ViewHolder();
28             view=LayoutInflater.from(getContext()).inflate(resourceId,null);//创建一个view,与自定义布局上的对应起来,然后把组装好的视图作为返回值传出去
29             viewholder.fruitImage =(ImageView) view.findViewById(R.id.fruit_image);
30             viewholder.fruitName =(TextView) view.findViewById(R.id.fruit_name);
31         
32             view.setTag(viewholder);
33         }else{
34             view=convertView;
35             viewholder =(ViewHolder)view.getTag();
36         }
37         viewholder.fruitImage.setImageResource(fruit.getImageId());
38         viewholder.fruitName.setText(fruit.getName());
39         return view;
40         
41     }
42 }
43 class ViewHolder{
44     ImageView fruitImage;
45     TextView fruitName;
46 }

我们新增了一个ViewHolder内部类,用于对空间的实现进行缓存,当convertView为空的时候创建一个ViewHolder对象,并将控件的实例都存放在ViewHolder里,然后调用View的setTag()方法,j将Viewholder对象存储在View,当convertView 不为空时,把ViewHolder重新取出,这样所有实例都缓存在了ViewHolder里没有必要每次都通过findViewById的方法来获取控件实例了。

有了这两步的优化之后,我们ListView的运行效果就已经非常不错了。

总结:

ListView运行效率优化方法有两种

1.通过convertView参数对布局进行缓存

2.通过新建一个含有两个实例变量的ViewHolder类对控件进行缓存。

 四、ListView的点击事件

话说回来,ListView的滚动毕竟只是满足了我们的视觉效果,如果不能点击的话也没有什么卵用,因此接下来我们将要学习ListView如何响应用户的点击事件,就该MainActivity.javah中的代码,如是:

 1 package com.example.mmm;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 import android.app.Activity;
 7 import android.os.Bundle;
 8 import android.view.Menu;
 9 import android.view.MenuItem;
10 import android.view.View;
11 import android.view.Window;
12 import android.widget.AdapterView;
13 import android.widget.AdapterView.OnItemClickListener;
14 import android.widget.ArrayAdapter;
15 import android.widget.ListView;
16 import android.widget.Toast;
17 
18 public class MainActivity extends Activity {
19     
20     
21     private String[] data={"Apple","Banana","Orange","Watermelon","Pear","Grape","Pineapple","Strawberry","Cherry","Mango"};
22     private List<Fruit> fruitList =new ArrayList<Fruit>();
23     @Override
24     protected void onCreate(Bundle savedInstanceState) {
25         
26         super.onCreate(savedInstanceState);
27         requestWindowFeature(Window.FEATURE_NO_TITLE);
28         setContentView(R.layout.listview);
29         initFruits();//初始化水果数据
30         FruitAdapter adapter =new FruitAdapter (MainActivity.this,R.layout.fruit_item,fruitList);
31         ListView listview=(ListView)findViewById(R.id.listView_1);
32         listview.setOnItemClickListener(new OnItemClickListener(){
33             public void onItemClick(AdapterView<?>parent,View view ,int position,long id){
34                 Fruit fruit =fruitList.get(position);
35                 Toast.makeText(MainActivity.this, fruit.getName(), Toast.LENGTH_SHORT).show();
36             } 
37         });
38         listview.setAdapter(adapter);
39     }
40     public void initFruits(){
41         Fruit apple=new Fruit("apple",R.drawable.apple_pic);
42         fruitList.add(apple);
43         Fruit banana=new Fruit("banana",R.drawable.banana_pic);
44         fruitList.add(banana);
45         Fruit orange=new Fruit("orange",R.drawable.orange_pic);
46         fruitList.add(orange);
47         Fruit pear=new Fruit("pear",R.drawable.pear_pic);
48         fruitList.add(pear);
49         Fruit cherry=new Fruit("cherry",R.drawable.cherry_pic);
50         fruitList.add(cherry);
51         Fruit mango=new Fruit("mango",R.drawable.mango_pic);
52         fruitList.add(mango);
53         Fruit pineapple=new Fruit("pineapple",R.drawable.pineapple_pic);
54         fruitList.add(pineapple);
55         Fruit grape=new Fruit("grape",R.drawable.grape_pic);
56         fruitList.add(grape);
57         Fruit strawberry=new Fruit("strawberry",R.drawable.strawberry_pic);
58         fruitList.add(strawberry);
59         Fruit watermelon=new Fruit("watermelon",R.drawable.watermelon_pic);
60         fruitList.add(watermelon);
61     }
62     @Override
63     public boolean onCreateOptionsMenu(Menu menu) {
64         
65         // Inflate the menu; this adds items to the action bar if it is present.
66         getMenuInflater().inflate(R.menu.main, menu);
67         return true;
68     }
69     
70     @Override
71     public boolean onOptionsItemSelected(MenuItem item) {
72         
73         // Handle action bar item clicks here. The action bar will
74         // automatically handle clicks on the Home/Up button, so long
75         // as you specify a parent activity in AndroidManifest.xml.
76         int id = item.getItemId();
77         if (id == R.id.action_settings) {
78             return true;
79         }
80         return super.onOptionsItemSelected(item);
81     }
82 }

无论点击哪一个Item,都会调用ListView的setOnItemClickListener方法,根据position参数确定点击的是哪个Fruit

posted @ 2017-07-03 00:39  雁湖初平  阅读(244)  评论(0编辑  收藏  举报