关闭

android开发——ListView的使用

467人阅读 评论(0) 收藏 举报

 ListView是一种如今比较常见的组件,是用来显示多个可滑动项(Item)列表的的ViewGroup。它的优点在于可以使用列表的形式来展示内容,超出屏幕部分的内容只需要通过手指滑动就可以移动到屏幕内了。即使在ListView中加载非常非常多的数据,都不会发生崩溃,而且随着我们手指滑动来浏览更多数据时,程序所占用的内存竟然都不会跟着增长。其他关于ListView的一些基础知识可参考郭神的文章从源码角度分析ListView。在这里我们只通过一些的demo来让大家了解如何使用这么一款强大的原生控件。

 一个ListView的创建需要三个元素:

  • 每一列Item的View
  • 填入View的数据或图片等
  • 连接ListView和数据的适配器Adapter

 我们首先把布局工作完成。ListView的布局其实并不复杂,可以理解为有两层布局。第一是整体布局,第二是每个Item的布局。我们首先创建整体的布局:activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.listviewdemo.MainActivity">

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

</LinearLayout>

创建完该xml布局后我们可以发现预览中已经出现了我们想要的ListView的效果:

                                                   

 这也是系统的布局,但目前我们只需显示一行String,所以最终不考虑这样的布局。当然我们自定义这样的布局也不困难。

 那么接下来再为我们的Item创建布局:item.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">

    <TextView
        android:id="@+id/tx_view"
        android:gravity="center"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="1"
        android:textSize="36dp"/>
    
</LinearLayout>
 这里我们先简单的只在Item中添加一个TextView。text设置成什么并没有影响,之后会被绑定的数据所替换。

 这样每一列Item的View就完成了。接着需要把数据写入到Item的TextView中并展现出来。在这之前我们需要一个连接ListView和数据的适配器Adapter,可以先自定义一个适配器MyAdapter:

public class MyAdapter extends ArrayAdapter{

    private int resourceId;
    private ArrayList<String> mData;

    public MyAdapter(Context context, int textViewResourceId, List objects){
        super(context,textViewResourceId,objects);
        resourceId = textViewResourceId;
        mData = (ArrayList<String>)objects;
    }

    public View getView(int position,View convertView,ViewGroup parent){
        View view = LayoutInflater.from(getContext()).inflate(resourceId,null);
        TextView tx = view.findViewById(R.id.tx_view);
        tx.setText(mData.get(position));
        return view;
    }
}

 可能有人不了解View view = LayoutInflater.from(getContext()).inflate(resourceId,null);的作用是啥,那咱们来拆分一下。

        //加载布局管理器
        LayoutInflater inflater = LayoutInflater.from(context);
        //将xml布局转换为view对象
        convertView = inflater.inflate();
        //利用view对象找到布局中的组件
        convertView.findViewById();

 因为在TextView中我只简单的添加字符串,因此我把要接受(适配)的数据规定为String类型。如果你想要实现Item中有很多控件(如按钮、图片、文字等)可以先自定义一个类,然后再规定适配器所要适配的数据类型(下面会有实例)。getView方法我们可以把它认为是把数据绑定View并返回,即之前所说的填入View的数据或图片等。在getView中我们通过每个Item的位置position来确定他所要加载的数据。

 最后我们需要完成MainActivity并写入我们所要呈现的数据,设置ListView与我们的适配器相关联。MainActivity:

public class MainActivity extends Activity {

