Android ListView 与 RecyclerView 实战:仿今日头条项目从 0 到 1 详解

0 阅读12分钟

前言

在 Android 开发中,列表控件是最常用、最重要的 UI 组件之一。无论是新闻、商城、社交、视频类 App,都离不开列表展示。

本文将从基础到实战,完整讲解:

ListView 传统列表用法与优化

RecyclerView 现代列表用法与优势

仿今日头条首页布局

多类型 Item 实现

下拉刷新 + 上拉加载

图片加载与性能优化

一、项目概述

本次基于Android Studio开发的HeadLine仿今日头条项目,核心是通过ListView与RecyclerView两种列表控件实现数据展示,同时完成仿今日头条的UI布局与交互逻辑。项目覆盖了列表控件的基础使用、自定义适配器、布局资源设计、控件联动等核心知识点,是Android列表开发的经典实战案例。

二、ListView与RecyclerView核心概念对比

在项目开发前,先明确两种列表控件的核心差异,为后续实战做铺垫:

微信图片_20260331160555_647_1.jpg

三、ListView实战:购物商城列表实现

  1. 项目效果展示

项目中ListView实现了购物商城列表,包含商品图片、名称、价格三部分信息,效果如下:

  1. 核心布局资源与控件

(1)主布局:activity_main.xml

主布局用于承载ListView控件,核心代码如下:

<!-- 顶部标题栏 -->
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:background="#FF9800"
    android:gravity="center">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="购物商城"
        android:textColor="#FFFFFF"
        android:textSize="18sp"
        android:textStyle="bold"/>

</LinearLayout>

<!-- ListView列表控件 -->
<ListView
    android:id="@+id/lv_goods"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:divider="#E0E0E0"
    android:dividerHeight="1dp"/>
核心控件说明:

ListView:列表根控件,id用于代码中绑定,divider设置条目分割线,dividerHeight设置分割线高度。

LinearLayout:垂直布局,用于承载标题栏与ListView,实现上下结构。

TextView:标题栏文本控件,设置背景色、文字样式,突出标题。

(2)条目布局:item_goods.xml

ListView的每个条目对应一个自定义布局,包含商品图片、名称、价格,核心代码如下:

<!-- 商品图片 -->
<ImageView
    android:id="@+id/iv_goods_img"
    android:layout_width="80dp"
    android:layout_height="80dp"
    android:scaleType="centerCrop"
    android:src="@mipmap/ic_launcher"/>

<!-- 商品信息布局 -->
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:layout_marginLeft="15dp">

    <!-- 商品名称 -->
    <TextView
        android:id="@+id/tv_goods_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="商品名称"
        android:textSize="16sp"
        android:textColor="#333333"
        android:textStyle="bold"/>

    <!-- 商品价格 -->
    <TextView
        android:id="@+id/tv_goods_price"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="价格:0元"
        android:textSize="14sp"
        android:textColor="#FF9800"
        android:layout_marginTop="5dp"/>

</LinearLayout>

核心控件说明:

ImageView:用于展示商品图片,scaleType="centerCrop"保证图片按比例裁剪填充,避免变形。

TextView:分别用于展示商品名称(加粗深色)与价格(橙色突出),通过layout_marginTop设置间距。

LinearLayout:水平布局实现图片+文字的左右结构,垂直布局实现名称+价格的上下结构,padding设置条目内边距,提升视觉效果。

  1. ListView核心代码实现

(1)数据实体类:Goods.java

用于封装商品数据,实现数据与视图的分离:

public class Goods {
private int imgId; // 商品图片资源ID
private String name; // 商品名称
private String price; // 商品价格

public Goods(int imgId, String name, String price) {
    this.imgId = imgId;
    this.name = name;
    this.price = price;
}

// Getter方法
public int getImgId() { return imgId; }
public String getName() { return name; }
public String getPrice() { return price; }

}

(2)自定义适配器:GoodsAdapter.java

