当前位置: 首页 > 知识库问答 >
问题:

为什么我的Edgrid RecyclerView会对每一个项目进行布局,而不仅仅是可见的项目?

施敏达
2023-03-14

我有一个交错的网格,包含370个项目和图像。

我想确保项目被快速回收,以小心内存,但创建了一个ViewHolder,然后为适配器中的每个项目绑定,而不考虑子项目是否可见

我试过以下方法

StaggeredGridLayoutManager lm = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
rv.setLayoutManager(lm);

rv.setItemViewCacheSize(20); //Has no effect

RecyclerView.RecycledViewPool pool = new RecyclerView.RecycledViewPool();
pool.setMaxRecycledViews(0, 20);
rv.setRecycledViewPool(pool); //also has no effect

日志显示onCreateViewHolder和onBindViewHolder分别被调用185次。然后调用onViewRecycled 185次,然后继续调用onCreateViewHolder,直到达到完整的370次。

这对我来说可能是一个理解上的问题,但我认为RecyclerView应该只绑定那些可见的视图,或者只绑定20个视图,或者在池中绑定20个视图,不管屏幕上有多少视图。我怎样才能让这件事发生在LayoutManager身上?

如果我听滚动更改并使用findFirstCompletelyVisibleItemPositions和findLastCompletelyVisibleItemPositions,这仍然会覆盖适配器中的每一项,而不仅仅是屏幕上的6项

我的适配器代码

class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {

    static final int NUM_COLS = 3;
    private final LayoutInflater mInflater;
    private final List<GridItem> mEntries;
    private int mLastExpanded; //stores where the last expanded item was
    private OnCardClickListener mOnItemClick;

    MyAdapter(Context context) {
        super();
        mEntries = new ArrayList<>();
        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    void setOnTileClickListener(@Nullable OnCardClickListener listener) {
        mOnItemClick = listener;
        notifyDataSetChanged(); //recall bind logic
    }

    void setItems(Collection<GridItem> items) {
        mEntries.clear();
        mEntries.addAll(items);
        sort();
    }

    @WorkerThread
    private void sort() {
        Collections.sort(mEntries, (thisEntry, otherEntry) -> {
            int ret;
            if (otherEntry == null || thisEntry.getCreated() == otherEntry.getCreated()) {
                ret = 0;
            } else if (thisEntry.getCreated() > otherEntry.getCreated()) {
                ret = -1;
            } else {
                ret = 1;
            }
            return ret;
        });
    }

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

    private GridItem getItem(int position) {
        return mEntries.get(position);
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new MyViewHolder(mInflater.inflate(R.layout.li_grid_item, parent, false));
    }

    @Override
    public void onViewRecycled(MyViewHolder holder) {
        super.onViewRecycled(holder);
        holder.onViewRecycled(); //clears bitmap reference
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        determineTileSize(holder, position);
        holder.bind(getItem(position),  mOnItemClick);
    }

    private void determineTileSize(MyViewHolder holder, int position) {
        ViewGroup.LayoutParams cardParams = holder.getCardLayout().getLayoutParams();
        StaggeredGridLayoutManager.LayoutParams gridItemParams = (StaggeredGridLayoutManager.LayoutParams) holder.itemView.getLayoutParams();
        if (shouldBeExpanded(position)) {
            cardParams.height = (int) holder.getCard().getResources().getDimension(R.dimen.spacing_card_large);
            mLastExpanded = position;
            gridItemParams.setFullSpan(true);
        }
        holder.getCardLayout().setLayoutParams(cardParams);
    }

    private boolean shouldBeExpanded(int position) {
        return position > (mLastExpanded + NUM_COLS);  //minimum 1 row between enlarged
    }

}

我的活动布局结构

<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto" ...>
    <android.support.design.widget.AppBarLayout ...>
        <android.support.design.widget.CollapsingToolbarLayout ...>
            <android.support.v7.widget.Toolbar
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin" ... />
            <android.support.design.widget.TabLayout ...
                app:layout_collapseMode="pin"
                android:layout_width="wrap_content"
                android:layout_height="?attr/actionBarSize" />
        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>
    <FrameLayout
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />
    <FrameLayout
        android:id="@+id/bottom_sheet"
        android:layout_width="match_parent"
        android:layout_height="@dimen/height_backdrop"
        android:minHeight="@dimen/height_backdrop"
        android:background="@color/colorAccent"
        android:visibility="gone"
        app:elevation="@dimen/spacing_narrow"
        app:behavior_peekHeight="0dp"
        app:behavior_hideable="true"
        app:layout_behavior="android.support.design.widget.BottomSheetBehavior" />
</android.support.design.widget.CoordinatorLayout>

片段布局

<android.support.v4.widget.NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior" ...>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/grid_recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical" />

