【Android 实战深度剖析】HeadLine 仿今日头条项目:ListView/RecyclerView 全流程精讲

0 阅读16分钟

【Android 实战 3.2 万字深度剖析】HeadLine 仿今日头条项目:ListView/RecyclerView 全流程精讲 + 所有布局控件落地用法

摘要

HeadLine 仿今日头条是 Android 初中级开发者必敲的标杆级实战项目,整个 App 核心流量展示、新闻信息流、频道分类、图文混排、下拉刷新、上拉加载,全部依托 ListView / RecyclerView 两大列表控件实现

本文从零落地:拆解项目所有布局资源、详解每一个原生控件作用、手把手手写 ListView 传统信息流、深度吃透 RecyclerView 复用 + 多布局 + 刷新加载高级特性,搭配完整可运行 Java 代码、分层架构讲解、避坑优化方案,看完直接吃透 Android 列表开发核心,面试 + 项目双通关。


目录

  1. 项目整体定位 & 技术架构说明
  2. 开发环境配置 & 工程目录规范
  3. 项目全布局体系详解(LinearLayout/RelativeLayout/ConstraintLayout)
  4. 项目所有原生控件精讲(作用 + 属性 + 场景 + 代码)
  5. ListView 原生列表:原理 + 复用 + 头条信息流完整实现
  6. RecyclerView 进阶列表:依赖 + 基础用法 + 头条新版信息流
  7. RecyclerView 高级扩展:多布局 / 横向 / 网格 / 瀑布流 / 下拉刷新 / 上拉加载
  8. HeadLine 项目核心页面整合:首页信息流完整联调
  9. 项目高频 Bug 排查 & 性能优化终极方案
  10. 面试高频考点总结(列表复用 / 缓存 / 卡顿 / OOM)
  11. 全文总结 & 学习落地建议

一、项目整体定位 & 技术架构说明

1.1 项目背景

今日头条移动端最核心的体验就是「无限滑动新闻信息流」:

  • 顶部频道 Tab 切换
  • 主体竖向图文列表
  • 单图 / 三图 / 无图多模板 item
  • 下拉刷新最新资讯
  • 滑动到底自动加载更多
  • 点击 Item 跳转详情页

而支撑这一切的底层核心,就是 Android 两大列表容器:

  • 早期原生:ListView(适配老版本教学、理解复用原理)
  • 现代主流:RecyclerView(企业级开发、性能更强、扩展性拉满)

本项目 HeadLine 仿头条,1:1 复刻信息流逻辑,把两个列表控件做对比落地,同时把项目用到的所有布局、所有控件拆碎讲透。

1.2 核心技术栈

  • 编程语言:Java(通用教学、兼容所有 AS 版本)
  • 列表控件:ListView + RecyclerView 双版本实现
  • 布局容器:LinearLayout / RelativeLayout / ConstraintLayout
  • 基础控件:TextView/ImageView/View/ProgressBar/SwipeRefreshLayout
  • 数据层:本地模拟 Bean 实体(可无缝对接 OkHttp/Retrofit 网络接口)
  • 交互能力:下拉刷新、上拉加载、Item 点击事件、视图复用缓存

1.3 本文学习收益

读完你能彻底掌握:✅ 三种主流布局什么时候用、怎么嵌套、怎么优化层级

✅ 头条项目每一个 UI 控件的属性配置与业务场景

✅ ListView 从 0 到 1 手写信息流,吃透 ViewHolder 复用原理

✅ RecyclerView 基础 + 高级用法,企业级项目直接照搬

✅ 多图文混排列表、刷新加载逻辑独立开发

✅ 解决列表卡顿、图片错乱、数据错位、内存溢出等经典问题

image.png

二、开发环境配置 & 工程目录规范

2.1 开发工具适配

  • Android Studio 任意稳定版(Hedgehog/Meerkat 均可)
  • SDK 适配:Android 7.0 ~ Android 14 全兼容
  • Gradle:3.5+ / 7.0+ 均可运行本文代码

