首页 > 解决方案 > ViewHolder 视图创建时没有附加,但是有错误

问题描述

我从 Firebase Crashlytics 收到了一个错误:


Fatal Exception: java.lang.IllegalStateException: ViewHolder views must not be attached when created. Ensure that you are not passing 'true' to the attachToRoot parameter of LayoutInflater.inflate(..., boolean attachToRoot)
       at androidx.recyclerview.widget.RecyclerView$Adapter.createViewHolder(RecyclerView.java:7080)
       at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6235)
       at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6118)
       at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6114)
       at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2303)
       at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1627)
       at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1587)
       at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:665)
       at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:4134)
       at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:3851)
       at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:4404)
       at android.view.View.layout(View.java:23750)
       at android.view.ViewGroup.layout(ViewGroup.java:7277)
       at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1829)
       at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1673)
       at android.widget.LinearLayout.onLayout(LinearLayout.java:1582)
       at android.view.View.layout(View.java:23750)
       at android.view.ViewGroup.layout(ViewGroup.java:7277)
       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
       at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
       at android.view.View.layout(View.java:23750)
       at android.view.ViewGroup.layout(ViewGroup.java:7277)
       at androidx.constraintlayout.widget.ConstraintLayout.onLayout(ConstraintLayout.java:1855)
       at android.view.View.layout(View.java:23750)
       at android.view.ViewGroup.layout(ViewGroup.java:7277)
       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
       at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
       at android.view.View.layout(View.java:23750)
       at android.view.ViewGroup.layout(ViewGroup.java:7277)
       at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1829)
       at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1673)
       at android.widget.LinearLayout.onLayout(LinearLayout.java:1582)
       at android.view.View.layout(View.java:23750)
       at android.view.ViewGroup.layout(ViewGroup.java:7277)
       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
       at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
       at android.view.View.layout(View.java:23750)
       at android.view.ViewGroup.layout(ViewGroup.java:7277)
       at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1829)
       at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1673)
       at android.widget.LinearLayout.onLayout(LinearLayout.java:1582)
       at android.view.View.layout(View.java:23750)
       at android.view.ViewGroup.layout(ViewGroup.java:7277)
       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
       at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
       at com.android.internal.policy.DecorView.onLayout(DecorView.java:1099)
       at android.view.View.layout(View.java:23750)
       at android.view.ViewGroup.layout(ViewGroup.java:7277)
       at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:3712)
       at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3164)
       at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2225)
       at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:9126)
       at android.view.Choreographer$CallbackRecord.run(Choreographer.java:999)
       at android.view.Choreographer.doCallbacks(Choreographer.java:797)
       at android.view.Choreographer.doFrame(Choreographer.java:732)
       at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:984)
       at android.os.Handler.handleCallback(Handler.java:883)
       at android.os.Handler.dispatchMessage(Handler.java:100)
       at android.os.Looper.loop(Looper.java:237)
       at android.app.ActivityThread.main(ActivityThread.java:8167)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:496)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1100)

有问题的行是:

android.view.View.layout (View.java:23750)

这是:


    public View getRootView() {
            if (mAttachInfo != null) {
                final View v = mAttachInfo.mRootView;
                if (v != null) {
                    return v;
                }
            }
    
            View parent = this;
    
            while (parent.mParent != null && parent.mParent instanceof View) {
                parent = (View) parent.mParent;
            }
    
            return parent;
        }

据我了解,这发生在onCreateViewHolder(). 当我在我的代码中调用这些函数时,我已经仔细检查了 3 次 - 它们都没有参数attachToRoot: true

他们来了:

 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DeviceCellHolder {
        val inflater = LayoutInflater.from(parent.context)
        val v = inflater.inflate(R.layout.item_header, parent, false)
        return DeviceCellHolder(v)
    }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val inflater = LayoutInflater.from(parent.context)
        return deviceHolder?.getViewHolderFor(viewType, inflater, parent)!!
    }
 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoDeviceListViewHolder {
        val inflater = LayoutInflater.from(parent.context)
        val v = inflater.inflate(R.layout.item_find_nothing, parent, false)
        return NoDeviceListViewHolder(v)
    }

我也知道放置null而不是父级(AlertDialog 除外)是一种不好的做法,但是在我的代码onBindHeader布局中,inflater 看起来像这样:

 val v = LayoutInflater.from(holder.itemView.context).inflate(R.layout.item_chart, null, false)

有什么问题?

谢谢!

升级版:

这是返回所需视图持有者的函数:

 open fun getViewHolderFor(
        itemType: Int,
        inflater: LayoutInflater,
        parent: ViewGroup
    ): MtXXXBodyViewHolder {
        return when (itemType) {
            ViewHolderType.DeviceDetailsHeader.typeId -> DeviceDetailsHeaderHolder(inflater, parent)
            ViewHolderType.LineInfo.typeId -> InfoLineViewHolder(inflater, parent)
            ViewHolderType.ItemChart.typeId -> {
                val v = inflater.inflate(R.layout.item_chart, parent, false)
                ChartViewHolder(v)
            }
            ViewHolderType.Item3DObject.typeId -> Gyro3DViewHolder(inflater, parent)
            ViewHolderType.MT_111.typeId -> Mt111BodyViewHolder(inflater, parent)
            else -> InfoLineViewHolder(inflater, parent)
        }
    }

升级版 [1]:

每个 DeviceHolder 如下所示:

class DeviceDetailsHeaderHolder(inflater: LayoutInflater, parent: ViewGroup) :
    MtXXXBodyViewHolder(inflater.inflate(R.layout.item_device_detail_header, parent, false)) {
    val tvDeviceTypeName: TextView = v.tvDeviceTypeName
    val tvDeviceMac: TextView = v.tvDeviceMac
    val ivDeviceImage: ImageView = v.ivDeviceImage
    val ibBack: ImageButton = v.btnCloseDetails
}

标签: androidkotlinandroid-recyclerviewlayout-inflater

解决方案


推荐阅读