            <!-- Empty and loading views -->

    </RelativeLayout>

</android.support.v4.widget.NestedScrollView>

共有2个答案

慕胡媚
2023-03-14

问题在NestedScrollview中,recyclerview的可用空间尚未确定。

您可以使用android:fillViewport=“true”来制作NestedScrollView测量RecyclerViewRecyclerView将填充剩余高度。因此,如果您想滚动嵌套滚动视图,可以设置RecyclerView的最小高度。

澹台展鹏
2023-03-14

您之所以面临这个问题,是因为您在NestedScrollView中添加了RecyclerView

这不是我第一次听说这个问题,我和可能所有试图将RecyclerView放入NestedScrollView的人都遇到了这个问题(如果注意到的话)。

就我所能理解的原因而言,这是因为当您将RecyclerView放在NestedScrollView中时,它无法确定RecyclerView所需的确切高度。开发人员通常认为(简单地说)一旦上述所有视图都离开屏幕,RecyclerView height应该是match_parent。但不幸的是,情况并非如此。

它通过添加所有视图,然后测量其高度(如果我错了,请纠正我),以某种方式包装内容。不确定是否存在可能的错误或预期的行为,但我相信NestedScrollView应该能够明确地处理这种情况,否则,在NestedScrollView中添加RecyclerView是完全无用的,因为它不会回收视图,完全破坏了RecyclerView的概念,从而消耗了大量内存。

只需从NestedScrollView中删除RecyclerView,即可正确重用视图。

注意:答案可能不是100%正确,因为它完全基于我个人的观察和经验。如果答案中有更好的解决方案或改进,我们将不胜感激。

 类似资料:
  • 我有以下代码来设置RecyclerView项目的动画。这工作得很好,但如果我向下滚动,它会为所有出现的项目设置动画。 我想要的是类似于Google Play音乐应用程序中的行为,其中动画仅为最初可见的项目播放。通过滚动变得可见的项目应该出现在没有动画适配器的情况下。知道如何存档这种行为吗?

  • 我看到过许多关于将Eclipse或Gradle项目导入IntelliJ的问题,但没有一个问题问我应该使用哪种方法。我想只是导入Gradle? 我们在gradle上使用Eclipse,我可以用Gradle命令行或者(不太可能:)在Eclipse中构建和运行我的项目。只要我能在命令行上与gradle一起构建,我的老板就很高兴。 我找到了完全启用的免费学生版的 IntelliJ/Jetbrains 产品

  • 最后是我的适配器类

  • 问题内容: 我有一个go项目,这个项目开始变得越来越复杂,并且希望以减轻痛苦的方式布置文件系统。 是否有一些很好的例子说明什么有意义? 问题答案: 2013年5月更新:官方文档位于“ 代码组织 ”部分 Go代码必须保存在 工作空间中 。 工作区是目录层次结构,其根目录包含三个目录: 包含归类为软件包的Go源文件(每个目录一个软件包), 包含包对象,并且 包含可执行命令。 在构建源码包和安装产生的二

  • 我需要一些关于我的GridLayoutManager的帮助。 如果我有一个项目,它应该在中间正常显示。 对于两个项目,它们应该彼此相邻。从三到四个项目的示例,它应该像正方形一样显示。3个项目的示例4个项目的示例 如果我是对的,spanCount 2应该就是这种情况。但是,如果有5个项目(所以连续3个),那就有问题了 从5个项目(及以上)开始,每个项目应该是连续3个,例如有5个项目,如果底部行的项目

  • 我正在用Android制作一个简单的列表视图,它显示为一个对话框。它的目的是让用户选择一个将被保存为SharedReference的项目。下次用户打开同一个列表时,我希望他们上次选择的项目高亮显示。 我的问题是,当我使用ArrayAdapter创建列表时,挑出他们上次选择的索引,然后应用自定义绘图作为背景以显示当前保存的项目,ListView将3个项目显示为“selected”。它总是第一个项目,