Android中ListView的使用

本文详细介绍了AndroidListView组件的使用场景、组件结构、如何通过FruitAdapter关联数据和布局,以及如何进行性能优化,包括convertView的利用和数据变化时更新视图的方法。
摘要由CSDN通过智能技术生成

ListView使用的步骤

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

ListView使用示例

接下来先看看一个 ListView 的使用示例,然后再看看其中关键的元素,最终的显示效果如下: 在这里插入图片描述

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

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 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 就是存储的每一项的数据

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 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 类的内容,后面会解释这个类的作用

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 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

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 方法中,接下来看看这个方法

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 这个参数来优化,这个参数是将之前已经加载好的布局进行缓存,因此可以进行如下方式修改:

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 中有如下方法

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
	public void notifyDataSetChanged() {  
	    mDataSetObservable.notifyChanged();  
	}
}

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

如果你看到了这里,觉得文章写得不错就给个赞呗?
更多Android进阶指南 可以扫码 解锁更多Android进阶资料


在这里插入图片描述
敲代码不易,关注一下吧。ღ( ´・ᴗ・` )

  • 30
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值