RecyclerView.onBindViewHolder 只调用一次

Posted

技术标签:

【中文标题】RecyclerView.onBindViewHolder 只调用一次【英文标题】:RecyclerView.onBindViewHolder called only once 【发布时间】:2016-08-18 19:31:54 【问题描述】:

经过数小时的研究,我终于咨询了官方帮助。

我有一个完美运行的 RecyclerView.Adapter 和 RecyclerView.ViewHolders。但是由于某些我不明白的原因,没有正确调用 RecyclerView.Adapter.onBindViewHolder。

    private class AttendeeAdapter extends RecyclerView.Adapter<AttendeeHolder> 
    /*FIELDS*/
    private List<Attendee> mAttendeeList;

    /*CONSTRUCTORS*/
    public AttendeeAdapter(List<Attendee> attendees) 
        mAttendeeList = attendees;
        //Log.i(TAG, "AttendeeAdapter size: " + getItemCount());
    

根据日志消息(项目按预期计为列表的大小),我假设 AttendeeAdapter 已正确实例化。

所以我希望 onBindViewHolder(VH, int) 方法的调用次数与 List 的大小一样多,但事实并非如此。该方法只调用一次!

    /*METHODS*/
    @Override
    public AttendeeHolder onCreateViewHolder(ViewGroup parent, int viewType) 
        LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
        View itemView = layoutInflater.inflate(R.layout.list_attendee, parent, false);
        return new AttendeeHolder(itemView);
    

    @Override
    public void onBindViewHolder(AttendeeHolder holder, int position) 
        Attendee attendee = mAttendeeList.get(position);
        holder.bindAttendee(attendee, position);

        Log.i(TAG, "Binding ViewHolder #" + position);
        /* Binding ViewHolder #0 and that's it */
    

    @Override
    public int getItemCount() 
        return mAttendeeList.size();
    

我的 AttendeeHolder(扩展 RecyclerView.ViewHolder)如下:

    private class AttendeeHolder extends RecyclerView.ViewHolder 
    /*FIELDS*/
    private EditText mAttendeeNameEditText;
    private Attendee mAttendee;

    /*CONSTRUCTOR*/
    public AttendeeHolder(View itemView) 
        super(itemView);
        mAttendeeNameEditText = (EditText) itemView.findViewById(R.id.edit_text_list_item);
        mAmountEditTextList = new ArrayList<>(eventMaxCount);
      

    /*METHODS*/
    public void bindAttendee(Attendee attendee, final int position) 
        mAttendee = attendee;
        String attendeeName = mAttendee.getName();

        // Set the name to the EditText if a name has already been set
        if (attendeeName != null) 
            mAttendeeNameEditText.setText(attendeeName);
        
    

并在主代码中实现为

List<Attendee> attendees = AttendeeLab.get().getAttendeeList();
     mAttendeeAdapter = new AttendeeAdapter(attendees);
     mAmountRecyclerView.setAdapter(mAttendeeAdapter);

我猜代码可以工作(我想我没有做任何更改),但可能没有正确设置 gradle 依赖项。那是我尝试将 recyclerview-v7:23.3.0 修改为 recyclerview-v7:23.1.0 或其他任何东西的地方(它们都不起作用)。

dependencies 
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.3.0'
compile 'com.android.support:design:23.3.0'
compile 'com.android.support:support-v4:23.3.0'
compile 'com.android.support:recyclerview-v7:23.1.2'

任何帮助或评论将不胜感激。我希望我能在几个小时后告别头痛。

【问题讨论】:

不确定这是否意味着什么 - 但是您如何访问您的 AttendeeHolder,因为它具有私有访问权限并且它不位于您的 AttendeeAdapter ..?此外,对您的build.gradle 中的支持包使用相同的版本。 ViewHolder 和 Adapter 是主代码中的内部类,因此可以访问。 你解决了这个问题! RecyclerViews 使用相同(旧)版本的支持包正常工作,即 v7:23.1.2。很抱歉,我对 Gradle 的工作原理了解甚少,但它应该以这种方式工作吗?如果我坚持将 RecyclerViews 与新的 recyclerview-v7:23.3.0 库一起使用怎么办? 【参考方案1】:

