Android - 在 RecyclerView 中重新创建自定义动态生成的复合视图,导致性能不佳

Android - Custom dynamically-generated compound View recreated in RecyclerView, causing poor performance

我正在使用扩展 LinearLayout 的自定义 CompoundView 来显示 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 时它工作正常。

但是,对于具有多个 ImageViewTextViewCompoundView 项目,当我滚动离开它然后再向后滚动时,滚动变得非常不平滑。

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

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

提前致谢!

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

听起来您需要一个具有多种视图类型的 RecyclerView。这个想法是创建多个视图持有者 - 其中一些具有 ImageView,其他具有 TextView。您必须覆盖 getItemViewType(int position) method of your adapter - it should return different values for the IMAGE items and the TEXT items. The onCreateViewHolder(ViewGroup parent, int viewType) receives a viewType parameter so you know which type of ViewHolder to create there. In the onBindViewHolder(VH holder, int position) 您可以假设传递给您的持有人是正确的类型(即 TEXT 项目的 TextView 类型和 IMAGE 项目的 ImageView 类型), 因此无需删除其子视图并重新创建它们。

有一篇关于具有多种视图类型的 RecyclerView 适配器的好文章 here

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

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

托管的容器中

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

第三 - 如果上述 none 是可以接受的并且你仍然想采用当前方法 - 不要 construct/add 你在绑定方法中的视图。在 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);
        }
    }
}