Android--ListView作用详解

本文详细介绍了Android中ListView的用途、属性、方法以及两种使用方式:ListActivity和使用ListView控件构建。通过实例展示了如何设置ListView的适配器、自定义界面以及提升运行效率。此外,还提供了代码下载链接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.ListView

  ListView列表视图控件是Android中常用的控件之一,其直接继承了AbsListView,是一个以垂直方式在项目中显示View视图的列表。ListView的数据项,来自一个继承了ListAdapter接口的适配器。

  ListView的常用属性一般就是用来设置列表的间隔、分割线、表头、表尾等属性的,常用属性有以下几个,并且Android也为其提供了对应的setter/getter方法:

  1. android:divider:使用一个Drawable或者color设置数据项之间的间隔样式。
  2. android:dividerHeight:设置数据项之间的间隔距离。
  3. android:entries:设置一个资源Id用于填充ListView的数据项。
  4. android:footerDividersEnabled:设定列表表尾是否显示分割线,如果有表尾的话。
  5. android:headerDividerEnabled:设定列表表头是否显示分割线,如果有表头的话。

  ListView提供了一些方法,用于操作ListView。这里介绍一些常用的方法,更多的请参见API文档:

  1. void addFooterView(View v):添加表尾View视图。
  2. boolean removeFooterView(View v):移除一个表尾View视图。
  3. void addHeaderView(View v):添加一个表头View视图。
  4. boolean removeHeaderView(View v):移除一个表头View视图。
  5. ListAdapter getAdapter():获取当前绑定的ListAdapter适配器。
  6. void setAdapter(ListAdapter adapter):设置一个ListAdapter适配器到当前ListView中。
  7. void setSelection(int posotion):设定当前选中项。
  8. long[] getCheckItemIds():获取当前选中项。

  作为一个列表选择控件,ListView具有一些选中选项可以触发的事件,但它本身没有定义这些事件,均继承自间接父类AdapterView。ListView支持的几个常用事件有以下几个:

  1. AdapterView.OnItemCLickListener:列表项被点击时触发。
  2. AdapterView.OnItemLongClickListener:列表项被长按时触发。
  3. AdapterView.OnItemSelectedListener:列表项被选择时触发。

2. ListView两种使用方式

        在Android项目中使用ListView,有两种方式,一种是通过一个继承了ListActivity的Activity,在其中设定ListAdapter,对于这种方式,比较适用于整个页面就是一个ListView;第二种方式就是直接使用ListView控件,这种方式也是项目中比较常用的方式。

        接下来就对这两种使用ListView方式进行详细讲解。首先新建一个listViewTest工程,并让Android studio帮我们自动生成主活动和布局。在activity_main.xml布局文件中添加两个按钮,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.fd.listviewtest.MainActivity">

    <Button
        android:id="@+id/btn_list_activity"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="使用ListActivity"
        android:textAllCaps="false" />

    <Button
        android:id="@+id/btn_list_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="使用ListView"
        android:textAllCaps="false" />

</LinearLayout>

        MainActivity的java代码如下所示:

package com.fd.listviewtest;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        bindView();
    }

    private void bindView() {
        findViewById(R.id.btn_list_activity).setOnClickListener(this);
        findViewById(R.id.btn_list_view).setOnClickListener(this);
    }


    @Override
    public void onClick(View v) {
        Intent intent;
        switch (v.getId()){
            case R.id.btn_list_activity:
                intent = new Intent(this, UseListActivity.class);
                startActivity(intent);
                break;
            case R.id.btn_list_view:
                intent = new Intent(this, UseListViewActivity.class);
                startActivity(intent);
                break;
        }
    }
}

        布局和代码都很简单,这里就不作讲解了。