    private ListView listView;
    private MyAdapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        listView = findViewById(R.id.list_view);
        mAdapter = new MyAdapter(MainActivity.this,R.layout.item,getData());
        listView.setAdapter(mAdapter);
    }

    private ArrayList<String> getData(){
        ArrayList<String> mData = new ArrayList<>();
        String temp = "item";
        for(int i=1;i<41;i++)
            mData.add(temp + i);
        return mData;
    }
}
 核心代码就是定义适配器并setAdapter。这里MyAdapter()传入的三个数据分别为:相关上下文context、子Item的布局、所要适配的数据。最后效果:(这里由于录屏软件的问题并没有把所有Item都录进去)

                                             

 上面应该能说最简单的ListView实现了吧。接下来稍微增加一点难度,Item里面实现多个组件。那么就需要定义一个类来“容纳它们”,并设置它为适配器类型,同时需要改变Item的布局。

 每个Item中包含学生的照片,学号和姓名,定义类MyClass:

public class MyClass{

    private String name;
    private String id;
    private int imageId;

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

    public String getName(){
        return name;
    }

    public String getId(){
        return id;
    }

    public int getImageId(){
        return imageId;
    }
}
 同时修改我们的布局。此时如果继续采用LinearLayout布局可能会出现嵌套,所以我们采用相对布局RelativeLayout:

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

    <ImageView
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_alignParentTop="true"
        android:layout_alignParentBottom="true"
        android:adjustViewBounds="true"
        android:padding="2dp" />

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@+id/image"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:gravity="center_vertical"
        android:layout_marginTop="10dp"
        android:textSize="25dp"/>

    <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_vertical"
        android:layout_toRightOf="@id/image"
        android:layout_alignParentRight="true"
        android:layout_alignParentBottom="true"
        android:layout_below="@id/title"
        android:textSize="40dp"/>

</RelativeLayout>
 这里通过layout_alignParent来设置控件相对于父容器的位置。通过layout_toRightOf和layout_below来确定相互之间的位置关系。这里需要注意如何设置第一个TextView layout_above第二个TextView,最后第一个TextView可能会显示不出来。具体原因目前也不太清楚,按照网上的方法试验下来也不起效果,如果哪位同学知道原因可直接在评论区留言。

 布局完成之后我们就需要自定义一个类来“包含”这些内容。MyClass:

public class MyClass{

    private String name;
    private String id;
    private int imageId;

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

    public String getName(){
        return name;
    }

    public String getStId(){
        return id;
    }

    public int getImageId(){
        return imageId;
    }
}
 比较简单,就定义了三个值。三个返回函数也是为了等会能在getView方法中绑定数据使用。

 接着我们需要改变我们的适配器所适配的数据对象类型,注意接收对象改为MyClass类型。MyAdapter:

public class MyAdapter extends ArrayAdapter<MyClass>{

    private int resourceId;

    public MyAdapter(Context context, int textViewResourceId, List<MyClass> objects){
        super(context,textViewResourceId,objects);
        resourceId = textViewResourceId;
    }

    public View getView(int position,View converView,ViewGroup parent){
        MyClass stu = getItem(position);  //直接通过getItem方法获取当前实例
        View view = LayoutInflater.from(getContext()).inflate(resourceId,null);
        TextView title = view.findViewById(R.id.title);
        TextView text = view.findViewById(R.id.text);
        ImageView image = view.findViewById(R.id.image);
        title.setText(stu.getStId());
        text.setText(stu.getName());
        image.setImageResource(stu.getImageId());
        return view;
    }
}

 最后在MainActivity中同步修改接受数据的类型。MainAcitvity:

public class MainActivity extends Activity {

    private ListView listView;
    private MyAdapter mAdapter;
    private String[] names = {"曹一","孙二","张三","李四","王五","赵六","广七","刘八","夏九","付十"};
    private String[] ids = {"10001","10002","10003","10004","10005","10006","10007","10008","10009","10010"};
    private MyClass[] students = new MyClass[10];
    private List<MyClass> list = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        listView = findViewById(R.id.list_view);
        for (int i=0;i<10;i++){
            students[i] = new MyClass(names[i],ids[i],R.drawable.ic_launcher_background);
            list.add(students[i]);
        }
        mAdapter = new MyAdapter(MainActivity.this,R.layout.item,list);//最后的数据类型发生改变
        listView.setAdapter(mAdapter);
    }
}
 放上最后的效果图(这里偷懒我就直接用了系统自带的背景图啦~):

                                         

 那么稍复杂的ListView我们也就完成啦。同理我们可以继续添加其他如Button等的控件来实现需求。

