android - 在 RecyclerView 中的 ViewPager 片段中获取“java.lang.IllegalArgumentException: No view found for id”
问题描述
我得到了例外:
E/MessageQueue-JNI: java.lang.IllegalArgumentException: No view found for id 0x7f0a0554 (ua.com.company.app:id/vpBanners) for fragment BannerItemFragment{30698be} (4c80b228-4303-4c80-b99d-a55b8359b8c2) id=0x7f0a0554}
我的层次结构如下所示:
我的适配器vpHome
:
class HomeViewPagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
private val mFragmentList: MutableList<Fragment> = ArrayList()
private val mFragmentTitleList: MutableList<String> = ArrayList()
override fun getItem(position: Int): Fragment {
return mFragmentList[position]
}
override fun getCount(): Int {
return mFragmentList.size
}
override fun getPageTitle(position: Int): CharSequence? {
return mFragmentTitleList[position]
}
fun addFragment(fragment: Fragment, title: String) {
mFragmentList.add(fragment)
mFragmentTitleList.add(title)
}
}
我以这种方式应用它:
private fun setupViewPager(viewPager: DisableSwipeViewPager) {
vpAdapter = HomeViewPagerAdapter(childFragmentManager).apply {
addFragment(ForYouFragment(), "for you")
addFragment(AnotherFragment1(), "a1")
addFragment(AnotherFragment2(), "a2")
}
viewPager.adapter = vpAdapter
}
接下来,我SnapBannersCarouselViewHolder
要处理ViewPager
里面的物品:
class SnapBannersCarouselViewHolder(
private val mBinding: HomeFragmentItemSnapBannersCarouselBinding
) : RecyclerView.ViewHolder(mBinding.root) {
companion object {
// ...
fun newInstance(
inflater: LayoutInflater,
parent: ViewGroup,
onMoreInteractionListener: ((infoBlockId: String) -> Unit)?
): SnapBannersCarouselViewHolder {
val binding =
HomeFragmentItemSnapBannersCarouselBinding.inflate(inflater, parent, false)
return SnapBannersCarouselViewHolder(
binding,
onMoreInteractionListener
)
}
}
fun bind(item: SnapBannersItem, fragmentManager: FragmentManager) {
// ...
val adapter = BannerPagesAdapter(fragmentManager, item.banners)
with(mBinding.vpBanners) {
// ...
this.adapter = adapter
offscreenPageLimit = 3
}
}
}
我的 RecyclerView 适配器ForYouContentAdapter
:
class ForYouContentAdapter(
var data: List<HomeBaseItem> = emptyList(),
var fragmentManagerRetriever: () -> FragmentManager
) : BaseRecyclerViewAdapter<HomeBaseItem>(data) {
enum class ViewType(val value: Int) {
// ...
SNAP_BANNERS(6)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
// ...
ViewType.SNAP_BANNERS.value -> SnapBannersCarouselViewHolder.newInstance(
mInflater!!,
parent,
onBannersMoreInteractionListener // todo possibly change it
)
else -> throw RuntimeException("Can not create view holder for undefined view type")
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (getItemViewType(position)) {
// ...
ViewType.SNAP_BANNERS.value -> {
val vHolder = holder as SnapBannersCarouselViewHolder
vHolder.bind(
getItem(position) as SnapBannersItem,
fragmentManagerRetriever.invoke()
)
}
}
}
}
我的fragmentManagerRetriever
实现ForYouFragment
看起来像这样:
private val fragmentManagerRetriever: () -> FragmentManager = {
childFragmentManager
}
我的BannerItemFragment
代码:
class BannerItemFragment : Fragment() {
private lateinit var mBinding: HomeFragmentSnapBannerItemBinding
companion object {
// ...
fun newInstance(
item: RectWebViewItem
): BannerItemFragment {
return BannerItemFragment()
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
mBinding = HomeFragmentSnapBannerItemBinding.inflate(inflater, container, false)
return mBinding.root
}
// ...
}
在我需要在我正在使用的其他片段中创建片段的每个地方childFragmentManager
。
当我ForYouFragment
第一次打开时,它可以正常工作。我的项目ViewPager
正常工作。但是,当我在 Activity 的容器中替换片段(添加到后堆栈)打开ForYouFragment
然后返回(HomeFragment
因为ForYouFragment
inside而返回HomeFragment
)时,我收到错误。
为了替换片段,我在里面使用了这个方法ForYouFragment
:
private fun showAnotherFragment() {
ActivityUtils.replaceFragmentToActivity(
requireActivity(),
SomeFragment.newInstance(),
true
)
}
和ActivityUtils
代码:
object ActivityUtils {
fun replaceFragmentToActivity(
activity: FragmentActivity,
fragment: Fragment,
addToBackStack: Boolean
) {
replaceFragmentToActivity(activity, fragment, addToBackStack, containerId = R.id.fragmentContainer)
}
fun replaceFragmentToActivity(
activity: FragmentActivity,
fragment: Fragment,
addToBackStack: Boolean,
containerId: Int
) {
val fragmentManager = activity.supportFragmentManager
val transaction = fragmentManager.beginTransaction()
transaction.replace(containerId, fragment)
if (addToBackStack) {
transaction.addToBackStack(null)
}
transaction.commitAllowingStateLoss()
}
}
请帮我理解为什么我会得到这个异常?
UPD
ViewPager
RecyclerView 内部的适配器:
class BannerPagesAdapter(
fragmentManager: FragmentManager,
var data: List<RectWebViewItem>
) : FragmentStatePagerAdapter(
fragmentManager,
BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT
) {
private var fragments: MutableList<BannerItemFragment> = mutableListOf()
init {
// initial empty fragments
for (i in data.indices) {
fragments.add(BannerItemFragment())
}
}
override fun getItem(position: Int): Fragment {
return BannerItemFragment.newInstance(data[position])
}
override fun getCount(): Int {
return fragments.size
}
override fun instantiateItem(container: ViewGroup, position: Int): Any {
val f = super.instantiateItem(container, position)
fragments[position] = f as BannerItemFragment
return f
}
}
解决方案
解决了
问题是片段希望在附加到其父级ViewPager
之前附加到。这个问题在这里ViewPager
概述。
所以,为了解决这个问题,我创建了自定义ViewPager
:
/**
* Use this ViewPager when you need to place ViewPager inside RecyclerView.
* [LazyViewPager] allows you to set [PagerAdapter] in lazy way. This prevents IllegalStateException
* in case when the fragments want to be attached to the viewpager before the viewpager is
* attached to its parent
*/
class LazyViewPager
@JvmOverloads
constructor(
context: Context,
attrs: AttributeSet? = null
) : ViewPager(context, attrs) {
private var mPagerAdapter: PagerAdapter? = null
override fun onAttachedToWindow() {
super.onAttachedToWindow()
if (mPagerAdapter != null) {
super.setAdapter(mPagerAdapter)
}
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
super.setAdapter(null)
}
@Deprecated("Do not use this method to set adapter. Use setAdapterLazy() instead.")
override fun setAdapter(adapter: PagerAdapter?) {}
fun setAdapterLazy(adapter: PagerAdapter?) {
mPagerAdapter = adapter
}
}
然后,而不是使用setAdapter()
我使用setAdapterLazy()
.
此外,将适配器重置为 null 也很重要onDetachedFromWindow()
。
推荐阅读
- amazon-redshift - 如何使用 AWS DMS to Redshift 识别修改的行?
- javascript - React 如何识别点击项目?
- javascript - res.render 在身份验证后未呈现 user.ejs 页面。res.send 工作但 res.render 不工作
- oracle - PLS-00049:错误的绑定变量“X_VARIABLE”
- go - 在 monorepo 环境中发布 go 模块
- python - Django,如何在 TabularInline 中显示父级的“辅助属性”
- python - 如何将具有匹配名称的 CSV 放在 Python 中的一个工作簿中?
- google-sheets-formula - 是否可以使用谷歌电子表格中的 IMPORTRANGE 公式自动更改单元格范围?
- python - 如何在 dask 数据框中减去两个数据框列?
- elasticsearch - ElasticSearch DSL 中的“+”运算符和“&”运算符有什么区别?