Android开发系列之ListView用法

  ListView是我们开发过程中最常用的控件之一,由于手机屏幕空间都比较有限度,能够一次性在屏幕上显示的内容并不多,当我们程序中有大量的数据需要展示的时候,就可以借助它来完成。它允许用户通过手指上下滑动的方式将屏幕外的数据滚动到屏幕内,同时屏幕上原有的数据则会滚动出屏幕。

一、ListVeiw的简单用法

  首先在XML文件中定义ListView的布局

<ListView
        android:id="@+id/listView01"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        ></ListView>

   然后修改activity中的代码,如下所示:

 1 public class SecondActivity extends AppCompatActivity {
 2 
 3     private String[] data = {"Apple", "Banana", "Orange", "Watermelon", "Pear"};
 4 
 5     @Override
 6     protected void onCreate(Bundle savedInstanceState) {
 7         super.onCreate(savedInstanceState);
 8         setContentView(R.layout.activity_second);
 9 
10         ArrayAdapter<String> adapter = new ArrayAdapter<String>(SecondActivity.this, android.R.layout.simple_list_item_1,data);
11         ListView listView = (ListView)findViewById(R.id.listView01);
12         listView.setAdapter(adapter);
13     }
14 
15 }

  这样,我们就简单的实现了ListView的数据展示。不过,数组中的数据是无法直接传递给ListView的,这里我们就要提到适配器的概念了。Android中提供了很多的适配器类,我们先说下ArrayAdapter,它可以通过泛型来指定要适配的数据类型,然后在构造函数中把要适配的数据传入即可。它有多个构造函数的重载,我们可以根据实际情况选择最合适的一种。上面的代码中由于我们提供的数据都是字符串,因此将ArrayAdapter的泛型指定为String,然后在ArrayAdapter的构造函数中依次传入当前上下文、ListView的子项布局的id、以及要适配的数据。注意,我们使用android.R.layout.simple_list_item_1作为它的子项布局id ,这是Android内置的布局文件,里面只有一个TextView,可用于简单的显示一段文本。调用ListViewd的setAdapter()方法,将构造好的适配器对象传递进去,这样ListView和数据之间的关联就建立完成了。

二、定制ListView

  在项目中ListView不但要展示大量的数据,显示也要非常的美观,只显示一段文本的ListView太过于单调,下面就来介绍一下如何去定制ListView的界面,让它可以显示更加丰富的内容。这里我们让ListView的每一行显示一张图片和一个文本,详细代码如下所示:

  (1)首先在activity对应的XML文件activity_list_view.xml里定义ListView布局

<ListView
        android:id="@+id/listView02"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        ></ListView>

  (2)自定义ListView的子项布局,新建一个XML文件custom_item.xml:

<ImageView
        android:id="@+id/imageView01"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@+id/listView_TextVeiw01"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginLeft="10dp"
        />

  (3)自定义一个实体类CustomClass,作为ListView适配器的适配类型。

 1 /*
 2  *定制CustomClass实体类,作为ListView适配器的适配类型
 3  *name:表示图片的含义
 4  *imageId:表示对应的图片资源ID
 5  */
 6 public class CustomClass {
 7     private String name;
 8     private int imageId;
 9 
10     public CustomClass(String name, int imageId){
11         this.name = name;
12         this.imageId = imageId;
13     }
14 
15     public String getName() {
16         return name;
17     }
18 
19     public int getImageId() {
20         return imageId;
21     }
22 }

  (4)自定义适配器CustomAdapter

 1 /*
 2 * CustomAdapter重写了父类的一组构造函数,用于将上下文、ListVeiw的子项布局的id和数据都传递进来
 3 * 另外,又重写了getView()方法,这个方法在每个子项被滚动到屏幕内的时候调用
 4 * 在getView方法中,首先通过getItem()方法得到当前项的CustomClass实例,然后使用LayoutInflater来
 5 * 为这个子项加载我们传入的布局,接着调用Veiw的findViewById()方法分别获取到ImageVeiw和TextView的实例,
 6 * 并分别调用他们的setImageResource()和setText()方法来设置显示的图片和文字,最后将布局返回。
 7 * */
 8 
 9 
