有没有一种方法可以在RecyclerView中使用ViewStub?

6

我是一名新手,正在学习Android开发。我正在制作一个项目,其中的新闻页面需要包含一个模块化的RecyclerView订阅源,显示不同类型问题的答案表单。目前我使用include来实现,并在需要时将表单设置为可见。最近由于添加了更多模块,应用程序开始显著减慢,因此我正在尝试使用ViewStubs进行优化。

这是我的RecyclerView适配器:

public class ReQuestionAdapter extends RecyclerView.Adapter<FeedItem> {
private ArrayList<Question> myQuestions;
public ReQuestionAdapter(Context context, ArrayList<Question> questions) {
    myQuestions = questions ;
}

@Override
public FeedItem onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.list_item_re_question, parent, false);
    return new FeedItem(view);
}

@Override
public void onBindViewHolder(FeedItem holder, int position) {
    Question q = myQuestions.get(position);
    holder.bindQuestion(q);
}

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

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

这是适配器的ViewHolder类:

public class FeedItem extends RecyclerView.ViewHolder{
private Question mQuestion;
public TextView tvName;
public TextView tvTime;
public TextView tvContent;
public ProfilePictureView profilePictureView;
public ViewStub moduleView;
private int moduleType;

public FeedItem(View itemView) {
    super(itemView);

}

public void bindQuestion(Question question) {
    mQuestion = question;
    tvTime = (TextView) itemView.findViewById(R.id.li_q_date);
    tvContent = (TextView) itemView.findViewById(R.id.li_q_content);
    moduleView = (ViewStub) itemView.findViewById(R.id.module_viewstub);
    tvTime.setText(TimeHandler.When(mQuestion.publish_time));
    tvContent.setText(mQuestion.content);
    moduleType = question.type;
        switch (moduleType) {
            case Question.TYPE_YN:
                moduleView.setLayoutResource(R.layout.module_yes_no);
                moduleView.inflate();
                break;
            case Question.TYPE_CUSTOM:
                moduleView.setLayoutResource(R.layout.module_custom);
                moduleView.inflate();
                break;
            default:
                break;
        }
    }
}

现在的问题是,包含特定布局的ViewStub不能用新的布局重新填充,原因是它一离开屏幕就从视图结构中删除了,症状是:当向下滚动RecyclerView时,填充屏幕的前几个列表项工作正常,但是当上一个离开屏幕时需要加载其他列表项时,FeedItem绑定会引发一个NullPointerException(无法在列表项布局中找到它)。
我正在寻找一个和ViewStubs一样高效的解决方案,或者让它们正常工作的方法,因为我的应用程序有很多模块,将它们全部作为不可见的填充每个项目会使应用程序变慢。
1个回答

7
在你的 bindQuestion() 方法中,你引用了两个不同的布局来填充视图,因此实际上你有两种不同的视图类型。
适配器视图内置了一种高效的处理方式来处理这种情况。
首先重写 getItemViewType()。当位置为 module_yes_no 布局时,返回 0。当位置为 module_custom 布局时,返回 1。
然后在 onCreateViewHolder() 中,当 viewType 参数为 0 时,填充一个完整的 list_item_re_question 视图,并包含 module_yes_no 布局。当 viewType == 1 时,填充视图的 module_custom 版本。
现在当你在 onBindViewHolder() 中得到一个视图时,它将已经有了正确的子视图,所以你只需要根据需要填写该视图即可。通过使用 getItemViewType()RecyclerView 正在与你协作,回收你需要的确切视图。
你甚至可以有两个 FeedItem 子类,一个用于 module_yes_no,另一个用于 module_custom,这样在 onBindViewHolder() 中,你只需要检查 ViewHolder 的类并进行相应的分支即可。
这应该有助于提高你的应用程序性能。

1
两个FeedItem类只是为了方便您使用。通过覆盖getItemViewType()RecyclerView知道要回收哪个视图。假设第20项是一个yes_no项目。如果您已经设置getItemViewType()返回yes_no项目的0,则当用户滚动到第20项时,RecyclerView将使用位置== 20调用您的getItemViewType()。它接收到0,并说:“哦,看这里,我有一个类型为0的视图可以回收”,然后将该视图传递给onBindViewHolder()。明白了吗? - kris larson
如果RecyclerView没有可回收的视图,它会调用onCreateViewHolder()并将viewType设置为0,这样你就知道要填充哪种类型的视图和创建哪种类型的视图持有者。 - kris larson
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="15dp" android:background="#ffffff"> <include layout="@layout/list_item_re_question" /> <include layout="@layout/module_yes_no"/> </RelativeLayout> - Or Posener
还有没有更简单的方法来实现这个,比如通过代码实现部分功能(类似于ViewStub)? - Or Posener
1
@AJW是的,你应该覆盖getItemViewType()。这很简单:第1步:覆盖getItemViewType(),使其对于小数据返回0,对于大数据返回1。第2步:在viewType参数上使用switch覆盖onCreateViewHolder():0膨胀小布局,1膨胀展开布局。第3步:在onBindViewHolder()中,您的视图持有人参数将通过第1步和第2步的功劳已经正确地膨胀为您的数据。如果您被卡住了,请发布一个新问题,这样社区就有机会回答。如果你在评论中链接到你的问题,我会看一下它。 - kris larson
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接