Android语言基础教程(59)Android基本组件之实现带图标的ListView:别再用光秃秃的列表了!手把手教你打造“颜值即正义”的带图标ListView,妹子看了都夸好!

嘿,各位在Android世界里摸爬滚打的“准大神”们,今天咱们来聊点有意思的。

你有没有经历过这种尴尬?当你把你呕心沥血开发的App展示给朋友或者(你懂的)心仪的妹子时,她看着屏幕上那个只有一行行黑白文字、像极了上世纪DOS界面的列表,轻轻皱了下眉头,说:“嗯……功能挺全的。” 然后,就没有然后了。

痛,太痛了!

在这个“颜值即正义”的时代,一个应用的界面就是它的脸面。而列表(ListView)作为App中最常见、最基础的“门面担当”,如果只是光秃秃地展示文字,就像你去相亲却只穿了件跨栏背心——再有趣的灵魂,也可能被第一眼pass掉。

所以,今天咱们的目标就一个:把那个土掉渣的ListView,改造成人见人爱、花见花开的“型男”! 而秘诀,就是给它加上小巧又传神的图标。

第一章:思想准备——咱不是在写代码,是在给数据“化妆”

在动手之前,咱们得先搞清楚一个核心思想:ListView的本质是个“直男”。

它非常耿直,你给它什么数据,它就原封不动地显示出来。你只给它一串字符串(List<String>),它当然就只给你显示文字。你想让它显示图标+文字?对不起,它的大脑(默认的ArrayAdapter)理解不了这么复杂的指令。

那怎么办呢?答案是:我们需要一个“私人造型师”!

这个造型师能理解我们“图标+文字”的时尚需求,并且能亲手为每一个列表项(就是列表里的每一行)打造完美造型。这个造型师在Android里,就叫 “自定义适配器”(Custom Adapter)

整个过程,我们可以理解为一场精心的“造星运动”:

  1. 打造明星(数据模型): 首先,你得有一个“明星”的蓝图。他不能只有一个名字(String),他得有名字(文字)、有脸(图标资源ID)。我们创建一个Java Bean(或者说数据类)来承载这些信息。
  2. 组建造型团队(自定义适配器): 然后,你需要一个专业的造型团队(自定义适配器)。这个团队知道如何根据明星的蓝图(数据模型),为他挑选衣服和化妆(将数据绑定到布局上)。
  3. 设计舞台和造型间(布局文件): 你需要一个华丽的舞台(ListView本身)和一个为明星量身定制的化妆间(每个列表项的布局item_layout.xml)。
  4. 上台表演(绑定与显示): 最后,把明星们(数据列表)交给造型团队(适配器),再由适配器安排他们依次登上舞台(ListView)进行表演。

是不是瞬间就觉得高大上了起来?咱们一步步来。

第二章:实战开始——从零打造一个“明星列表”

假设我们要做一个“美食App”的菜单列表,每一项要有食物图标和食物名称。

Step 1: 创造“明星”蓝图 —— FoodItem.java

首先,我们得定义一下“美食明星”应该有哪些属性。

public class FoodItem {
    private String foodName; // 明星的名字,比如“红烧肉”
    private int foodIconResId; // 明星的脸,也就是图标的资源ID,比如R.drawable.红烧肉图标

    // 构造方法,用于快速创建一个明星
    public FoodItem(String foodName, int foodIconResId) {
        this.foodName = foodName;
        this.foodIconResId = foodIconResId;
    }

    // 下面是Getter方法,方便“造型师”(适配器)获取明星的信息
    public String getFoodName() {
        return foodName;
    }

    public int getFoodIconResId() {
        return foodIconResId;
    }
}

看,很简单吧?这个类就是一个模板,以后我们每创建一个FoodItem对象,就相当于签约了一位有名字、有头像的“美食明星”。

Step 2: 设计“明星专属化妆间” —— item_layout.xml

接下来,我们要设计每个列表项长什么样。在res/layout/目录下新建这个文件。

<?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="wrap_content"
    android:orientation="horizontal"
    android:padding="16dp" <!-- 给点内边距,别让内容太挤 -->
    android:gravity="center_vertical"> <!-- 内容垂直居中 -->

    <!-- 这里是明星的头像展示位 -->
    <ImageView
        android:id="@+id/iv_food_icon"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:src="@mipmap/ic_launcher" <!-- 先放个默认图标占位,避免尴尬 -->
        android:layout_marginEnd="16dp"/> <!-- 和右边的文字保持点距离 -->

    <!-- 这里是明星的名字展示位 -->
    <TextView
        android:id="@+id/tv_food_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="美食名称"
        android:textSize="18sp"
        android:textColor="#333333"
        android:textStyle="bold"/>

</LinearLayout>

这个布局就是一个水平排列的“小房间”,左边挂照片(ImageView),右边摆名牌(TextView)。完美!

Step 3: 聘请“王牌造型师” —— FoodAdapter.java

重头戏来了!这是我们今天的核心科技。这个自定义适配器继承自BaseAdapter,它就是我们最专业的“造型团队”。

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

import java.util.List;

public class FoodAdapter extends BaseAdapter {

    private Context mContext;
    private List<FoodItem> mFoodList; // 所有等待上场的“明星”名单
    private LayoutInflater mInflater; // 一个“布局加载器”,负责把XML布局文件变成View对象

    // 构造方法,传入上下文和明星名单
    public FoodAdapter(Context context, List<FoodItem> foodList) {
        this.mContext = context;
        this.mFoodList = foodList;
        this.mInflater = LayoutInflater.from(context);
    }