ListView的核心是适配器,用于将数据绑定到条目布局: public class GoodsAdapter extends BaseAdapter { private Context mContext; private List mGoodsList;

public GoodsAdapter(Context context, List<Goods> goodsList) {
    this.mContext = context;
    this.mGoodsList = goodsList;
}

@Override
public int getCount() {
    // 返回数据总数
    return mGoodsList.size();
}

@Override
public Object getItem(int position) {
    // 返回对应位置的数据
    return mGoodsList.get(position);
}

@Override
public long getItemId(int position) {
    // 返回条目ID
    return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder viewHolder;
    // 复用View,优化性能
    if (convertView == null) {
        convertView = LayoutInflater.from(mContext).inflate(R.layout.item_goods, parent, false);
        viewHolder = new ViewHolder();
        viewHolder.ivImg = convertView.findViewById(R.id.iv_goods_img);
        viewHolder.tvName = convertView.findViewById(R.id.tv_goods_name);
        viewHolder.tvPrice = convertView.findViewById(R.id.tv_goods_price);
        convertView.setTag(viewHolder);
    } else {
        viewHolder = (ViewHolder) convertView.getTag();
    }

    // 绑定数据
    Goods goods = mGoodsList.get(position);
    viewHolder.ivImg.setImageResource(goods.getImgId());
    viewHolder.tvName.setText(goods.getName());
    viewHolder.tvPrice.setText(goods.getPrice());

    return convertView;
}

// ViewHolder缓存控件
static class ViewHolder {
    ImageView ivImg;
    TextView tvName;
    TextView tvPrice;
}

核心逻辑说明:

BaseAdapter:ListView的基础适配器,需实现4个核心方法,其中getView是条目渲染的核心。

ViewHolder:缓存条目内的控件,避免每次getView都重复findViewById,大幅优化ListView性能。

convertView:复用已滑出屏幕的条目View,减少View的创建次数,提升列表滑动流畅度。

(3)MainActivity.java:绑定ListView与数据

public class MainActivity extends AppCompatActivity {
private ListView lvGoods;
private List<Goods> goodsList = new ArrayList<>();

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

    // 绑定ListView控件
    lvGoods = findViewById(R.id.lv_goods);

    // 初始化商品数据
    initGoodsData();

    // 创建适配器并设置给ListView
    GoodsAdapter adapter = new GoodsAdapter(this, goodsList);
    lvGoods.setAdapter(adapter);

    // 设置条目点击事件
    lvGoods.setOnItemClickListener((parent, view, position, id) -> {
        Goods goods = goodsList.get(position);
        Toast.makeText(this, "你点击了:" + goods.getName(), Toast.LENGTH_SHORT).show();
    });
}

// 初始化商品数据
private void initGoodsData() {
    goodsList.add(new Goods(R.drawable.table, "桌子", "价格:1800元"));
    goodsList.add(new Goods(R.drawable.apple, "苹果", "价格:10元/kg"));
    goodsList.add(new Goods(R.drawable.cake, "蛋糕", "价格:300元"));
    goodsList.add(new Goods(R.drawable.sweater, "线衣", "价格:350元"));
    goodsList.add(new Goods(R.drawable.kiwi, "猕猴桃", "价格:10元/kg"));
    goodsList.add(new Goods(R.drawable.scarf, "围巾", "价格:280元"));
}

核心逻辑说明:

findViewById:绑定布局中的ListView控件。

initGoodsData:初始化商品数据,将图片资源、名称、价格封装到Goods对象中。

setAdapter:将自定义适配器设置给ListView,完成数据与视图的绑定。

setOnItemClickListener:设置条目点击事件,实现点击条目弹出Toast提示。

四、RecyclerView实战:动物列表与仿今日头条实现

  1. 项目效果展示

(1)动物列表效果

通过RecyclerView实现垂直列表,展示动物图片、名称、介绍,效果如下:

(2)仿今日头条效果

基于RecyclerView实现复杂的新闻列表,包含顶部搜索栏、分类标签、多类型新闻条目,效果如下:

  1. 核心布局资源与控件

(1)主布局:activity_main.xml(RecyclerView版)

<!-- 顶部标题栏 -->
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:background="#009688"
    android:gravity="center">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="RecyclerView"
        android:textColor="#FFFFFF"
        android:textSize="18sp"
        android:textStyle="bold"/>

</LinearLayout>

<!-- RecyclerView列表控件 -->
<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/rv_animal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="5dp"/>

核心控件说明:

RecyclerView:AndroidX提供的列表控件,需使用完整包名androidx.recyclerview.widget.RecyclerView,padding设置列表内边距。

与ListView相比,RecyclerView无默认分割线,需自定义ItemDecoration实现。

(2)条目布局:item_animal.xml

<!-- 动物图片 -->
<ImageView
    android:id="@+id/iv_animal_img"
    android:layout_width="80dp"
    android:layout_height="80dp"
    android:scaleType="centerCrop"
    android:src="@mipmap/ic_launcher"/>

<!-- 动物信息布局 -->
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:layout_marginLeft="15dp">

    <!-- 动物名称 -->
    <TextView
        android:id="@+id/tv_animal_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="动物名称"
        android:textSize="16sp"
        android:textColor="#333333"
        android:textStyle="bold"/>

    <!-- 动物介绍 -->
    <TextView
        android:id="@+id/tv_animal_desc"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="动物介绍"
        android:textSize="14sp"
        android:textColor="#666666"
        android:layout_marginTop="5dp"
        android:maxLines="2"
        android:ellipsize="end"/>

</LinearLayout>

核心控件说明:

maxLines="2" + ellipsize="end":限制介绍文本最多2行,超出部分以省略号结尾,保证列表布局整齐。

selectableItemBackground:设置条目点击水波纹效果,提升交互体验。

(3)仿今日头条主布局:activity_main.xml

<!-- 顶部搜索栏 -->
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:background="#F44336"
    android:gravity="center_vertical"
    android:paddingLeft="10dp"
    android:paddingRight="10dp">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="仿今日头条"
        android:textColor="#FFFFFF"
        android:textSize="18sp"
        android:textStyle="bold"/>

    <EditText
        android:layout_width="0dp"
        android:layout_height="35dp"
        android:layout_weight="1"
        android:layout_marginLeft="15dp"
        android:background="@drawable/bg_search"
        android:hint="搜你想搜的"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"/>

</LinearLayout>

<!-- 分类标签栏 -->
<HorizontalScrollView
    android:layout_width="match_parent"
    android:layout_height="40dp"
    android:background="#FFFFFF"
    android:paddingLeft="10dp">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:orientation="horizontal"
        android:gravity="center_vertical">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="推荐"
            android:textColor="#F44336"
            android:textSize="15sp"
            android:paddingLeft="10dp"
            android:paddingRight="10dp"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="抗疫"
            android:textColor="#333333"
            android:textSize="15sp"
            android:paddingLeft="10dp"
            android:paddingRight="10dp"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="小视频"
            android:textColor="#333333"
            android:textSize="15sp"
            android:paddingLeft="10dp"
            android:paddingRight="10dp"/>

        <!-- 更多分类标签 -->

    </LinearLayout>

</HorizontalScrollView>

<!-- RecyclerView新闻列表 -->
<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/rv_news"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#F5F5F5"
    android:padding="5dp"/>

核心控件说明:

HorizontalScrollView:实现横向滚动的分类标签栏,解决标签过多超出屏幕的问题。

EditText:搜索框,通过layout_weight实现宽度自适应,bg_search自定义圆角背景。

RecyclerView:承载新闻列表,设置背景色与内边距,提升视觉效果。

  1. RecyclerView核心代码实现

(1)数据实体类:Animal.java / News.java

Animal.java(动物列表):
public class Animal {
private int imgId;
private String name;
private String desc;

public Animal(int imgId, String name, String desc) {
    this.imgId = imgId;
    this.name = name;
    this.desc = desc;
}

public int getImgId() { return imgId; }
public String getName() { return name; }
public String getDesc() { return desc; }}

News.java(新闻列表): public class News { private String title; private String source; private String time; private int imgId; private int type; // 1:单图新闻 2:三图新闻 3:视频新闻

public News(String title, String source, String time, int imgId, int type) {
    this.title = title;
    this.source = source;
    this.time = time;
    this.imgId = imgId;
    this.type = type;
}

// Getter方法
public String getTitle() { return title; }
public String getSource() { return source; }
public String getTime() { return time; }
public int getImgId() { return imgId; }
public int getType() { return type; }}

(2)自定义适配器:AnimalAdapter.java public class AnimalAdapter extends RecyclerView.Adapter<AnimalAdapter.AnimalViewHolder> { private List mAnimalList; private OnItemClickListener mListener;

// 点击事件接口
public interface OnItemClickListener {
    void onItemClick(int position);
}

public void setOnItemClickListener(OnItemClickListener listener) {
    this.mListener = listener;
}

public AnimalAdapter(List<Animal> animalList) {
    this.mAnimalList = animalList;
}

@NonNull
@Override
public AnimalViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    // 加载条目布局
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_animal, parent, false);
    return new AnimalViewHolder(view);
}

