NestedScrollView 内的 RecyclerView 导致 RecyclerView 膨胀所有元素

作者:编程家 分类: xml 时间:2025-07-19

NestedScrollView 是一个功能强大的 Android 支持库,它可以提供滚动功能,并且可以包含其他可滚动的视图组件。其中,最常用的就是将 RecyclerView 放在 NestedScrollView 中实现滚动效果。然而,这种做法有时会导致 RecyclerView 中的元素全部加载,从而导致内存膨胀的问题。

在我们的日常开发中,经常会遇到需要在一个界面中同时显示多个列表的情况。为了方便用户查看,我们通常会使用 NestedScrollView 包裹 RecyclerView,以实现整体的滚动效果。然而,当 RecyclerView 中的元素较多时,使用这种方式会导致所有的元素一次性加载到内存中,从而导致内存占用过大,界面卡顿的问题。

那么,如何解决这个问题呢?下面我们将通过一个案例来详细介绍。

问题分析:

假设我们有一个需求,要在一个界面中同时展示两个列表:列表 A 和列表 B。为了方便用户查看,我们将这两个列表放在一个 NestedScrollView 中实现滚动效果。我们使用 RecyclerView 分别展示列表 A 和列表 B 的数据,并且通过 Adapter 将数据传递给 RecyclerView。

问题重现:

为了演示这个问题,我们创建一个简单的示例项目。首先,在布局文件中添加一个 NestedScrollView,然后在其中分别添加两个 RecyclerView。接着,我们分别创建两个 Adapter,并通过数据源将数据传递给它们。最后,将 Adapter 设置给 RecyclerView,并将 RecyclerView 添加到布局文件中。

案例代码如下所示:

xml

android:layout_width="match_parent"

android:layout_height="match_parent">

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:orientation="vertical">

android:id="@+id/recyclerViewA"

android:layout_width="match_parent"

android:layout_height="wrap_content" />

android:id="@+id/recyclerViewB"

android:layout_width="match_parent"

android:layout_height="wrap_content" />

接下来,我们在代码中实现这两个 RecyclerView 的逻辑。首先,我们创建一个数据源,用于存储列表 A 和列表 B 的数据。然后,我们分别创建两个 Adapter,并将数据源传递给它们。最后,将 Adapter 设置给 RecyclerView,并将 RecyclerView 添加到布局文件中。

java

// 创建数据源

List dataA = new ArrayList<>();

List dataB = new ArrayList<>();

// 添加数据

for (int i = 0; i < 100; i++) {

dataA.add("Item A " + i);

dataB.add("Item B " + i);

}

// 创建 Adapter

RecyclerView.Adapter adapterA = new RecyclerViewAdapter(dataA);

RecyclerView.Adapter adapterB = new RecyclerViewAdapter(dataB);

// 设置 Adapter

RecyclerView recyclerViewA = findViewById(R.id.recyclerViewA);

recyclerViewA.setLayoutManager(new LinearLayoutManager(this));

recyclerViewA.setAdapter(adapterA);

RecyclerView recyclerViewB = findViewById(R.id.recyclerViewB);

recyclerViewB.setLayoutManager(new LinearLayoutManager(this));

recyclerViewB.setAdapter(adapterB);

解决方案:

为了解决 RecyclerView 中的元素全部加载导致内存膨胀的问题,我们可以使用以下两种方式之一:

1. 使用嵌套的 RecyclerView:将 RecyclerView A 和 RecyclerView B 放在一个 LinearLayout 中,并将 LinearLayout 设置给 RecyclerView 的一个 item。这样,当 RecyclerView 在屏幕上显示时,只会加载当前可见区域的 item,从而减少了内存的占用。

2. 使用分页加载:将 RecyclerView 中的数据进行分页,每次只加载当前页的数据。当用户滚动到底部时,再加载下一页的数据。这样,不仅可以减少内存的占用,还可以提高用户体验。

使用嵌套的 RecyclerView:

首先,我们需要修改布局文件,将 LinearLayout 替换为 RecyclerView,并设置 RecyclerView 的布局管理器为 LinearLayoutManager。

xml

android:layout_width="match_parent"

android:layout_height="match_parent">

android:id="@+id/recyclerView"

android:layout_width="match_parent"

android:layout_height="wrap_content" />

接下来,我们需要修改代码,将数据源中的数据进行分页,并将每一页的数据传递给 Adapter。

java

// 创建数据源

List> data = new ArrayList<>();

// 添加数据

for (int i = 0; i < 10; i++) {

List pageData = new ArrayList<>();

for (int j = 0; j < 10; j++) {

pageData.add("Item " + (i * 10 + j));

}

data.add(pageData);

}

// 创建 Adapter

RecyclerView.Adapter adapter = new NestedRecyclerViewAdapter(data);

// 设置 Adapter

RecyclerView recyclerView = findViewById(R.id.recyclerView);

recyclerView.setLayoutManager(new LinearLayoutManager(this));

recyclerView.setAdapter(adapter);

最后,我们需要创建一个新的 Adapter,用于加载每一页的数据。

java

public class NestedRecyclerViewAdapter extends RecyclerView.Adapter {

private List> mData;

public NestedRecyclerViewAdapter(List> data) {

mData = data;

}

@NonNull

@Override

public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {

View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_nested_recyclerview, parent, false);

return new ViewHolder(view);

}

@Override

public void onBindViewHolder(@NonNull ViewHolder holder, int position) {

List pageData = mData.get(position);

RecyclerView.Adapter adapter = new RecyclerViewAdapter(pageData);

holder.recyclerView.setLayoutManager(new LinearLayoutManager(holder.itemView.getContext()));

holder.recyclerView.setAdapter(adapter);

}

@Override

public int getItemCount() {

return mData.size();

}

public static class ViewHolder extends RecyclerView.ViewHolder {

RecyclerView recyclerView;

public ViewHolder(View itemView) {

super(itemView);

recyclerView = itemView.findViewById(R.id.recyclerView);

}

}

}

分页加载:

首先,我们需要对数据源进行分页处理。可以通过添加分页标识字段,将数据分为多个页,并根据页码加载相应的数据。

java

public class DataItem {

private String content;

private int page;

public DataItem(String content, int page) {

this.content = content;

this.page = page;

}

public String getContent() {

return content;

}

public int getPage() {

return page;

}

}

// 创建数据源

List data = new ArrayList<>();

// 添加数据

for (int i = 0; i < 100; i++) {

int page = i / 10 + 1;

data.add(new DataItem("Item " + i, page));

}

接着,我们需要修改 Adapter 的逻辑,在 onBindViewHolder 方法中根据页码加载相应的数据。

java

@Override

public void onBindViewHolder(@NonNull ViewHolder holder, int position) {

List pageData = new ArrayList<>();

for (DataItem item : mData) {

if (item.getPage() == position + 1) {

pageData.add(item.getContent());

}

}

RecyclerView.Adapter adapter = new RecyclerViewAdapter(pageData);

holder.recyclerView.setLayoutManager(new LinearLayoutManager(holder.itemView.getContext()));

holder.recyclerView.setAdapter(adapter);

}

最后,我们需要为 RecyclerView 添加滚动监听器,当用户滚动到底部时,加载下一页的数据。

java

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

@Override

public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {

super.onScrolled(recyclerView, dx, dy);

LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();

int totalItemCount = layoutManager.getItemCount();

int lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition();

if (lastVisibleItemPosition == totalItemCount - 1) {

// 加载下一页的数据

int nextPage = mData.get(totalItemCount - 1).getPage() + 1;

for (int i = 0; i < 10; i++) {

int index = (nextPage - 1) * 10 + i;

mData.add(new DataItem("Item " + index, nextPage));

}

adapter.notifyDataSetChanged();

}

}

});

通过以上两种方式,我们可以有效地解决 NestedScrollView 内的 RecyclerView 导致内存膨胀的问题。使用嵌套的 RecyclerView 或分页加载,可以减少内存的占用,提高应用的性能和用户体验。