    @Override
    public int getCount() {
        // 告诉ListView,我们总共有多少位明星要上场
        return mFoodList != null ? mFoodList.size() : 0;
    }

    @Override
    public Object getItem(int position) {
        // 根据位置(position)返回某一位具体的明星
        return mFoodList != null ? mFoodList.get(position) : null;
    }

    @Override
    public long getItemId(int position) {
        // 返回明星的ID,这里我们简单返回位置本身
        return position;
    }

    // 最最最核心的方法!这里就是“化妆”的现场!
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // 为了性能优化而生的ViewHolder模式!必考知识点!
        ViewHolder holder;

        // 如果“化妆间”是空的(convertView为null),我们就新搭建一个
        if (convertView == null) {
            // 用布局加载器,把咱们设计的item_layout.xml实例化成一个View(化妆间)
            convertView = mInflater.inflate(R.layout.item_layout, parent, false);

            // 创建一个ViewHolder,并把化妆间里的“道具”(ImageView和TextView)都找出来,放进“工具箱”
            holder = new ViewHolder();
            holder.ivIcon = convertView.findViewById(R.id.iv_food_icon);
            holder.tvName = convertView.findViewById(R.id.tv_food_name);

            // 把这个“工具箱”挂在化妆间(convertView)上,方便下次直接使用
            convertView.setTag(holder);
        } else {
            // 如果化妆间不是空的(是回收来的),那就直接把挂在它上面的“工具箱”拿下来用
            holder = (ViewHolder) convertView.getTag();
        }

        // 从名单里取出当前这位要化妆的明星
        FoodItem currentFood = mFoodList.get(position);

        // 开始化妆!把明星的头像设置到ImageView上
        holder.ivIcon.setImageResource(currentFood.getFoodIconResId());
        // 把明星的名字设置到TextView上
        holder.tvName.setText(currentFood.getFoodName());

        // 化妆完成,送他上台!
        return convertView;
    }

    // 这个ViewHolder就是我们的“工具箱”,用来缓存已经找到的View,避免每次都findViewById,极大提升流畅度!
    static class ViewHolder {
        ImageView ivIcon;
        TextView tvName;
    }
}

这段代码请务必仔细看注释!ViewHolder模式是ListView性能优化的灵魂,理解了它,你就超越了80%的初学者。

Step 4: 一切就绪,准备“公演” —— MainActivity.java

万事俱备,只欠东风。现在回到我们的主活动,把明星、造型师和舞台全部组织起来!

import android.os.Bundle;
import android.widget.ListView;

import androidx.appcompat.app.AppCompatActivity;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

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

        // 1. 初始化舞台(ListView)
        ListView listView = findViewById(R.id.lv_food_list);

        // 2. 组建我们的“明星天团”
        List<FoodItem> foodList = new ArrayList<>();
        foodList.add(new FoodItem("香喷喷红烧肉", R.drawable.icon_hongshaorou)); // 记得在drawable里放图片!
        foodList.add(new FoodItem("清蒸大闸蟹", R.drawable.icon_dazhaxie));
        foodList.add(new FoodItem("麻婆豆腐", R.drawable.icon_mapodoufu));
        foodList.add(new FoodItem("北京烤鸭", R.drawable.icon_kaoya));
        // ...可以继续添加更多明星

        // 3. 聘请我们的王牌造型师(自定义适配器),并把明星名单给他
        FoodAdapter adapter = new FoodAdapter(this, foodList);

        // 4. 将造型师和舞台关联起来!大幕正式拉开!
        listView.setAdapter(adapter);
    }
}

当然,别忘了主布局activity_main.xml里得有一个ListView

<ListView
    android:id="@+id/lv_food_list"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
第三章:深度拷问与升华

Q: 为啥要用ViewHolder?不用会怎样?
A: 想象一下,如果你的明星天团有1000人,每次滑动屏幕,getView方法会被疯狂调用。如果没有ViewHolder,你的“造型师”就要为每一个滑进来的项,都重新去“化妆间”里findViewById找一次道具,这活儿又累又慢,手机很快就卡成PPT了。而用了ViewHolder,就等于给每个回收的化妆间配了个“工具箱”,道具都在里面,拿来就用,效率飙升!

Q: 除了吃的,我还能秀什么?
A: 兄弟,你的想象力是唯一的限制!用这个套路,你可以做:

  • 音乐播放器: 图标(专辑封面)+ 文字(歌曲名)+ 文字(歌手)。
  • 联系人列表: 图标(头像)+ 文字(姓名)+ 文字(电话)。
  • 新闻App: 图标(新闻图片)+ 文字(标题)+ 文字(简介)。
    只需要扩展你的数据模型(比如在FoodItem里加description字段),然后在适配器的getView里多绑定一个TextView即可。一通百通!
结语

看完了吗?是不是感觉,从那个只会显示文字的“愣头青”ListView,到如今这个图文并茂、气质出众的“型男”ListView,其实只隔了一层“自定义适配器”的窗户纸?

技术的进步,就是从这种一点一滴的“颜值提升”和“体验优化”开始的。当你掌握了这个核心技能,你会发现,Android世界里很多复杂的UI组件,其思想都是相通的。

好了,教程到此结束。赶紧去打开你的Android Studio,照着代码敲一遍,打造一个属于你自己的、能让妹子眼前一亮的炫酷列表吧!

记住,我们不是代码的搬运工,我们是用户体验的造物主!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

值引力

持续创作,多谢支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值