2.2 依赖引入(app/build.gradle)

RecyclerView、下拉刷新必须手动依赖:

gradle

dependencies {
    // 核心RecyclerView
    implementation 'androidx.recyclerview:recyclerview:1.3.2'
    // 下拉刷新控件
    implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
    // 基础兼容包
    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'com.google.android.material:material:1.11.0'
}

同步 Gradle 后,即可使用所有高级列表能力。

2.3 标准化工程目录(头条项目专用)

plaintext

src/main/
├── java/包名/
│   ├── MainActivity          # 首页主容器
│   ├── NewsDetailActivity   # 新闻详情跳转页
│   ├── bean/
│   │   └── NewsBean.java    # 新闻数据实体类
│   ├── adapter/
│   │   ├── ListViewNewsAdapter.java  # 旧版ListView适配器
│   │   └── RecyclerViewNewsAdapter.java # 新版RV适配器
│   └── utils/
│       └── DataUtil.java    # 模拟数据工具类
├── res/layout/
│   ├── activity_main.xml        # 主页面布局
│   ├── item_news_listview.xml   # ListView单条新闻Item
│   ├── item_news_rv.xml         # RecyclerView单条新闻Item
│   ├── layout_load_more.xml     # 上拉加载更多底部布局
│   └── activity_detail.xml      # 详情页布局
├── res/drawable/                # 新闻缩略图、图标资源
└── res/values/                  # 颜色、尺寸、文字常量

规范目录是企业级开发基础,后续加网络请求、图片框架、埋点都能无缝扩展。

三、项目全布局体系详解(头条项目全部在用)

布局 = 容器,决定控件摆放规则,HeadLine 项目只依赖三大布局,覆盖 99% 场景。

3.1 LinearLayout 线性布局(项目最常用)

3.1.1 核心原理

水平 horizontal / 垂直 vertical单向排列子控件,适合规整流式布局。

3.1.2 必记核心属性

android:orientation="vertical"   竖向排
android:orientation="horizontal" 横向排
android:layout_width="match_parent" 占满父宽
android:layout_height="wrap_content" 自适应高
android:gravity="center" 内部居中
android:layout_weight="1" 权重均分空间
android:padding="10dp" 内边距
android:layout_margin="5dp" 外边距

3.1.3 头条项目实际使用场景

  1. 每条新闻 Item 内部:左边图、右边文字竖向排列
  2. 顶部标题栏、频道栏竖向容器
  3. 底部加载更多提示栏
  4. 文字描述 + 时间来源横向组合

image.png

3.1.4 实战标准模板(头条 Item 基础结构)

<?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="12dp"
    android:gravity="center_vertical">

    <!-- 左侧新闻缩略图 -->
    <ImageView
        android:id="@+id/iv_news_img"
        android:layout_width="100dp"
        android:layout_height="70dp"
        android:scaleType="centerCrop"/>

    <!-- 右侧文字区域:标题+来源+时间 -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_marginStart="10dp">

        <TextView
            android:id="@+id/tv_news_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="16sp"
            android:textColor="#222222"
            android:maxLines="2"
            android:ellipsize="end"/>

        <TextView
            android:id="@+id/tv_news_info"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="12sp"
            android:textColor="#999999"
            android:layout_marginTop="6dp"/>

    </LinearLayout>
</LinearLayout>

3.2 RelativeLayout 相对布局(传统复杂对齐专用)

3.2.1 核心原理

控件不按流排列,而是相对父容器 / 其他控件定位

3.2.2 核心定位属性

android:layout_alignParentLeft="true" 贴父左
android:layout_alignParentRight="true" 贴父右
android:layout_centerInParent="true" 整体居中
android:layout_below="@id/xxx" 在某控件下方
android:layout_toRightOf="@id/xxx" 在某控件右侧

3.2.3 头条项目使用场景

  1. Item 右上角标注「热门 / 置顶」标签
  2. 时间信息固定靠右、标题靠左不重叠
  3. 老版本复杂详情页布局嵌套

3.2.4 极简实战示例

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="12dp">

    <TextView
        android:id="@+id/title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="头条热点新闻标题"/>

    <TextView
        android:id="@+id/hot_tag"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="热门"
        android:textColor="#FF4444"
        android:layout_alignParentRight="true"
        android:layout_below="@id/title"/>

</RelativeLayout>

3.3 ConstraintLayout 约束布局(现代推荐、性能最优)

3.3.1 核心原理

通过上下左右约束线固定控件位置,减少布局嵌套、层级扁平化,企业级首选。

3.3.2 核心约束属性

app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/xxx"

3.3.3 头条项目使用场景

  1. 新版扁平化新闻 Item(无多层嵌套)
  2. 首页顶部 Tab + 列表联动复杂页面
  3. 三图图文混排复杂 Item 布局

3.3.4 头条标准约束布局 Item 示例

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="12dp">

    <ImageView
        android:id="@+id/iv_img"
        android:layout_width="100dp"
        android:layout_height="70dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="头条新闻标题超长两行省略展示"
        android:maxLines="2"
        android:ellipsize="end"
        app:layout_constraintLeft_toRightOf="@id/iv_img"
        app:layout_constraintTop_toTopOf="@id/iv_img"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintMarginStart="10dp"/>

    <TextView
        android:id="@+id/tv_info"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="来源:头条资讯 | 2026-04-01"
        app:layout_constraintLeft_toRightOf="@id/iv_img"
        app:layout_constraintTop_toBottomOf="@id/tv_title"
        app:layout_constraintMarginStart="10dp"/>

</androidx.constraintlayout.widget.ConstraintLayout>

3.4 三大布局选型总结(开发必背)

  1. 简单横竖排列 → LinearLayout
  2. 老项目复杂相对对齐 → RelativeLayout
  3. 新项目、扁平化、性能优化 → ConstraintLayout

头条主流信息流,现在全用 ConstraintLayout 扁平化实现。

四、项目所有原生控件精讲(作用 + 属性 + 场景 + 代码)

4.1 TextView 文本控件(最核心)

作用

展示新闻标题、来源、时间、简介、标签、备注文字。

高频必备属性

android:text="文字内容"
android:textSize="16sp"  // 文字字号必须用sp
android:textColor="#333333"
android:maxLines="2"     // 标题最多两行
android:ellipsize="end"  // 超出末尾省略号
android:textStyle="bold" // 加粗标题
android:padding="3dp"

头条专属场景

  • 主标题:16sp 深灰、两行省略
  • 来源时间:12sp 浅灰
  • 热门标签:红色小字

4.2 ImageView 图片控件

作用

新闻缩略图、频道图标、置顶图标、封面大图。

核心属性

android:src="@drawable/news1" // 本地图片资源
android:scaleType="centerCrop" // 居中裁剪不变形
android:adjustViewBounds="true" // 自适应边界
android:visibility="gone/visible" // 无图时隐藏

头条场景

单图 Item、三图 Item、大图详情封面全靠它。

4.3 View 分割线控件

作用

Item 之间灰色分割线,美化信息流。

xml

<View
    android:layout_width="match_parent"
    android:layout_height="1px"
    android:background="#EEEEEE"/>

4.4 ProgressBar 进度加载条

作用

上拉加载更多时显示「加载中」转圈动画。

<ProgressBar
    android:layout_width="20dp"
    android:layout_height="20dp"
    style="?android:attr/progressBarStyleSmall"/>

4.5 SwipeRefreshLayout 下拉刷新容器

作用

包裹 RecyclerView,实现下拉转圈刷新最新资讯。头条首页必备交互控件。

4.6 ListView / RecyclerView 列表容器

全文核心,后面整章节落地实现。

五、ListView 原生列表:原理 + 复用 + 头条信息流完整实现

image.png

5.1 ListView 核心认知

Android 早期经典竖向列表:

  • 自带滚动
  • 自带复用缓存(convertView)
  • 入门必学,理解列表底层原理
  • 适合简单信息流、老项目维护

5.2 ListView 固定五步开发流程

  1. 写 Activity 主布局,放入 ListView
  2. 写 Item 单行新闻布局
  3. 创建 NewsBean 数据实体类
  4. 继承 BaseAdapter 写适配器 + ViewHolder
  5. Activity 初始化模拟数据 + 绑定适配器 + 点击事件

image.png

5.3 第一步:主页面布局 activity_main.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="vertical">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center"
        android:text="HeadLine 仿今日头条(ListView版)"
        android:textSize="18sp"
        android:textColor="#FFFFFF"
        android:background="#FF6600"/>

    <ListView
        android:id="@+id/lv_news"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:divider="#EEEEEE"
        android:dividerHeight="1px"
        android:scrollbars="none"/>

</LinearLayout>

5.4 第二步:新闻单行 Item item_news_listview.xml

沿用前面 LinearLayout 标准 Item 布局即可。

5.5 第三步:新闻实体类 NewsBean.java

public class NewsBean {
    private String title;
    private String info;
    private int imgRes;

    public NewsBean(String title, String info, int imgRes) {
        this.title = title;
        this.info = info;
        this.imgRes = imgRes;
    }

    public String getTitle() { return title; }
    public String getInfo() { return info; }
    public int getImgRes() { return imgRes; }
}

5.6 第四步:ListView 适配器(含 ViewHolder 复用)

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 ListViewNewsAdapter extends BaseAdapter {

    private Context mContext;
    private List<NewsBean> mNewsList;

    public ListViewNewsAdapter(Context context, List<NewsBean> list){
        this.mContext = context;
        this.mNewsList = list;
    }

    @Override
    public int getCount() {
        return mNewsList.size();
    }

    @Override
    public Object getItem(int position) {
        return mNewsList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        // 复用判断:无缓存则加载布局、找控件
        if(convertView == null){
            convertView = LayoutInflater.from(mContext)
                    .inflate(R.layout.item_news_listview,parent,false);
            holder = new ViewHolder();
            holder.ivImg = convertView.findViewById(R.id.iv_news_img);
            holder.tvTitle = convertView.findViewById(R.id.tv_news_title);
            holder.tvInfo = convertView.findViewById(R.id.tv_news_info);
            convertView.setTag(holder);
        }else{
            // 有缓存直接拿Tag复用控件,避免重复findViewById
            holder = (ViewHolder) convertView.getTag();
        }

        // 绑定当前行数据
        NewsBean bean = mNewsList.get(position);
        holder.tvTitle.setText(bean.getTitle());
        holder.tvInfo.setText(bean.getInfo());
        holder.ivImg.setImageResource(bean.getImgRes());

        return convertView;
    }

    // ViewHolder:缓存控件,极致优化性能
    static class ViewHolder{
        ImageView ivImg;
        TextView tvTitle;
        TextView tvInfo;
    }
}

5.7 第五步:MainActivity 绑定数据 + 点击事件

import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private ListView lvNews;
    private List<NewsBean> newsList;
    private ListViewNewsAdapter adapter;

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

        lvNews = findViewById(R.id.lv_news);
        initNewsData();
        adapter = new ListViewNewsAdapter(this,newsList);
        lvNews.setAdapter(adapter);

        // Item点击跳转交互
        lvNews.setOnItemClickListener((AdapterView<?> parent, View view, int position, long id) -> {
            String title = newsList.get(position).getTitle();
            Toast.makeText(MainActivity.this,"打开资讯:"+title,Toast.LENGTH_SHORT).show();
            // 此处可跳转NewsDetailActivity详情页
        });
    }

    // 模拟头条海量新闻数据
    private void initNewsData(){
        newsList = new ArrayList<>();
        newsList.add(new NewsBean("2026互联网资讯行业最新趋势解读","头条科技 | 2026-04-01",R.drawable.news1));
        newsList.add(new NewsBean("Android开发零基础入门全攻略","技术频道 | 2026-04-01",R.drawable.news2));
        newsList.add(new NewsBean("移动端信息流列表性能优化深度分享","开发者专栏 | 2026-04-01",R.drawable.news3));
        // 可循环批量添加几十条模拟无限流
    }
}

5.8 ListView 核心复用原理(面试必考)

  1. convertView:划出屏幕的 Item 不会销毁,存入缓存
  2. 只创建「一屏可见数量」的 View,不会成千上百创建
  3. ViewHolder 缓存控件 ID,杜绝频繁 findViewById 造成卡顿
  4. 极大减少内存占用,防止滑动 OOM

理解 ListView 复用,才能彻底看懂 RecyclerView 底层优化逻辑。

六、RecyclerView 进阶列表:依赖 + 基础用法 + 头条新版信息流

image.png

6.1 RecyclerView 为什么取代 ListView

  • 强制 ViewHolder,杜绝手写不规范
  • 原生支持横向 / 网格 / 瀑布流
  • 原生支持动画、局部刷新
  • 完美适配多图文混排(头条核心需求)
  • 架构解耦,扩展性极强

6.2 基础五步落地

  1. 引入 RecyclerView 依赖
  2. 主布局放入 RecyclerView + 下拉刷新
  3. 复用新闻 Item 布局
  4. 自定义 RecyclerView.Adapter
  5. 设置 LayoutManager + 绑定数据 + 点击事件

6.3 主布局(含下拉刷新)

<?xml version="1.0" encoding="utf-8"?>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/srl_refresh"
    
    
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:gravity="center"
            android:text="HeadLine 仿今日头条(RecyclerView新版)"
            android:textSize="18sp"
            android:background="#FF6600"
            android:textColor="#FFFFFF"/>

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/rv_news"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scrollbars="none"/>

    </LinearLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

6.4 RecyclerView 标准适配器

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;

public class RecyclerViewNewsAdapter extends RecyclerView.Adapter<RecyclerViewNewsAdapter.NewsHolder> {

    private List<NewsBean> mList;
    private OnItemClick listener;

    // 点击事件回调接口
    public interface OnItemClick{
        void click(int pos);
    }

    public void setOnItemClick(OnItemClick click){
        this.listener = click;
    }

    public RecyclerViewNewsAdapter(List<NewsBean> list){
        this.mList = list;
    }

    // 创建ViewHolder:加载Item布局
    @NonNull
    @Override
    public NewsHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.item_news_rv,parent,false);
        return new NewsHolder(view);
    }

    // 绑定数据到控件
    @Override
    public void onBindViewHolder(@NonNull NewsHolder holder, int position) {
        NewsBean bean = mList.get(position);
        holder.tvTitle.setText(bean.getTitle());
        holder.tvInfo.setText(bean.getInfo());
        holder.ivImg.setImageResource(bean.getImgRes());

        // 条目点击
        holder.itemView.setOnClickListener(v->{
            if(listener != null){
                listener.click(position);
            }
        });
    }

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

    // 内置ViewHolder
    public static class NewsHolder extends RecyclerView.ViewHolder{
        ImageView ivImg;
        TextView tvTitle;
        TextView tvInfo;

        public NewsHolder(@NonNull View itemView) {
            super(itemView);
            ivImg = itemView.findViewById(R.id.iv_news_img);
            tvTitle = itemView.findViewById(R.id.tv_news_title);
            tvInfo = itemView.findViewById(R.id.tv_news_info);
        }
    }
}

6.5 Activity 初始化 RV + 下拉刷新

import android.os.Bundle;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import java.util.ArrayList;
import java.util.List;

public class MainRvActivity extends AppCompatActivity {

    private RecyclerView rvNews;
    private SwipeRefreshLayout refreshLayout;
    private List<NewsBean> newsList;
    private RecyclerViewNewsAdapter rvAdapter;

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

        rvNews = findViewById(R.id.rv_news);
        refreshLayout = findViewById(R.id.srl_refresh);

        // 1. 设置竖向布局管理器(头条默认)
        rvNews.setLayoutManager(new LinearLayoutManager(this));

        // 2. 初始化模拟数据
        initData();

        // 3. 绑定适配器
        rvAdapter = new RecyclerViewNewsAdapter(newsList);
        rvNews.setAdapter(rvAdapter);

        // 4. 条目点击
        rvAdapter.setOnItemClick(pos->{
            Toast.makeText(this,"查看资讯:"+newsList.get(pos).getTitle(),Toast.LENGTH_SHORT).show();
        });

        // 5. 下拉刷新逻辑
        refreshLayout.setOnRefreshListener(()->{
            newsList.clear();
            initData();
            rvAdapter.notifyDataSetChanged();
            refreshLayout.setRefreshing(false);
        });
    }

    private void initData(){
        newsList = new ArrayList<>();
        // 批量添加头条模拟资讯数据
    }
}

七、RecyclerView 高级扩展(头条必备)

7.1 横向频道列表

LinearLayoutManager manager = new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false);
rvChannel.setLayoutManager(manager);

实现顶部左右滑动频道标签(推荐 / 热点 / 科技 / 娱乐)。

7.2 三图网格资讯

rvNews.setLayoutManager(new GridLayoutManager(this,3));

7.3 瀑布流图文

rvNews.setLayoutManager(new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL));

7.4 上拉加载更多

监听 RV 滑动到底部,追加数据并调用:

rvAdapter.notifyItemInserted(newsList.size());

image.png

八、HeadLine 项目高频 Bug 排查 + 终极性能优化(实战必用)

8.1 列表滑动卡顿(头条项目最常见)

问题原因
  1. Item 布局嵌套太深(多层 LinearLayout 套娃)
  2. getView /onBindViewHolder 中做耗时操作(循环、网络、大图处理)
  3. 频繁 findViewById(没用好 ViewHolder)
  4. 图片原图加载、未压缩、未缓存
优化方案

1 布局扁平化优先用 ConstraintLayout 代替多层 LinearLayout,减少布局测量 / 绘制耗时。2 强制 ViewHolder 复用ListView 必须封装 ViewHolder;RecyclerView 本身强制缓存,不要在绑定里重复找控件。3 图片专业处理禁止原生 setImageResource 放大图;接入 Glide:

Glide.with(context)
     .load(url)
     .override(100,70) // 限制Item图片尺寸
     .centerCrop()
     .into(ivImg);

4 主线程减负数据解析、拼接字符串、图片压缩全放子线程;UI 只做赋值。


8.2 滑动复用导致数据错乱 / 图片闪跳

现象

上下滑动列表,标题、图片来回乱变;明明是 A 新闻,滑一下变成 B 图。

根因

复用机制导致 Item 被回收复用,部分控件没强制覆值

修复标准写法

绑定数据时:

  • 有图就赋值,无图主动 setImageDrawable(null)
  • 文字全部重新 setText
  • 隐藏控件主动设置 visibility
if(bean.getImgRes() == 0){
    holder.ivImg.setVisibility(View.GONE);
    holder.ivImg.setImageDrawable(null);
}else{
    holder.ivImg.setVisibility(View.VISIBLE);
    holder.ivImg.setImageResource(bean.getImgRes());
}

核心口诀:复用场景,所有控件必须全覆盖赋值


8.3 下拉刷新 / 上拉加载重复触发

问题

拉一次,连续加载多次数据、重复弹窗、重复添加列表。

解决

加全局状态锁:

boolean isLoading = false;

// 开始加载
if(!isLoading){
    isLoading = true;
    // 请求数据 / 追加列表
    // 完成后置空
    isLoading = false;
}

下拉刷新结束必须:

refreshLayout.setRefreshing(false);

8.4 大图内存溢出 OOM(闪退)

原因

新闻缩略图尺寸太大、没压缩、没复用缓存。

优化
  1. 图片统一限制 ImageView 固定宽高(头条 Item 固定 100x70)
  2. 使用 Glide 内存缓存 + 磁盘缓存
  3. 禁止在 Item 内加载高清原图
  4. 页面销毁时停止图片加载:
Glide.with(context).clear(ivImg);

8.5 分割线错乱、Item 间距不均

解决

放弃原生 divider,统一用:

<View
    android:layout_width="match_parent"
    android:layout_height="1px"
    android:background="#EEEEEE"/>

自己控制上下边距,适配所有机型。

九、企业级进阶优化(上线版头条必备)

9.1 RecyclerView 局部刷新(不闪屏)

不用全局 notifyDataSetChanged()精准刷新单条:

adapter.notifyItemChanged(position);

新增数据:

adapter.notifyItemInserted(list.size()-1);

9.2 预加载优化(无限流顺滑)

监听滑动,距离底部 3 个 Item 时提前加载更多:

LinearLayoutManager manager = (LinearLayoutManager) recyclerView.getLayoutManager();
int lastPos = manager.findLastCompletelyVisibleItemPosition();
if(lastPos >= list.size() - 3 && !isLoading){
    loadMore();
}

9.3 多布局 Item(头条单图 / 三图 / 无图)

重写适配器:

@Override
public int getItemViewType(int position){
    // 根据Bean类型返回 0=单图 1=三图 2=纯文字
}

再根据 viewType 加载不同 Item 布局,实现头条混排信息流。

9.4 开启硬件加速

在 AndroidManifest 配置:

android:hardwareAccelerated="true"

提升列表滑动帧率。


十、面试必考 10 道真题(ListView+RecyclerView 高频标准答案)

1、ListView 复用原理是什么?

屏幕只创建可见个数的 ItemView;滑出屏幕的 View 存入缓存(convertView);新 Item 直接复用旧 View,配合 ViewHolder 缓存控件 ID,减少 inflate 和 findViewById,提升性能防 OOM。

2、ViewHolder 作用?

缓存控件引用,避免每次 getView 频繁 findViewById,解决卡顿。

3、为什么 RecyclerView 替代 ListView?

  • 强制 ViewHolder
  • 自带布局管理器(横 / 竖 / 网格 / 瀑布流)
  • 支持局部刷新、动画、多布局
  • 解耦、扩展性强、性能更好

4、RecyclerView 三大组件是什么?

Adapter(数据绑定)+ LayoutManager(布局规则)+ ItemAnimator(动画)。

5、列表滑动图片错乱怎么解决?

复用导致;绑定数据时所有控件强制全覆盖赋值,空数据清空图片 / 隐藏控件。

6、notifyDataSetChanged 缺点?

全局刷新,开销大、闪屏、不精准;推荐用 notifyItemChanged /notifyItemInserted。

7、RecyclerView 缓存几级?

四级缓存:ViewCache → RecyclerViewPool → 自定义缓存 → 磁盘缓存(图片)。

8、item 布局为什么推荐 ConstraintLayout?

减少嵌套、测量绘制更快、扁平化布局,大幅降低滑动卡顿。

9、怎么实现上拉加载更多?

监听滑动到底部 + 加加载锁防重复请求 + 追加数据 + notifyItemInserted。

10、图文列表防 OOM 怎么做?

固定图片尺寸、压缩图片、使用图片框架缓存、不加载原图、页面销毁清空图片引用。