Android基础之ListView | Adapter | MVC | 带源码

ListView

  • 在讲ListView的使用之前,需要了解Adapter适配器,也顺便讲一下MVC的原理以及Adapter在其中的作用

MVC

  • 图解

View:用户操作接口,GUI,可以想象成用户看到的界面

Model:事务逻辑层,负责执行程序的核心运算和逻辑判断,通过view来获取用户输入的数据,然后根据数据库查询相关操作,将结果返回给view展示

Controller:控制器,view与model之间的枢纽,通过控制程序的执行流程来进行view和model的交互

Adapter

  • 在android中就是充当controller的作用
  • 连接view和model的桥梁
  • 图解

  • 表格说明

BaseAdapter:抽象类,实际开发中我们一般继承该类并重写相关的方法

ArrayAdapter:支持泛型操作,只能展示一行文字

SimpleAdapter:具有良好扩展性的简单Adapter,可以自定义多种效果

SimpleCursorAdapter:一般在数据库会用到,用来显示简单文本类型的listView(textview或imageView等)

简单的ListView实现

<ListView
        android:id="@+id/list_view"
        android:layout_width="409dp"
        android:layout_height="729dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
private String[] data = {"Apple","Banana","Orange","Watermelon","Pear",
            "Grape","Pineapple","Strawberry","Cherry","Mango","Apple","Banana",
            "Orange","Watermelon","Pear", "Grape","Pineapple","Strawberry","Cherry","Mango"};

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

    ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,data);
    ListView listView = findViewById(R.id.list_view);
    listView.setAdapter(adapter);
}

ArrayAdapter有多个构造函数的重载,需要三个参数(当前上下文/ListView子项布局的id/要适配的数据)

android.R.layout.simple_list_item_1是一个Android内置的布局文件,里面只有一个TextView,可用于简单的显示一段文本

就相当于ListView每一行就是一个TextView

实例(定制ListView界面)

  • 定义一个实体类,作为ListView适配器的适配类型
public class Fruit {

    private String name;
    private int imageId;

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

    public String getName() {
        return name;
    }

    public int getImageId() {
        return imageId;
    }
}
  • 为ListView的子项定义一个布局fruit_item.xml
<ImageView
        	android:id="@+id/image_fruit"
        	android:layout_width="64dp"
        	android:layout_height="45dp"
        	app:layout_constraintBottom_toBottomOf="parent"
        	app:layout_constraintStart_toStartOf="parent"
        	app:layout_constraintTop_toTopOf="parent"
        	android:contentDescription="@string/image_fruit_description" />

<TextView
       		android:id="@+id/text_fruit_name"
        	android:layout_width="348dp"
        	android:layout_height="45dp"
        	app:layout_constraintBottom_toBottomOf="parent"
       		app:layout_constraintEnd_toEndOf="parent"
        	app:layout_constraintHorizontal_bias="0.0"
        	app:layout_constraintStart_toEndOf="@+id/image_fruit"
        	app:layout_constraintTop_toTopOf="parent" />
  • 创建一个适配器FruitAdapter,继承自ArrayAdapter

重写了父类的一组构造函数,用于将上下文、ListView的子项布局id和数据都传递进来

重写了getView()方法,每个子项被滚动到屏幕内的时候会被调用

在此方法中,首先通过getItem()方法得到当前项的Fruit实例,然后使用LayoutInflater来为子项加载我们传入的布局

LayoutInflater的inflate()有三个参数,分别是子项布局id,父布局,false(表示只让我们在父布局中声明的layout属性生效,但不会为这个view添加父布局。因为一旦view有了父布局,就不能被添加进ListView中了)

public class FruitAdapter extends ArrayAdapter<Fruit> {

    private int resourceId;

    public FruitAdapter(@NonNull Context context, int resourceId, @NonNull List<Fruit> objects) {
        super(context, resourceId, objects);
        this.resourceId = resourceId;
    }

    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {

        //获取当前项的fruit实例
        Fruit fruit = getItem(position);
        View view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
        ImageView imageView = view.findViewById(R.id.image_fruit);
        TextView textView = view.findViewById(R.id.text_fruit_name);

        imageView.setImageResource(fruit.getImageId());
        textView.setText(fruit.getName());

        return view;

    }
}
  • 修改MainActivity

