ListView简单使用

ListView简单使用

ListView以列表的形式展示数据内容,并且能够根据列表的高度自适应屏幕展示。

属性名称功能描述
android:listSelector当条目被点击后,改变条目的背景颜色
android:divider设置分割线颜色
android:dividerHeight设置分割线的高度
android:scrollbars是否显示滚动条
android:fadingEdge去掉上边和下边的黑色阴影

常见数据适配器(Adapter)

  • 数据适配器是数据与视图之间的桥梁,它类似于一个转换器,将复杂数据转换成用户可以接受的方式进行呈现。
  • 常见的数据适配器
    • BaseAdapter
    • SimpleAdapter
    • ArrayAdapter

1.BaseAdapter

  • BaseAdapter顾名思义是基础的适配器。他实际上是一个抽象类,通常在自定义适配器时会继承BaseAdapter
  • BaseAdapter中的方法
方法名称功能描述
public int getCount()获取Iten条目的总数
public Object getItem(int position)根据position(位置)获取某个Item的对象
public long getItemId(int position)根据position(位置)获取某个Item的id
public View getView(int position, View convertView, ViewGroup parent)获取相应position对应的Item视图,position是当前Item的位置,convertView用于复用旧视图,parent用于加载XML布局。

创建一个用于加载数据的布局文件

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

    <TextView
            android:text="TextView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/textView"/>
</LinearLayout>

创建一个TestAdapter类继承BaseAdapter

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

public class TestAdapter extends BaseAdapter {
    private String[] data;
    private Context myContext;

    public TestAdapter(String[] data, Context context) {
        this.data = data;
        this.myContext = context;
    }

    @Override
    public int getCount() {
        return data.length;
    }

    @Override
    public Object getItem(int position) {
        return null;
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        convertView = LayoutInflater.from(myContext).inflate(R.layout.item_list, parent, false);
        TextView tv = (TextView) convertView.findViewById(R.id.textView);
        tv.setText(data[position]);
        return convertView;
    }
}

绑定到MainActivity

ListView lv = findViewById(R.id.lv);
String[] data = {"列表1", "列表2", "列表3", "列表4", "列表5"};
lv.setAdapter(new TestAdapter(data, this));
效果图

BaseAdapter优化

我们从上面代码 中可以看出比较重要的两个方法:getCount()getView(),界面上有多少列就会调用多少次getView, 这个时候可能看出一些端倪,每次都是新inflate一个View,都要进行这个XML的解析,这样会 很浪费资源,当然,几十列或者几百列的列表并不能体现什么问题,但假如更多或者布局更加复杂? 所以学习ListView的优化很重要,而本节针对的是BaseAdapter的优化,优化的两点有,复用convertView 以及使用ViewHolder重用组件,不用每次都findViewById

1.复用ConvertView

上面也说了,界面上有多少个Item,那么getView()方法就会被调用多少次! 我们来看看上面我们写的getView()部分的代码。

@Override
    public View getView(int position, View convertView, ViewGroup parent) {
        convertView = LayoutInflater.from(myContext).inflate(R.layout.item_list, parent, false);
        TextView tv = (TextView) convertView.findViewById(R.id.textView);
        tv.setText(data[position]);
        return convertView;
    }

inflate()每次都要加载一次xml,其实这个convertView是系统提供给我们的可供服用的View 的缓存对象,我们只需要加个判断条件就好。

@Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            convertView = LayoutInflater.from(myContext).inflate(R.layout.item_list, parent, false);
        }
        TextView tv = (TextView) convertView.findViewById(R.id.textView);
        tv.setText(data[position]);
        return convertView;
    }

2.ViewHolder重用组件

getView()会被调用多次,那么findViewById不一样得调用多次,而我们的ListViewItem 一般都是一样的布局,我们可以对这里在优化下,我们可以自己定义一个ViewHolder类来对这一部分 进行性能优化!

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

public class TestAdapter extends BaseAdapter {
    private String[] data;
    private Context myContext;

    static class ViewHolder {
        TextView tv;
    }

    public TestAdapter(String[] data, Context context) {
        this.data = data;
        this.myContext = context;
    }

    @Override
    public int getCount() {
        return data.length;
    }

    @Override
    public Object getItem(int position) {
        return null;
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;
        if (convertView == null) {
            convertView = LayoutInflater.from(myContext).inflate(R.layout.item_list, parent, false);
            holder = new ViewHolder();
            holder.tv = (TextView) convertView.findViewById(R.id.textView);
            // 将Holder存储到convertView中
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        holder.tv.setText(data[position]);
        return convertView;
    }
}

以后BaseAdapter照着这个模板写就对了,另外这个修饰ViewHolderstatic,关于是否定义成静态,跟里面的对象数目是没有关系的,加静态是为了在多个地方使用这个 Holder的时候,类只需加载一次,如果只是使用了一次,加不加也没所谓!

2.SimpleAdapter

SimlpeAdapter继承自BaseAdapter,实现了BaseAdapter的四个抽象方法并进行封装。因此在使用SimpleAdapter进行数据适配时,只需要在结构中穿入相应的参数即可。SimpeAdapter的构造方法的具体信息如下:

public SimpleAdpter(Context context, List<?extends Map<String,?>> data, int resource, String[] from, int[] to);
public SimpleAdpter(上下文对象, List<?extends Map<String,?>> 数据集合, int item布局的资源id, String[] Map集合中的key值, int[] item布局对应的控件);

创建一个用于加载数据的布局文件

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

    <TextView
            android:text="TextView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/textView"/>
</LinearLayout>

把SimpleAdapter适配器加载ListView控件中

import android.widget.ListView;
import android.widget.SimpleAdapter;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MainActivity extends AppCompatActivity {

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

        List<Map<String, Object>> MapList = new ArrayList<Map<String, Object>>();

        for (int i = 0; i < 10; i++) {
            Map<String, Object> data = new HashMap<String, Object>();
            data.put("content", "内容" + i);
            MapList.add(data);
        }

        ListView lv = findViewById(R.id.lv);
        lv.setAdapter(new SimpleAdapter(MainActivity.this, MapList, R.layout.item_list, new String[]{"content"}, new int[]{R.id.textView}));
    }
}
效果图

3.ArrayAdapter

  • ArrayAdapter也是BaseAdapt的子类,用法于SimpleAdapter类似,开发者只需要在构造方法里面传入相应参数即可。ArrayAdapter通常用于适配器TextView控件。
  • 构造方法如下:
// context 上下文对象
// resource item布局的资源id
public ArrayAdapter(Context context, int resource);
// textViewResourceId item布局中相应TextView的id
public ArrayAdapter(Context context, int resource, int textViewResourceId);
// T[] objects 需要适配的数组类型的数据
public ArrayAdapter(Context context, int resource, T[] objects);
public ArrayAdapter(Context context, int resource, int textViewResourceId, T[] objects);
public ArrayAdapter(Context context, int resource, List<T> objects);
// List<T> objects 需要适配的List类型的数据
public ArrayAdapter(Context context, int resource, int textViewResourceId, List<T> objects);

创建一个用于加载数据的布局文件

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

    <TextView
            android:text="TextView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/textView"/>
</LinearLayout>

把ArrayAdapter适配器加载ListView控件中

import android.widget.ArrayAdapter;
import android.widget.ListView;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;


public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        ListView lv = findViewById(R.id.lv);
        lv.setAdapter(new ArrayAdapter<String>(MainActivity.this,R.layout.item_list,R.id.textView,new String[]{"内容1","内容2","内容3","内容4","内容5"}));
        
    }
}
效果图

ins:rinomaru_