Android中ListView的使用

ListView使用的步骤

在什么情况下会需要使用 ListView 组件呢? 当有很多项元素,而且每一项元素的布局都是相同的情况下就可以使用 ListView 组件了, ListView = List + View

ListView使用示例

接下来先看看一个 ListView 的使用示例,然后再看看其中关键的元素,最终的显示效果如下:

先看看这个示例涉及到类和布局文件有那些,项目的整体结构如下所示:

makefile 复制代码
E:.
│  AndroidManifest.xml
├─java
│  └─com
│      └─example
│          └─ademo
│              │  MainActivity.java
│              │
│              ├─adapter
│              │      FruitAdapter.java
│              │
│              └─vo
│                      EachItem.java
│
└─res
    ├─drawable
    │      ic_launcher_background.xml
    │      ic_launcher_foreground.xml
    ├─layout
    │      activity_main.xml
    │      fruit_item.xml
  • MainActivity 是项目的主 Activity
  • EachItem 是每一行需要展示的数据内容
  • FruitAdapter 是继承了 ArrayAdapter 的类,使用 ListView 的时候需要使用 Adapter
  • activity_main.xml 是主布局文件
  • fruit_item.xml 是每一行的布局形式
  • AndroidManifest.xml 是 android 项目的主配置文件

AndroidManifest.xml

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>  
<manifest xmlns:android="http://schemas.android.com/apk/res/android"  
    xmlns:tools="http://schemas.android.com/tools">  
  
    <application  
        android:allowBackup="true"  
        android:dataExtractionRules="@xml/data_extraction_rules"  
        android:fullBackupContent="@xml/backup_rules"  
        android:icon="@mipmap/ic_launcher"  
        android:label="@string/app_name"  
        android:roundIcon="@mipmap/ic_launcher_round"  
        android:supportsRtl="true"  
        android:theme="@style/Theme.ADemo"  
        tools:targetApi="31">  
        <activity  
            android:name=".MainActivity"  
            android:exported="true"  
            android:label="MainActivity">  
            <intent-filter>  
                <action android:name="android.intent.action.MAIN" />  
  
                <category android:name="android.intent.category.LAUNCHER" />  
            </intent-filter>  
        </activity>  
    </application>  
  
</manifest>

EachItem

EachItem 就是存储的每一项的数据

java 复制代码
package com.example.ademo.vo;  
  
public class EachItem {  
    private String name;
    // imageId 就是存储的图片的id
    private int imageId;  
    public EachItem(String name, int imageId)  
    {  
        this.name = name;  
        this.imageId = imageId;  
    }  
  
    public String getName() {  
        return name;  
    }  
  
    public int getImageId() {  
        return imageId;  
    }  
}

fruit_item.xml

  • fruit_item.xml 就是每一项数据最终需要展示的样子
  • 也就是最终是要将每一个 EachItem 类型的数据映射到 fruit_item 这个布局上
    • fruit_item 布局是 LinearLayout 类型,左侧是一个 ImageView, 右侧是一个 TextView
    • 最终呈现的每一行的效果就是左侧是图片,右侧是文字
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="horizontal">  
  
    <ImageView  
        android:id="@+id/fruit_image"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content" />  
  
    <TextView  
        android:id="@+id/fruit_name"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:layout_marginLeft="20dp"  
        android:layout_gravity="center_vertical"/>  
  
</LinearLayout>

FruitAdapter

这个 Adapter 的作用是什么呢? 可以先看看 FruitAdapter 的继承体系

下面是 FruitAdapter 类的内容,后面会解释这个类的作用

java 复制代码
package com.example.ademo.adapter;  
  
import android.content.Context;  
import android.view.LayoutInflater;  
import android.view.View;  
import android.view.ViewGroup;  
import android.widget.ArrayAdapter;  
import android.widget.ImageView;  
import android.widget.TextView;  
  
import androidx.annotation.NonNull;  
import androidx.annotation.Nullable;  
  
import com.example.ademo.R;  
import com.example.ademo.vo.EachItem;  
  
import java.util.List;  
  
public class FruitAdapter extends ArrayAdapter<EachItem> {  
  
    private int resourceId;  
	// 这里的 textViewResourceId 在该例子中就是 fruit_item.xml
    public FruitAdapter(@NonNull Context context, int textViewResourceId, List<EachItem> eachItems) {  
        super(context, textViewResourceId, eachItems);  
        resourceId = textViewResourceId;  
    }  
  
    @NonNull  
    @Override    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {  
        EachItem eachItem = getItem(position);  
        View view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);  
        ImageView imageView = view.findViewById(R.id.fruit_image);  
        TextView textView = view.findViewById(R.id.fruit_name);  
        imageView.setImageResource(eachItem.getImageId());  
        textView.setText(eachItem.getName());  
        return view;  
    }  
}