initFruits()用于初始化所有的水果数据

在onCreate()里创建FruitAdapter对象,并将FruitAdapter作为适配器传递给listView

private List<Fruit> fruitList = new ArrayList<>();

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

    initFruits();
    FruitAdapter adapter = new FruitAdapter(this,R.layout.fruit_item,fruitList);

    ListView listView = findViewById(R.id.list_view);
    listView.setAdapter(adapter);

}

private void initFruits(){

    for (int i = 0; i < 2; i++){

        Fruit apple = new Fruit("Apple",R.drawable.apple);
        fruitList.add(apple);

        Fruit banana = new Fruit("Banana",R.drawable.banana);
        fruitList.add(banana);

        Fruit orange = new Fruit("Orange",R.drawable.orange);
        fruitList.add(orange);

        Fruit watermelon = new Fruit("Watermelon",R.drawable.watermelon);
        fruitList.add(watermelon);

        Fruit pear = new Fruit("Pear",R.drawable.pear);
        fruitList.add(pear);

        Fruit grape = new Fruit("Grape",R.drawable.grape);
        fruitList.add(grape);

        Fruit pineapple = new Fruit("Pineapple",R.drawable.pineapple);
        fruitList.add(pineapple);

        Fruit strawberry = new Fruit("Strawberry",R.drawable.strawberry);
        fruitList.add(strawberry);

        Fruit cherry = new Fruit("Cherry",R.drawable.cherry);
        fruitList.add(cherry);

        Fruit mango = new Fruit("Mango",R.drawable.mango);
        fruitList.add(mango);

    }

}
  • 提升ListView运行效率

在FruitAdapter里,getView()方法每次都将布局重新加载,当List View快速滚动时,会成为性能的瓶颈

convertView参数用于将之前加载好的布局进行缓存,以便之后可以进行重用

public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {

    //获取当前项的fruit实例
    Fruit fruit = getItem(position);

    View view;
    if(convertView == null){
        view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
    }else {
        view = convertView;
    }

    ImageView imageView = view.findViewById(R.id.image_fruit);
    TextView textView = view.findViewById(R.id.text_fruit_name);

    imageView.setImageResource(fruit.getImageId());
    textView.setText(fruit.getName());

    return view;

}
  • 继续更好的优化

前一个虽然不会重复去加载布局,但是每次还是会重复调用View的findViewById()方法来获取一次控件的实例

我们可以借助ViewHolder(内部类)来对此进行优化

ViewHolder用于对控件的实例进行缓存

view的setTag方法,将viewHolder对象存储在view中

@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {

    //获取当前项的fruit实例
    Fruit fruit = getItem(position);

    View view;
    ViewHolder viewHolder;
    if(convertView == null){
        view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
        viewHolder = new ViewHolder();

        viewHolder.imageView = view.findViewById(R.id.image_fruit);
        viewHolder.textView = view.findViewById(R.id.text_fruit_name);

        //将viewHolder储存在View中
        view.setTag(viewHolder);
    }else {
        view = convertView;

        //重新获取viewHolder
        viewHolder = (ViewHolder) view.getTag();
    }

    viewHolder.imageView.setImageResource(fruit.getImageId());
    viewHolder.textView.setText(fruit.getName());

    return view;

}

class ViewHolder{

    ImageView imageView;
    TextView textView;

}
  • 点击事件

使用setOnItemClickListener给listView注册了一个监听器,当用户点击任何一个子项时,都会回调onItemClick()方法,可以通过position参数判断用户点击的哪一个子项

/**
* listView点击事件
* */
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    initFruits();
    FruitAdapter adapter = new FruitAdapter(this,R.layout.fruit_item,fruitList);

    ListView listView = findViewById(R.id.list_view);
    listView.setAdapter(adapter);

    listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
            Fruit fruit = fruitList.get(position);
            Toast.makeText(MainActivity.this,fruit.getName(),Toast.LENGTH_SHORT).show();
        }
    });

}
  • 源码地址

DoSomeAndroidTest/ListViewTest at main · qricis/DoSomeAndroidTest · GitHub

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值