在我的情况下,recyclerview 的单项布局的根布局(约束布局)高度设置为 ma​​tch_parent 而不是 'wrap_content',将其更改为 'wrap_content' 和问题是固定的

<androidx.constraintlayout.widget.ConstraintLayout 
android:layout_
android:layout_>

<androidx.cardview.widget.CardView
    android:layout_
    android:layout_
    tools:ignore="MissingConstraints">

    <TextView
        android:id="@+id/tv_city_name"
        android:layout_
        android:layout_
        android:layout_margin="8dp"
        android:gravity="center"
        android:text="@location.name"
        android:textAppearance="@style/TextAppearance.AppCompat.Medium"
        android:typeface="serif" />

</androidx.cardview.widget.CardView>

【讨论】:

【参考方案2】:

就我而言,我必须覆盖getItemId()。当我向适配器添加不同的项目时,它们具有相同 idonBindViewHolder 在滚动时停止调用。所以,我从

override fun getItemId(position: Int): Long = list[position].id.toLong()

override fun getItemId(position: Int): Long = position.toLong()

适配器还包含以下设置:

override fun getItemCount(): Int = list.size

override fun getItemViewType(position: Int): Int = list[position].viewType

this.setHasStableIds(true)

最好使用带有 unique ids 的集合。

【讨论】:

【参考方案3】:

在我遇到的一个案例中,我的回收站视图只能水平滚动,不能垂直滚动。

【讨论】:

【参考方案4】:

还要检查您是否将 RecyclerView 本身的高度设置为 android:layout_height="wrap_content"android:layout_height="match_parent"。在这两种情况下,它都会调整单个项目的高度,并且看起来只有一个,但您实际上可以滚动列表。而是手动设置 RecyclerView 的高度。

【讨论】:

【参考方案5】:

对于最近遇到此问题并且正在运行支持库的 SDK 28 测试版的用户,请将您的目标 SDK 更改为 27 并使用支持库的 27.1.1 版本(撰写本文时的最新版本)。

我在 28.0.0-alpha3 库上遇到了这个问题,在我的 app.gradle 中将版本更改为 27.1.1,问题就解决了。

implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support:design:27.1.1'
implementation 'com.android.support:support-vector-drawable:27.1.1'
implementation 'com.android.support:recyclerview-v7:27.1.1'

【讨论】:

【参考方案6】:

注意 RecyclerView 循环中的 onBindViewHolder 行为。 RecyclerView.ViewHolder位置0 itemView

android:layout_

占据当前显示的屏幕。 Scroll 和 onBindViewHolder 应该被触发,但要确保 getItemCount 设置正确。

解决方案:

android:layout_

ConstraintLayout 作为膨胀的 itemView 的解决方案:

<?xml version="1.0" encoding="utf-8"?>    
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_
    android:layout_><!-- IMPORTANT -->
    <!-- ... -->
</android.support.constraint.ConstraintLayout>

【讨论】:

【参考方案7】:

问题不在您的代码中。确保将 RecyclerView 子项的 layout_height 设置为 wrap_content

【讨论】:

我同意代码可以正常工作。我检查了子项的layout_height 属性,发现它们都设置为wrap_content。我很确定问题出在库上。 是的,wrap_content 不像 match_parent 那样占据整个屏幕。这种方式 onBind 被称为其他时间更多 子项是什么意思? 子项表示行布局 谢谢!我在这上面浪费了很多时间!【参考方案8】:

对所有支持库使用相同的版本:

dependencies 

compile fileTree(dir: 'libs', include: ['*.jar'])

testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.3.0'
compile 'com.android.support:design:23.3.0'
compile 'com.android.support:support-v4:23.3.0'
compile 'com.android.support:recyclerview-v7:23.3.0' //<<< here

此外,RecyclerView 应与 'com.android.support:design:23.3.0' 包一起添加 - 覆盖类或 Gradle 在构建过程中使用此“重复”包所做的任何魔术都可能导致您的问题。

【讨论】:

不,它不起作用。 onBindViewHolder 只被调用一次。实际上,代码的最后一行就像您建议的那样,但我认为我应该坚持使用我曾经使用的库,所以我在发布时进行了更改。清理和重建项目不会改变结果。

以上是关于RecyclerView.onBindViewHolder 只调用一次的主要内容,如果未能解决你的问题,请参考以下文章