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 方法即可

相关推荐
烬奇小云3 小时前
认识一下Unicorn
android·python·安全·系统安全
顾北川_野15 小时前
Android 进入浏览器下载应用,下载的是bin文件无法安装,应为apk文件
android
CYRUS STUDIO15 小时前
Android 下内联汇编,Android Studio 汇编开发
android·汇编·arm开发·android studio·arm
右手吉他15 小时前
Android ANR分析总结
android
PenguinLetsGo17 小时前
关于 Android15 GKI2407R40 导致梆梆加固软件崩溃
android·linux
杨武博19 小时前
音频格式转换
android·音视频
音视频牛哥21 小时前
Android音视频直播低延迟探究之:WLAN低延迟模式
android·音视频·实时音视频·大牛直播sdk·rtsp播放器·rtmp播放器·android rtmp
ChangYan.1 天前
CondaError: Run ‘conda init‘ before ‘conda activate‘解决办法
android·conda
二流小码农1 天前
鸿蒙开发:ForEach中为什么键值生成函数很重要
android·ios·harmonyos
夏非夏1 天前
Android 生成并加载PDF文件
android