@Override
public void onBindViewHolder(@NonNull AnimalViewHolder holder, int position) {
    // 绑定数据
    Animal animal = mAnimalList.get(position);
    holder.ivImg.setImageResource(animal.getImgId());
    holder.tvName.setText(animal.getName());
    holder.tvDesc.setText(animal.getDesc());

    // 设置点击事件
    holder.itemView.setOnClickListener(v -> {
        if (mListener != null) {
            mListener.onItemClick(holder.getAdapterPosition());
        }
    });
}

@Override
public int getItemCount() {
    return mAnimalList.size();
}

// ViewHolder缓存控件
static class AnimalViewHolder extends RecyclerView.ViewHolder {
    ImageView ivImg;
    TextView tvName;
    TextView tvDesc;

    public AnimalViewHolder(@NonNull View itemView) {
        super(itemView);
        ivImg = itemView.findViewById(R.id.iv_animal_img);
        tvName = itemView.findViewById(R.id.tv_animal_name);
        tvDesc = itemView.findViewById(R.id.tv_animal_desc);
    }
}}    

核心逻辑说明:

RecyclerView.Adapter:需指定泛型为自定义ViewHolder,实现3个核心方法。

onCreateViewHolder:创建ViewHolder,加载条目布局。

onBindViewHolder:绑定数据到控件,设置点击事件。