10 public class CustomAdapter extends ArrayAdapter<CustomClass> {
11     private int resourceId;
12 
13     public CustomAdapter(Context context, int textViewResourceId, List<CustomClass> objects){
14         super(context, textViewResourceId, objects);
15         resourceId = textViewResourceId;
16     }
17 
18     public View getView(int position, View convertView, ViewGroup parent){
19         CustomClass custom = getItem(position);
20         View view = LayoutInflater.from(getContext()).inflate(resourceId, null);
21         ImageView customImage = (ImageView)view.findViewById(R.id.imageView01);
22         TextView customTextView = (TextView)view.findViewById(R.id.listView_TextVeiw01);
23         customImage.setImageResource(custom.getImageId());
24         customTextView.setText(custom.getName());
25         return view;
26     }
27 }

  (5)下面修改ListViewActivity中的代码

 1 public class ListViewActivity extends AppCompatActivity {
 2 
 3     private List<CustomClass> customList = new ArrayList<CustomClass>();
 4 
 5     @Override
 6     protected void onCreate(Bundle savedInstanceState) {
 7         super.onCreate(savedInstanceState);
 8         setContentView(R.layout.activity_list_view);
 9 
10         initCustoms();
11         CustomAdapter adapter = new CustomAdapter(ListViewActivity.this, R.layout.custom_item, customList);
12         ListView listView = (ListView)findViewById(R.id.listView02);
13         listView.setAdapter(adapter);
14 
15     }
16     //初始化数据
17     private void initCustoms(){
18         CustomClass chat = new CustomClass("Chat", R.drawable.chat_pressed);
19         customList.add(chat);
20 
21         CustomClass friends = new CustomClass("Friends", R.drawable.friends_pressed);
22         customList.add(friends);
23 
24     }
25 }

  总结:自定义ListView主要自定义了适配器、适配器指定的泛型、ListView的子项,理解了整体流程,实现起来就会更佳方便

  (6)提升ListView的运行效率

  目前我们定制的ListView的运行效率是很低的,因为CustomAdapter的getView()方法中每次都将布局重新加载了一遍,当ListView快速滚动的时候这就会成为性能瓶颈。在getView()中有一个convertView参数,该参数用于将之前加载好的布局进行缓存,以便之后可以进行重用。下面对getView()中的代码进行修改:

public View getView(int position, View convertView, ViewGroup parent){
        CustomClass custom = getItem(position);
        View view;
        if (convertView == null){
            view = LayoutInflater.from(getContext()).inflate(resourceId, null);
        }else {
            view = convertView;
        }
        ImageView customImage = (ImageView)view.findViewById(R.id.imageView01);
        TextView customTextView = (TextView)view.findViewById(R.id.listView_TextVeiw01);
        customImage.setImageResource(custom.getImageId());
        customTextView.setText(custom.getName());
        return view;
    }

  可以看到,我们在getView()中的代码进行了判断,如果convertView为空,则使用LayoutInflater去加载布局,如果不为空则直接对convertView进行重用。这样就大大提高了ListView的运行效率,在快速滚动的时候也可以表现出更好的性能。

  虽然在运行的时候已经不会再重复去加载布局,但是每次在getView方法中还是会调用View的findViewById()方法来获取一次控件的实例,我们可以借助ViewHolder来对这部分性能进行优化,修改CustomAdapter中的代码,如下所示:

 1 public View getView(int position, View convertView, ViewGroup parent){
 2         CustomClass custom = getItem(position);
 3         View view;
 4         ViewHolder viewHolder;
 5         if (convertView == null){
 6             view = LayoutInflater.from(getContext()).inflate(resourceId, null);
 7 
 8             viewHolder = new ViewHolder();
 9             viewHolder.customImage = (ImageView)view.findViewById(R.id.imageView01);
10             viewHolder.customTextView = (TextView)view.findViewById(R.id.listView_TextVeiw01);
11             view.setTag(viewHolder);  //将ViewHolder存储在View中
12 
13         }else {
14             view = convertView;
15             viewHolder = (ViewHolder)view.getTag();  //重新获取ViewHolder
16         }
17 
18         viewHolder.customImage.setImageResource(custom.getImageId());
19         viewHolder.customTextView.setText(custom.getName());
20 
21         return view;
22     }
23 
24     class ViewHolder{
25         ImageView customImage;
26         TextView customTextView;
27     }

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

  (7)ListView的点击事件

  修改ListViewActivity中的代码,如下所示:

 1 protected void onCreate(Bundle savedInstanceState) {
 2         super.onCreate(savedInstanceState);
 3         setContentView(R.layout.activity_list_view);
 4 
 5         initCustoms();
 6         CustomAdapter adapter = new CustomAdapter(ListViewActivity.this, R.layout.custom_item, customList);
 7         ListView listView = (ListView)findViewById(R.id.listView02);
 8         listView.setAdapter(adapter);
 9 
10         listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
11             @Override
12             public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
13                 CustomClass custom = customList.get(i);
14                 Toast.makeText(ListViewActivity.this, custom.getName(), Toast.LENGTH_SHORT).show();
15             }
16         });

 

 

 

 

  

 

posted @ 2017-04-21 07:38  hongsheng  阅读(287)  评论(0编辑  收藏  举报