ListView基本使用相信大家都已掌握,接下来咱们将进一步学习ListView的优化以及它的点击事件处理。

 相信大家都已经注意到了,在适配器的getView中我们每次都需要通过.inflate方法来加载一个布局,即使是已加载过但被划出屏幕的布局也需重新加载,这显然是十分不合理的。细看该方法,发现有个参数我们从来没有使用过,对就是convertView,简单来说他的作用是缓存了ListView中已经加载好的View。这样就可以用它来优化加载布局问题。修改getView方法:

public View getView(int position,View convertView,ViewGroup parent){
        MyClass stu = getItem(position);  
        View view;
        if(convertView==null)   //如果未加载过,则加载。否则直接使用convertView对象
            view = LayoutInflater.from(getContext()).inflate(resourceId,null);
        else
            view = convertView;
        TextView title = view.findViewById(R.id.title);
        TextView text = view.findViewById(R.id.text);
        ImageView image = view.findViewById(R.id.image);
        title.setText(stu.getStId());
        text.setText(stu.getName());
        image.setImageResource(stu.getImageId());
        return view;
    }
 同时我们发现每次我们都需要调用findViewById方法来获取一次控件的代码,这同样也是不合理的。那是否有办法可以对此进行优化呢?答案是有。我们可以新增内部类ViewHolder来缓存控件的实例。convertView为空时,会将控件的实例存放在ViewHolder里,然后用setTag方法将ViewHolder对象存储在view里。convertView不为空时,用getTag方法获取viewHolder对象。我们再次修改getView,并增加内部类ViewHolder:

public View getView(int position,View convertView,ViewGroup parent){
        MyClass stu = getItem(position); 
        View view;
        ViewHolder viewHolder;
        if(convertView==null){
            view = LayoutInflater.from(getContext()).inflate(resourceId,null);
            viewHolder = new ViewHolder();
            viewHolder.image = view.findViewById(R.id.image);
            viewHolder.text = view.findViewById(R.id.text);
            viewHolder.title = view.findViewById(R.id.title);
            view.setTag(viewHolder);
        }
        else {
            view = convertView;
            viewHolder = (ViewHolder) view.getTag();
        }
        viewHolder.title.setText(stu.getStId());
        viewHolder.text.setText(stu.getName());
        viewHolder.image.setImageResource(stu.getImageId());
        return view;
    }

    class ViewHolder{
        TextView title;
        TextView text;
        ImageView image;
    }

  • 判断convertView是否为空优化加载布局
  • 设置ViewHolder优化加载控件


 这样ListView的优化工作也就完成了。最后我们来说一下ListView中点击Item的事件处理。和普通的监听事件相类似,我们只需重写onItemClick方法即可,这里我们就直接贴出代码:

listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                MyClass stud = list.get(position);
                Toast.makeText(MainActivity.this,"你点击了学号为"+stud.getStId()+"的"+stud.getName()+"同学",Toast.LENGTH_SHORT).show();
            }
        });
 最后效果图(最后的鼠标点击gif上看的可能有点问题但是实际操作是OK的):

                                             

 到这里ListView我们算是基本都了解了一遍,之后我们将会开始了解RecyclerView。希望大家都有所收获一起进步。


0
0
查看评论

Android开发从入门到放弃(8)使用ListView显示一个简单的列表

本篇博客简单介绍一下Android开发中ListView的使用,并分别实现一个简单列表和复杂列表。ListView是用来显示一个列表的。在Android中,显示一个列表是比较容易的,我总结了下,只需三步 一个待显示的数据列表,可以是简单类型,也可以是自定义类型, 一个用于展示每一个数据项的模板,...
  • daguanjia11
  • daguanjia11
  • 2016-12-28 10:29
  • 991

