【问题标题】:Android - Custom dynamically-generated compound View recreated in RecyclerView, causing poor performanceAndroid - 在 RecyclerView 中重新创建自定义动态生成的复合视图,导致性能不佳
【发布时间】:2015-11-15 15:55:18
【问题描述】:

我正在使用自定义 CompoundView 扩展 LinearLayout 来显示 RecyclerView 的项目。每个项目显示一篇包含多个段落和图像的文章。

CompoundView根据CompoundView.setData(List<DataPiece> pieces)附加的数据动态添加TextViewImageView,附加数据前未知数量。

每个DataPiece 对象告诉CompoundView 它是一段文字还是一张图片。这是CompoundView.setData(List<DataPiece> pieces)的代码:

public void setData(List<DataPiece> pieces) {
    removeAllViews();
    for (DataPiece dataPiece : pieces) {
        switch (dataPiece.getType()) {
            case IMAGE:
                ImageView imageView = new ImageView(getContext());
                ...
                addView(imageView);
                break;
            case TEXT:
                TextView textView = new TextView(getContext());
                ...
                addView(textView);
                break;
        }
    }
}

RecyclerView.Adapter.onBindViewHolder() 中,数据通过调用MyViewHolder.compoundView.setData(...) 附加到CompoundView。创建 RecyclerView 时它工作正常。

但是,对于具有多个 ImageViews 和 TextViews 的 CompoundView 项目,当我滚动离开它然后向后滚动时,滚动变得非常不流畅。

我猜是因为setData()中的removeAllViews()被调用了,而CompoundView创建for循环又被回收器执行了。但我不知道如何避免这种情况。

而且我还想知道为什么在RecyclerView 中使用TextView(with Images) 时滚动总是很平滑,即使它也被回收了。

提前致谢!

【问题讨论】:

  • 如果您将自定义/复合 View 仅用于要创建的 RecyclerView 列表,则最好为一个列表项创建 XML 布局,然后使用 @ 987654346@ 的内置方法可将数据添加到列表项中的Views。
  • @FarbodSalamat-Zadeh 谢谢!但是TextViews 和ImageView 的数量在附加数据之前是不确定的。所以我必须在一个项目中动态添加Views。

标签: android performance android-layout android-view android-recyclerview


【解决方案1】:

在决定最佳方法时可能需要考虑多种因素。

首先,您知道回收商列表中的最大物品数量吗?如果只是少数,也许您可​​以放弃 RecyclerView 方法,只需将您的 CompoundView 添加到由 ScrollView 托管的容器中。

其次 - 每个项目的布局是否相当复杂(也就是其中有很多 TextViewsImageViews 等)?如果是,也许您可​​以采用类似于ExpandableListView 的方法 - 将摘要显示为每个列表项,并在单击时展开到项目的完整布局。

第三 - 如果以上都不可接受并且您仍想采用当前方法 - 不要在绑定方法中构造/添加您的视图。在onCreateViewHolder 中执行此操作,当系统希望您构建视图时(我不确定,但当您在onBindViewHolder 上调用时,您的视图可能已经添加到层次结构和任何层次结构中对其容器的更改会对其容器产生连锁反应 - 但不要相信我的话,我实际上并不知道视图已经添加,这只是一个假设)。您必须为每个项目分配不同的类型,以便在onCreateViewHolder 中您可以将视图类型与支持数据匹配(用于添加相应数量的子视图);每次都从头开始创建视图 - 这样您就不需要调用removeAllViews。类似的东西(我省略了与案例无关的适配器部分):

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    ArrayList<DataPiecesList> mItems;
    public RecyclerViewAdapter(ArrayList<DataPiecesList> items) {
        mItems = items;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        CompoundView compoundView = new CompoundView();
        List<DataPiece> dataPieces = mItems.get(viewType);
        for (int i = 0; i < dataPieces.size(); i++)
        {
            // construct TextView or ImageView or whatever
            compoundView.add(child);
        }
        MyViewHolder view = new MyViewHolder(compoundView);
        return view;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) {
        CompoundView compoundView = viewHolder.itemView;
        DataPiece dataPiece = mItems.get(i);
        for (int j = 0; j < compoundView.getChildCount(); j++)
        {
            compoundView.getChildAt(j) <- dataPiece.get(j);
        }
    }

    @Override
    public int getItemViewType(int position) {
        return position;
    }

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

    public class MyViewHolder extends RecyclerView.ViewHolder {
        ...
        public MyViewHolder(View itemView) {
            super(itemView);
        }
    }
}

【讨论】:

  • 非常感谢!第三种方式有效。用位置替换 ViewType 并通过getChildAt() 查找现有子项的想法非常棒。
【解决方案2】:

RecyclerView 应该重用视图。如果你扔掉已经创建的 TextView / ImageView 对象并每次都创建新对象会很慢。

听起来您需要具有多种视图类型的RecyclerView。这个想法是创建多个视图持有者——其中一些使用ImageView,其他使用TextView。您必须覆盖适配器的 getItemViewType(int position) 方法 - 它应该为 IMAGE 项和 TEXT 项返回不同的值。 onCreateViewHolder(ViewGroup parent, int viewType) 接收一个 viewType 参数,因此您知道要在那里创建哪种类型的 ViewHolder。在onBindViewHolder(VH holder, int position) 中,您可以假设传递给您的持有人是正确的类型(即带有TextView 的类型用于TEXT 项,带有ImageView 的类型用于IMAGE 项),因此无需删除其子项视图并再次创建它们。

有一篇关于 RecyclerView's Adapters with multiple view types here 的好文章。

【讨论】:

  • 谢谢!但是我想在每一项中显示一个类似文章的对象,每篇文章可能包含几个段落和图像。所以我认为很难计算具有不同viewTypes的项目的位置。在我的情况下,TextViews 和 ImageViews 在逻辑上应该被视为一个单独的项目。
  • 这里的超链接错了,请修改一下好吗?谢谢 :)
  • 哦,感谢您注意到断开的链接 - 我已修复它。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-06
  • 1970-01-01
  • 2021-11-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多