FruitAdapter 顶层父类是 BaseAdapter, 其实前面也说过,ListView = List + View, ListView 只负责将数据一行行的按照要求进行展示,但是具体到每一行的数据应该是要展示成什么样子呢?这个就不是 ListView 来负责的,上面已经说过每一行数据最终是通过 fruit_item.xml 这个布局文件来呈现的,那么现在的问题就是怎么将 ListView 和 fruit_item 进行关联的? 其实就是通过 Adapter 来进行关联,所以 Adapter 做的事情就是将我们一行行的数据(比如这个例子中就是 EachItem 结构的数据) 和每一行展示的视图关联起来(这个视图就是 fruit_item.xml 文件)

activity_listview.xml

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">  
  
    <ListView  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:id="@+id/list_view"/>  
</LinearLayout>

MainActivity

java 复制代码
package com.example.ademo;  
  
import android.os.Bundle;  
import android.util.Log;  
import android.widget.ListView;  
  
import androidx.appcompat.app.AppCompatActivity;  
  
import com.example.ademo.adapter.FruitAdapter;  
import com.example.ademo.vo.EachItem;  
  
import java.util.ArrayList;  
import java.util.List;  
  
public class MainActivity extends AppCompatActivity {  
  
    private List<EachItem> eachItems = new ArrayList<>();  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        Log.d("MainActivity", "开始创建主Activity" + this);  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_listview);  
        initItems();
        // adapter 做的第一件事情就是关联 fruit_item
        FruitAdapter adapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item, eachItems);  
        ListView listView = findViewById(R.id.list_view);
        // adapter 做的第二件事情就是和 ListView 进行关联 
        listView.setAdapter(adapter);  
    }  
  
    private void initItems() {  
        for (int i = 0; i < 20; i++) {  
            EachItem eachItem = new EachItem("苹果" + i, R.drawable.ic_launcher_background);  
            eachItems.add(eachItem);  
        }  
    }  
}

在 MainActivity 中也看到了 Adapter 做的事情其实就是桥梁,前面说过 ListView = List + View, Adapter 就是承担这个 "+" 的作用,将每一项的布局(View) 设置给 ListView 组件

ListView性能优化

性能瓶颈在 Adapter 的 getView 方法中,接下来看看这个方法

java 复制代码
public class FruitAdapter extends ArrayAdapter<EachItem> {  
  
    private int resourceId;  
  
    public FruitAdapter(@NonNull Context context, int textViewResourceId, List<EachItem> eachItems) {  
        super(context, textViewResourceId, eachItems);  
        resourceId = textViewResourceId;  
    }  
  
    @NonNull  
    @Override    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {  
        EachItem eachItem = getItem(position);  
        View view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);  
        ImageView imageView = view.findViewById(R.id.fruit_image);  
        TextView textView = view.findViewById(R.id.fruit_name);  
        imageView.setImageResource(eachItem.getImageId());  
        textView.setText(eachItem.getName());  
        return view;  
    }  
}

getView 方法是在滑动页面的时候来加载数据的,即使前面的数据已经加载了,当将屏幕往上滑动的时候还是会调用这个方法,这个方法会通过 View view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false); 这行代码来加载单个布局文件,当数据比较多的时候,对于已经加载的数据重复加载可能会存在瓶颈,所以可以通过 convertView 这个参数来优化,这个参数是将之前已经加载好的布局进行缓存,因此可以进行如下方式修改:

java 复制代码
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {  
    EachItem eachItem = getItem(position);  
    View view;  
    if (convertView == null) {  
        view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);  
    } else {  
        view = convertView;  
    }  
    ImageView imageView = view.findViewById(R.id.fruit_image);  
    TextView textView = view.findViewById(R.id.fruit_name);  
    imageView.setImageResource(eachItem.getImageId());  
    textView.setText(eachItem.getName());  
    return view;  
}

ListView数据变化后更新视图

在 BaseAdapter 中有如下方法

java 复制代码
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
	public void notifyDataSetChanged() {  
	    mDataSetObservable.notifyChanged();  
	}
}

所以当需要展示的数据变化后,只需要调用 Adapter 的 notifyDataSetChanged 方法即可

相关推荐
mmsx29 分钟前
android sqlite 数据库简单封装示例(java)
android·java·数据库
众拾达人3 小时前
Android自动化测试实战 Java篇 主流工具 框架 脚本
android·java·开发语言
吃着火锅x唱着歌4 小时前
PHP7内核剖析 学习笔记 第四章 内存管理(1)
android·笔记·学习
_Shirley6 小时前
鸿蒙设置app更新跳转华为市场
android·华为·kotlin·harmonyos·鸿蒙
hedalei7 小时前
RK3576 Android14编译OTA包提示java.lang.UnsupportedClassVersionError问题
android·android14·rk3576
锋风Fengfeng7 小时前
安卓多渠道apk配置不同签名
android
枫_feng8 小时前
AOSP开发环境配置
android·安卓
叶羽西8 小时前
Android Studio打开一个外部的Android app程序
android·ide·android studio
qq_1715388510 小时前
利用Spring Cloud Gateway Predicate优化微服务路由策略
android·javascript·微服务
Vincent(朱志强)11 小时前
设计模式详解(十二):单例模式——Singleton
android·单例模式·设计模式