2.1 ListActivity

  ListActivity继承了Activity,并通过绑定一个ListAdapter来显示一个数据列表。需要注意的是,如果对列表项的数据格式没有特殊要求,它完全可以不使用布局文件即可创建一个ListView,因为ListActivity类本身已经包含了一个ListView。因此在onCreate()方法中,不需要调用setContentView()方法来从一个布局文件加载用户界面。

  在ListActivity的onCreate()方法中,可以直接使用this.setListAdapter()方法为这个ListView设定ListAdapter。如果想获得并操作这个ListActivity自带的ListView,可以使用this.getListView()方法获取。

        下面讲解一下使用继承ListActivity的方式来实现ListView,因为这里只是使用一个ArrayAdapter填充数据,因此无需指定布局文件。UseListActivity的代码如下:

package com.fd.listviewtest;

import android.app.ListActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

public class UseListActivity extends ListActivity {
    String[] animal = {"Duck", "Pig", "Panda", "Fish", "Tiger", "Cat", "Dog", "Bird"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ListView listView = getListView();
        listView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, animal));
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        Toast.makeText(this, "你选择了:" + animal[position], Toast.LENGTH_SHORT).show();
    }
}

可以看到,在onCreate方法中,首先通过getListView方法获取到ListActivity类中定义的ListView对象,然后通过ListView对象的setAdapter方法来设置适配器及数据。同时实现了每个列表项的点击事件onListItemClick。现在运行一下程序,效果如下图所示:

2.2使用ListView控件构建

        上面介绍的这种方式会将整个Activity都作为一个ListView,但是在实际项目中,一般还是把ListView作为一个数据显示控件,填充在布局中。

    2.2.1 ListView的简单用法

        创建UseListViewActivity活动并生成activity_use_list_view.xml布局文件。修改布局文件中的代码,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

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

        布局文件中只是简单的添加了ListView控件,并为控件指定了一个id,同时将控件的宽度和高度都设置为match_parent,这样ListView就可以占满整个布局空间。

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

package com.fd.listviewtest;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

public class UseListViewActivity extends AppCompatActivity {
    String[] animal = {"Duck", "Pig", "Panda", "Fish", "Tiger", "Cat", "Dog", "Bird"};
   
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_use_list_view);

        ListView listView = (ListView) findViewById(R.id.lv);
        ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, animal);
       
        listView.setAdapter(adapter);
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {        
                Toast.makeText(UseListViewActivity.this, animal[position], Toast.LENGTH_SHORT).show();
            }
        });
    }

    
}

        上述代码同样是使用了ArrayAdapter适配器来将数据传递给ListView。同时使用setOnItemClickListener()方法为ListView注册了一个监听器,当用户点击了ListView中的任何一个子项时,就会回调onItemClick方法。在这个方法中可以通过position参数判断出用户点击的是哪一个子项,然后获取到相应信息。

        运行一下程序,并点击其中某一项,效果如下所示:

       可以看到,效果和上个例子一样。

    2.2.2 定制ListView界面

        只能显示一段文本的ListView实存太单调了,我们可以对ListView的界面进行定制,让其可以显示更加丰富的内容。下面我们要定制一个有图片有文字有选择框的ListView,怎么做呢?

        首先要准备好一组图片,分别对应上面提供的每一种动物。

        接下来定义一个实体类,作为ListView适配器的适配类型。新建类Animal,代码如下所示:

package com.fd.listviewtest;

/**
 * Created by Administrator on 2018-07-27.
 */

public class Animal {
    private String name;
    private int imageId;

    public Animal(String name, int imageId) {
        this.name = name;
        this.imageId = imageId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getImageId() {
        return imageId;
    }

    public void setImageId(int imageId) {
        this.imageId = imageId;
    }
}

        类中有两个字段,name表示动物名字,imageId表示动物对应的图片资源id。

        接着需要为ListView的子项指定一个我们自定义的布局,在layout目录下新建animal_item.xml文件,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ll_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/iv_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/tv_text"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="10dp"
        android:layout_weight="1" />

    <RadioGroup
        android:id="@+id/rg_group"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginEnd="10dp"
        android:orientation="horizontal">

        <RadioButton
            android:id="@+id/rb_select"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:checked="true"
            android:text="选中" />

        <RadioButton
            android:id="@+id/rb_deselect"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="取消" />
    </RadioGroup>
</LinearLayout>

