Android简单列表控件ListView详细解析

郝池暝
2023-12-01

前言

本系列将针对性的介绍Android开发的绝大部分控件,但介于作者水平有限,故对深层原理无法做出精准剖析,只能对常见用法总结概括,方便大家快速上手~

采用Java语言,后期会逐渐过渡到Kotlin


一个完整的ListView主要构成部分

一般的,无论是目前的ListView还是后续的GridView,都会按照如下三大结构构成,我们可以把适配器看成是一个特殊的,协调数据与渲染的中间件!

  1. ListViewActivity 主页面
  2. ListViewAdapter 适配器(一般都会把适配器分离出来作为单个文件,不会将其写到主页面里面去)
  3. ListContainer 数据存储类(构成listview的所有数据都会存储在这里)

ListContainer数据存储类编写

当然,如果数据量很少的话,也可以省略该类的编写,直接将数据搬到适配器类里面也是可以的!
当前仍然以一个独立的数据存储类作为例子;

代码解释:
我们需要展示的列表每一行都由一个图标和一行字组成,所以要传入数目相同的数据,下方代码中我们传入了6组图标和文字;
在构造函数中,每次都指定一组;
getListContainer方法让我们获得一个数据列表,列表中存储6个ListContainer对象,而每个对象又正好包含了一对数据,符合我们的预期条件!

public class ListContainer {

    /*需要两种数据,一个是图片,另一个是字符串内容*/
    public int img;
    public String content;

    /*在构造函数里面指定当前的img和content是那个*/
    public ListContainer(int img, String content) {
        this.img = img;
        this.content = content;
    }

    private static int[] imgArr = {
            R.drawable.download_cloud,
            R.drawable.edit,
            R.drawable.menu,
            R.drawable.folder,
            R.drawable.refresh,
            R.drawable.share
    };

    private static String[] contentArr = {
            "云下载", "编辑文件", "打开菜单", "新建文件夹", "禁止刷新", "分享"
    };

    /*使用该方法,来获得一个表,里面存储了所有的img和content*/
    @NonNull
    public static List<ListContainer> getListContainer() {
        List<ListContainer> listContainers = new ArrayList<>();
        for (int i = 0; i < imgArr.length; i++) {
            listContainers.add(new ListContainer(imgArr[i], contentArr[i]));
        }
        return listContainers;
    }
}

ListViewAdapter适配器

因为listview是根据指定数据进行迭代得出的,所以我们仍然需要新建一个layout用来表示列表中的一行的格式,最终适配器向该layout中的组件填入相关数据并迭代增加得出最终国的listview;
下面为item_listview.xml的基本代码:

以下layout表示一行中右侧为ImageView,左侧为文本,占据的比例为1:3
注意别忘了为这两个组件添加id,后续填入数据时需要使用到!

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

    <ImageView
        android:id="@+id/iv_listview"
        android:layout_weight="1"
        android:layout_width="0dp"
        android:layout_height="60dp"
        android:scaleType="fitCenter"/>

    <TextView
        android:id="@+id/tv_listview"
        android:layout_weight="3"
        android:layout_width="0dp"
        android:layout_height="60dp"
        android:textSize="22sp"
        android:text="helloworld"
        android:gravity="center"
        android:textColor="@color/black"/>

</LinearLayout>

然后回到ListViewAdapter.java文件内,设置我们的适配器;

  1. 先声明两个变量:上下文ctx和数据包list,然后于构造函数中初始化他们
  2. 我们让适配器继承BaseAdapter接口,并实现对应的四个方法(鼠标点击BaseAdapter后按下alt enter组合键,弹出窗口点击Implement即可!)
  3. 依次且必须全部填入三个方法:getCount getItem getItemId
  4. 然后创建私有内部类ListViewHolder,他是为了当listview过长时,在上下滑动列表时不需要每次都实例化每一行的对象,而是存储起来复用;
    ListViewHolder内部有两个变量,分别对应item_listview的两个控件
  5. getView渲染列表
    convertView == null 判断当前view是空的,即执行初始化;
    否则获取到之前存储好的内容,避免二次构造对象;
  6. LayoutInflater.from(ctx).inflate(R.layout.item_listview, null) 通过一个xml文件构造一个view对象,这里需要提供上下文context和对应的layout文件名;
public class ListViewAdapter extends BaseAdapter {

    private final Context ctx;
    private List<ListContainer> list;

    public ListViewAdapter(Context ctx, List<ListContainer> list) {
        this.ctx = ctx;
        this.list = list;
    }

    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public Object getItem(int position) {
        return list.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ListViewHolder vh;
        if (convertView == null) {
            convertView = LayoutInflater.from(ctx).inflate(R.layout.item_listview, null);
            vh = new ListViewHolder();
            vh.img = convertView.findViewById(R.id.iv_listview);
            vh.content = convertView.findViewById(R.id.tv_listview);

            convertView.setTag(vh);
        }else {
            vh = (ListViewHolder) convertView.getTag();
        }

        ListContainer listContainer = list.get(position);
        vh.img.setImageResource(listContainer.img);
        vh.content.setText(listContainer.content);

        return convertView;
    }

    private final class ListViewHolder {
        public ImageView img;
        public TextView content;
    }
}

ListViewActivity主页面编写

首先我们需要配置到主页面的xml文件,这里让整个主页面都是一个listview
你应该注意到listview控件的layout_height=“wrap_content”,因为他有很多行,所以要适配内容才对

<?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_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>

主页面的java文件
依次配置三大件:ListView ListContainer ListViewAdapter即可;
对listview对象使用setAdapter指定适配器;
setOnItemClickListener可以设置监听点击到哪一行,他指向一个方法onItemClick,里面的position表示目前的行索引,根据他我们可以做出对应的操作;

多练习,多巩固,别老看着不动!!!不做的话永远都复杂的要死!!!

public class ListViewActivity extends AppCompatActivity implements AdapterView.OnItemClickListener {

    private ListView listView;
    private List<ListContainer> listContainer;
    private ListViewAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_list_view);
        listView = findViewById(R.id.lv_content);

        listContainer = ListContainer.getListContainer();
        adapter = new ListViewAdapter(this, listContainer);

        listView.setAdapter(adapter);
        listView.setOnItemClickListener(this);
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        Toast.makeText(this, listContainer.get(position).content, Toast.LENGTH_SHORT).show();
    }
}
 类似资料: