RecyclerView动态添加FrameLayout到Header/Footer后滚动崩溃问题
这个报错java.lang.IllegalArgumentException: No view found for id 0x1 (unknown) for fragment PreviewFragment的核心原因,大概率是动态添加的FrameLayout容器在RecyclerView复用Holder时丢失了正确的ID绑定,或者Fragment在View被回收后仍持有无效的View引用,导致滚动到顶/底(触发Holder复用/回收)时找不到容器。
下面是几个针对性的解决方案:
给动态创建的FrameLayout设置固定唯一ID
你现在动态添加的FrameLayout可能用了系统默认的临时ID(比如0x1),RecyclerView复用Holder时会导致ID冲突或找不到。建议在res/values/ids.xml里定义专属ID:<resources> <item name="preview_fragment_container" type="id"/> </resources>然后在代码创建FrameLayout时绑定这个ID:
FrameLayout frameLayout = new FrameLayout(context); frameLayout.setId(R.id.preview_fragment_container); // 再添加到Header布局中在Holder回收时 detach 对应的Fragment
RecyclerView回收Header/Footer的ViewHolder时,Fragment还可能持有已被销毁的View引用,后续滚动回来时就会找不到容器。重写RecyclerView的onViewRecycled方法,在回收Header Holder时主动detach Fragment:@Override public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder) { super.onViewRecycled(holder); if (holder instanceof HeaderViewHolder) { FragmentManager fm = getSupportFragmentManager(); PreviewFragment fragment = (PreviewFragment) fm.findFragmentById(R.id.preview_fragment_container); if (fragment != null) { fm.beginTransaction().detach(fragment).commitNow(); } } }绑定Holder时避免重复添加Fragment
在onBindViewHolder中先检查Fragment是否已存在,再决定是否添加,防止重复创建导致的容器冲突:@Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { if (position == 0) { // 定位到Header位置 HeaderViewHolder headerHolder = (HeaderViewHolder) holder; Fragment existingFragment = getSupportFragmentManager().findFragmentById(R.id.preview_fragment_container); if (existingFragment == null) { PreviewFragment previewFragment = new PreviewFragment(); getSupportFragmentManager().beginTransaction() .replace(R.id.preview_fragment_container, previewFragment) .commitNow(); } } // 其他Item的绑定逻辑 }设置Header ViewHolder为不可复用(可选)
如果你的Header不需要复用,可以通过重写getItemViewType和onViewRecycled来阻止系统回收Header Holder,从根源避免复用导致的View问题:private static final int TYPE_HEADER = 0; private static final int TYPE_ITEM = 1; @Override public int getItemViewType(int position) { return position == 0 ? TYPE_HEADER : TYPE_ITEM; } @Override public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder) { // 只回收普通Item的Holder,跳过Header if (holder.getItemViewType() != TYPE_HEADER) { super.onViewRecycled(holder); } }
按照上面的步骤调整后,应该能解决滚动到顶/底时的崩溃问题。
内容的提问来源于stack exchange,提问作者Ashlok