        在这个布局中,我们定义了一个ImageView用于显示动物图片,又定义了一个TextView用于显示动物名称,最后定义了两个单选按钮用于表示选中和取消。

        接下来创建一个自定义的适配器,这个适配器继承自ArrayAdapter,并将泛型指定为Animal类。其代码如下:

package com.fd.listviewtest;

import android.content.Context;
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;

import java.util.List;


public class AnimalAdapter extends ArrayAdapter<Animal> {
    private int resourceId;
    private String radioText="选中";
    public AnimalAdapter(Context context, int resource, List<Animal> objects) {
        super(context, resource, objects);
        resourceId = resource;
    }

    @NonNull
    @Override
    public View getView(final int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        Animal animal = getItem(position);
        View view;
        view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
        LinearLayout ll_view = view.findViewById(R.id.ll_view);
        ImageView imageView = view.findViewById(R.id.iv_image);
        TextView  textView = view.findViewById(R.id.tv_text);
        RadioGroup rg_group = view.findViewById(R.id.rg_group);
        
        rg_group.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, @IdRes int checkedId) {
                switch (checkedId){
                    case R.id.rb_select:
                        radioText = "选中";
                        break;
                    case R.id.rb_deselect:
                        radioText = "取消";
                        break;                }
            }
        });
        ll_view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(getContext(), "你点击了第" + position + "项" + " ,你选择:" + radioText, Toast.LENGTH_SHORT).show();
            }
        });
        imageView.setImageResource(animal.getImageId());
        textView.setText(animal.getName());
        return view;
    }
}

        AnimalAdapter重写了父类的一组构造方法,用于将上下文、ListView子项布局id和数据传递进来。另外还重写了getView方法,这个方法在每个子项被滚动到屏幕内的时候会被调用。

        在getView方法中首先用LayoutInflater的inflate方法来为这个子项加载我们自定义的布局,然后用findViewById方法分别找到view内控件并对各个控件进行相应的处理,最后将这个view返回。

        下面修改UseListViewActivity的代码,如下所示:

package com.fd.listviewtest;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

public class UseListViewActivity extends AppCompatActivity {
    String[] animal = {"Duck", "Pig", "Panda", "Fish", "Tiger", "Cat", "Dog", "Bird"};
    private List<Animal> animalLists = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_use_list_view);

        InitAnimals();

        ListView listView = (ListView) findViewById(R.id.lv);
   
        AnimalAdapter adapter = new AnimalAdapter(UseListViewActivity.this, R.layout.animal_item, animalLists);
        listView.setAdapter(adapter);
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
                Animal animal = animalLists.get(position);
                Toast.makeText(UseListViewActivity.this, animal.getName(), Toast.LENGTH_SHORT).show();              
            }
        });
    }

    private void InitAnimals() {
        for (int i = 0; i < 2; i++) {
            Animal duck = new Animal("Duck", R.mipmap.duck);
            animalLists.add(duck);
            Animal pig = new Animal("Pig", R.mipmap.pig);
            animalLists.add(pig);
            Animal panda = new Animal("Panda", R.mipmap.panda);
            animalLists.add(panda);
            Animal fish = new Animal("Fish", R.mipmap.fish);
            animalLists.add(fish);
            Animal tiger = new Animal("Pear", R.mipmap.tiger);
            animalLists.add(tiger);
            Animal cat = new Animal("Grape", R.mipmap.cat);
            animalLists.add(cat);
            Animal dog = new Animal("Pineapple", R.mipmap.dog);
            animalLists.add(dog);
            Animal bird = new Animal("Strawberry", R.mipmap.bird);
            animalLists.add(bird);
        }
    }
}

        代码不难,就不作详细解释了。

        现在运行一下程序,效果如图所示:

    2.2.3 提升ListView的运行效率

        上面例子中ListView的运行效率是很低的,因为在AnimalAdapter的getView方法中,每次都将布局重新加载一遍,当ListView快速滚动的时候,这就有可能出现问题。

        getView方法中有一个convertView参数,这个参数用于将之前加载 好的布局进行缓存,以便之后可以进行重用。修改AnimalAdapter的代码,如下所示:

package com.fd.listviewtest;

import android.content.Context;
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;

import java.util.List;


public class AnimalAdapter extends ArrayAdapter<Animal> {
    private int resourceId;
    private String radioText="选中";
    public AnimalAdapter(Context context, int resource, List<Animal> objects) {
        super(context, resource, objects);
        resourceId = resource;
    }

   
    @NonNull
    @Override
    public View getView(final int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        Animal animal = getItem(position);
        View view;
        if (convertView == null) {
            view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
        }else{
            view = convertView;
        }
        LinearLayout ll_view = view.findViewById(R.id.ll_view);
        ImageView imageView = view.findViewById(R.id.iv_image);
        TextView  textView = view.findViewById(R.id.tv_text);
        RadioGroup rg_group = view.findViewById(R.id.rg_group);

        rg_group.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, @IdRes int checkedId) {
                switch (checkedId){
                    case R.id.rb_select:
                        radioText = "选中";
                        break;
                    case R.id.rb_deselect:
                        radioText = "取消";
                        break;                }
            }
        });
        ll_view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(getContext(), "你点击了第" + position + "项" + " ,你选择:" + radioText, Toast.LENGTH_SHORT).show();
            }
        });
        imageView.setImageResource(animal.getImageId());
        textView.setText(animal.getName());
        return view;
    }
}

        在getView方法中,我们对contentView进行了判断,如果其为null,就使用LayoutInflater去加载布局,如果不为null,就直接对contentView进行重用。这样就大大提高了ListView的运行效率,在快速滚动的时候也可以表现出更好的性能。

        虽然上述代码已经不会再去重复加载布局,但是每次在getView方法中还是会调用View的findViewById方法来获取一次控制实例。因此,我们可以使用一个ViewHolder来对这部分内容进行优化,修改AnimalAdapter的代码,如下所示:

package com.fd.listviewtest;

import android.content.Context;
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;

import java.util.List;


public class AnimalAdapter extends ArrayAdapter<Animal> {
    private int resourceId;
    private String radioText="选中";
    public AnimalAdapter(Context context, int resource, List<Animal> objects) {
        super(context, resource, objects);
        resourceId = resource;
    }

    @NonNull
    @Override
    public View getView(final int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        Animal animal = getItem(position);
        View view;
        ViewHolder viewHolder;
        if (convertView == null) {
            view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
            viewHolder = new ViewHolder();
            viewHolder.ll_view = view.findViewById(R.id.ll_view);
            viewHolder.imageView = view.findViewById(R.id.iv_image);
            viewHolder.textView = view.findViewById(R.id.tv_text);
            viewHolder.rg_group = view.findViewById(R.id.rg_group);
            view.setTag(viewHolder);
        } else {
            view = convertView;
            viewHolder = (ViewHolder) view.getTag();
        }
        viewHolder.rg_group.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, @IdRes int checkedId) {
                switch (checkedId){
                    case R.id.rb_select:
                        radioText = "选中";
                        break;
                    case R.id.rb_deselect:
                        radioText = "取消";
                        break;                }
            }
        });
        viewHolder.ll_view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(getContext(), "你点击了第" + position + "项" + " ,你选择:" + radioText, Toast.LENGTH_SHORT).show();
            }
        });
        viewHolder.imageView.setImageResource(animal.getImageId());
        viewHolder.textView.setText(animal.getName());
        return view;
    }      

    class ViewHolder {
        LinearLayout ll_view;
        ImageView imageView;
        TextView textView;
        RadioGroup rg_group;
    }
}

        上述代码中,我们新增了一个内部类ViewHolder,用于对控件的实例进行缓存。当contentView为null的时候,创建 一个ViewHolder对象,并将控件的实例存放在ViewHolder中,然后调用setTag方法将ViewHolder对象存储在View中。当contentView不为null的时候,通过getTag方法将ViewHolder取出来。

3.代码下载地址

https://github.com/streate/ListViewTest

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

单片机开发实例

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值