ViewHolder:强制要求缓存控件,从根本上避免重复findViewById,性能优于ListView。

OnItemClickListener:自定义点击事件接口,在Activity中实现监听,灵活性更高。

(3)MainActivity.java:绑定RecyclerView与数据

public class MainActivity extends AppCompatActivity {
private RecyclerView rvAnimal;
private List<Animal> animalList = new ArrayList<>();

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

    // 绑定RecyclerView控件
    rvAnimal = findViewById(R.id.rv_animal);

    // 初始化动物数据
    initAnimalData();

    // 设置布局管理器(垂直列表)
    LinearLayoutManager layoutManager = new LinearLayoutManager(this);
    rvAnimal.setLayoutManager(layoutManager);

    // 创建适配器并设置
    AnimalAdapter adapter = new AnimalAdapter(animalList);
    rvAnimal.setAdapter(adapter);

    // 设置条目点击事件
    adapter.setOnItemClickListener(position -> {
        Animal animal = animalList.get(position);
        Toast.makeText(this, "你点击了:" + animal.getName(), Toast.LENGTH_SHORT).show();
    });

    // 添加分割线
    rvAnimal.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
}

// 初始化动物数据
private void initAnimalData() {
    animalList.add(new Animal(R.drawable.cat, "小猫", "猫,属于猫科动物,分家猫、野猫,是全世界家庭中较为广泛的宠物。"));
    animalList.add(new Animal(R.drawable.husky, "哈士奇", "西伯利亚雪橇犬,常见别名哈士奇,昵称为二哈。"));
    animalList.add(new Animal(R.drawable.duck, "小黄鸭", "鸭的体型相对较小,颈短,一些属的嘴要大些。腿位于身体后方,步态蹒跚。"));
    animalList.add(new Animal(R.drawable.deer, "小鹿", "鹿科是哺乳纲偶蹄目下的一科动物。体型大小不等,为有角的反刍类。"));
    animalList.add(new Animal(R.drawable.tiger, "老虎", "虎,大型猫科动物;毛色浅黄或棕黄色,满有黑色横纹;头圆、耳短。"));
}

} 核心逻辑说明:

• LinearLayoutManager:设置RecyclerView为垂直滚动列表,也可使用GridLayoutManager实现网格布局。

• addItemDecoration:添加系统默认分割线,也可自定义ItemDecoration实现个性化分割线。

• 与ListView相比,RecyclerView的点击事件需在Adapter中自定义,避免了ListView的点击事件冲突问题。

五、AndroidManifest.xml配置说明

项目的清单文件AndroidManifest.xml用于配置应用的基本信息、Activity权限等,核心代码如下:

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/Theme.HeadLine">
    <activity
        android:name=".MainActivity"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>
核心配置说明:

• package:应用的包名,唯一标识应用。

• application:配置应用的图标、名称、主题等。

• activity:配置MainActivity,intent-filter设置为启动页,应用打开时首先进入该Activity。

• android:exported="true":允许系统启动该Activity,是启动页的必要配置。

六、ListView与RecyclerView核心知识点总结

  1. 核心使用流程对比

微信图片_20260331162550_681_1.jpg

  1. 性能优化要点

ListView优化:必须使用ViewHolder + convertView复用,避免重复创建View和findViewById。

RecyclerView优化:

启用setHasStableIds(true),避免数据刷新时条目闪烁。

使用DiffUtil进行局部数据刷新,替代notifyDataSetChanged。

对图片进行压缩处理,避免OOM(内存溢出)。

复杂布局使用ConstraintLayout减少层级,提升渲染速度。

  1. 适用场景选择

ListView:仅适用于简单、少量数据的列表,维护旧项目时使用,新项目不推荐。

RecyclerView:现代Android开发的主流列表控件,适用于所有列表场景,尤其是复杂、大量数据的列表,如新闻APP、电商APP等。

七、项目扩展与优化建议

  1. 多类型条目实现:在RecyclerView中重写getItemViewType,实现仿今日头条的多类型新闻条目(单图、三图、视频)。

  2. 下拉刷新与上拉加载:集成SwipeRefreshLayout实现下拉刷新,RecyclerView滚动监听实现上拉加载更多。

  3. 图片加载优化:使用Glide或Picasso框架加载网络图片,替代本地图片,实现真实新闻APP效果。

  4. Item动画定制:通过ItemAnimator自定义条目增删动画,提升交互体验。

  5. 分割线自定义:继承RecyclerView.ItemDecoration,实现个性化分割线(如间距、颜色、样式)。

八、项目截图汇总

  1. ListView购物商城列表运行效果

05df8ce05e6a2ebb7dcf1e30124a8b2b.jpg

  1. RecyclerView动物列表运行效果

9ae316babcfec68df3c159c477914f09.jpg 3. RecyclerView仿今日头条运行效果

8695f27f0a033fdce068ac8faeb9c22a.jpg 4. AndroidManifest.xml配置文件

adb4e1260549ebc414d4fb683d51ecf6.jpg 5. 项目目录结构

24b28bc79c4035bff3f4fe5892f00c25.jpg

九、总结

本次HeadLine仿今日头条项目,完整覆盖了Android中ListView与RecyclerView两种列表控件的核心使用,从布局设计、适配器实现、数据绑定到性能优化,全面梳理了列表开发的知识点。通过对比两种控件的差异,明确了现代Android开发中RecyclerView的主流地位,同时掌握了ListView的维护方法。项目可直接作为Android列表开发的学习模板,也可在此基础上扩展为完整的新闻类APP。