Android listView用法详解

1. 前言 在android开发中ListView是比较常用的组件,它以列表的形式展示具体内容,并且能够根据数据的长度自适应显示。 列表的显示需要三个元素: 1.ListVeiw:用来展示列表的View。 2.适配器:用来把数据映射到ListView上的中介。 3.数据:被映射的字符串,...
  • zhaokx3
  • zhaokx3
  • 2016-10-21 14:51
  • 969

ListView (加载、删除) 动画

由于图片大小超过2M,所以无法上传动态效果,好坑爹!!!你可以自己学习后,运行一下,自己去预览效果吧!!!import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.o...
  • LGGisKing
  • LGGisKing
  • 2015-10-27 22:27
  • 1182

Android开发之ListView条目批量选择删除

ListView实现的列表,如果是可编辑,可删除的,一般都要提供批量删除功能,否则的话,一项一项的删除体验很不好,也给用户带来了很大的麻烦。 实现效果图 具体实现代码 select.xml 主布局文件包含一个ListView还有一个隐藏的布局,包含了两个Button一个TextView,...
  • MiniMicall
  • MiniMicall
  • 2014-09-17 10:46
  • 3350

Android开发-ListView的使用

ListView在android程序中比较常用,在此做一下简单的总结 内容:自定义ListView, 自定义Adapter,ListView滚动事件的应用!   项目结构如下图   ListView的使用: 建立一个listView的布局文件,命名为:item_l...
  • huangyuhuangyu
  • huangyuhuangyu
  • 2016-08-19 16:56
  • 470

Android UI开发: 横向ListView(HorizontalListView)完整实现 (附源码下载)

横向ListView(HorizontalListView)完整实现例子: 先上效果图: 注: 1、这个横向Listview的高度,如果你设成wrap_cotent那么将会占据整个屏幕,即使你将它适配器里的view的高度限制死,限制成很小,这个HorizontalListView的...
  • u012604745
  • u012604745
  • 2015-11-13 16:25
  • 718

Android之ListView包含RadioButton的单选问题解决办法

1.最简洁的解决办法。 get
  • FastThinking
  • FastThinking
  • 2014-11-11 17:48
  • 10809

Android自定义控件——ListView的下拉刷新与上拉加载

无疑,在Android开发中,ListView是使用非常频繁的控件之一,ListView提供一个列表的容易,允许我们以列表的形式将数据展示到界面上,但是Google给我们提供的原生ListView的控件,虽然在功能上很强大,但是在用户体验和动态效果上,还是比较差劲的。为了改善用户体验,市面上纷纷出现...
  • lee_tianya
  • lee_tianya
  • 2014-10-10 20:14
  • 17290

[Android基础]Android中ListView详解

ListView是手机系统中非常常用的一个组件,以垂直列表的形式显示所有列表项,今天我们来探索一下吧。 首先我们来了解一下ListView的基本属性吧。XML属性 andorid:divider:设置List列表项的分隔条(既可用颜色分隔,也可用Drawable分隔) android:di...
  • CodeEmperor
  • CodeEmperor
  • 2015-08-05 15:19
  • 1590

Android开发之ListView Adapter 模板

如果不对 Adapter 的写法进行规范,开发人员还是会根据自己的习惯,写出各种各样的 Adapter,如: - 很多开发人员都喜欢将 Adapter 内嵌在 Activity 中,一般会使用 SimpleAdapter。 - 由于没有使用实体,所以一般会把一个字典作为构造函数的参数注入到 ...
  • duoduo_11011
  • duoduo_11011
  • 2017-06-13 16:07
  • 316
    个人资料
    • 访问:523次
    • 积分:30
    • 等级:
    • 排名:千里之外
    • 原创:2篇
    • 转载:1篇
    • 译文:0篇
    • 评论:0条
    文章存档