在 Kotlin 中使用 Android 数据绑定初始化 RecyclerView 会引发错误。我究竟做错了什么?
Initializing a RecyclerView with Android Data Binding in Kotlin is throwing an error. What am I doing wrong?
描述
我正在尝试使用 android 数据绑定在 Kotlin 中使用 RecyclerView 设置一个 Android 屏幕。我有一个包含片段的 activity,片段包含我使用数据绑定初始化的 RecyclerView。
问题是,当我尝试设置回收器视图的布局管理器时,应用程序抛出 IllegalStateException 并抱怨指定的子项(我假设这意味着 RecyclerView 或 LayoutManager)已经有一个父项,而我需要 运行 removeView()。我尝试从 RecyclerView 中删除所有视图,但无济于事。
这是我的 activity:
class HomeActivity : BaseActivity() {
private lateinit var mBinding: ActivityHomeBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_home)
if(savedInstanceState != null){
return
}
val fragment = HomeFragment.newInstance()
supportFragmentManager.beginTransaction().add(R.id.home_fragment_container, fragment).commit()
}
}
这是我的片段:
class HomeFragment : BaseFragment() {
private lateinit var mBinding: FragmentHomeBinding
private lateinit var mRecyclerView: RecyclerView
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val testList = listOf("TEST TITLE")
mBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_home, container, false)
mRecyclerView = mBinding.homeRecyclerview
mRecyclerView.adapter = HomeOptionsAdapter(testList, View.OnClickListener { Log.d("[onCreateView]:", "I've been clicked") })
mRecyclerView.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
return mBinding.root
}
companion object {
fun newInstance(): HomeFragment {
val fragment = HomeFragment()
return fragment
}
}
}
这是我的片段布局:
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="joseph.kinler.mtgutility.fragments.HomeFragment">
<android.support.v7.widget.RecyclerView
android:id="@+id/home_recyclerview"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
</layout>
当我尝试 运行 时,当我尝试设置 LayoutManager 时,我的 recyclerview 出现错误:
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
所以我的问题是:我做错了什么?我是否错误地使用了数据绑定? Kotlin 只是不能很好地处理数据绑定吗?如有任何帮助或反馈,我们将不胜感激。
编辑:
这是错误的完整堆栈跟踪:
06-14 21:34:44.876 3631-3631/com.kinler.mtgutility E/AndroidRuntime:
FATAL EXCEPTION: main
Process: com.kinler.mtgutility, PID: 3631
java.lang.IllegalStateException: The specified child already has a
parent. You must call removeView() on the child's parent first.
at android.view.ViewGroup.addViewInner(ViewGroup.java:4885)
at android.view.ViewGroup.addView(ViewGroup.java:4716)
at android.view.ViewGroup.addView(ViewGroup.java:4656)
at
android.support.v7.widget.RecyclerView.addView(RecyclerView.java:751)
at android.support.v7.widget.ChildHelper.addView(ChildHelper.java:107)
at
android.support.v7.widget.RecyclerView$LayoutManager.addViewInt(RecyclerView.java:7995)
at
android.support.v7.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:7953)
at
android.support.v7.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:7941)
at
android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1552)
at
android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1498)
at
android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:591)
at
android.support.v7.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3691)
at
android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:3408)
at
android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:3960)
at android.view.View.layout(View.java:19393)
at android.view.ViewGroup.layout(ViewGroup.java:6022)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
at android.view.View.layout(View.java:19393)
at android.view.ViewGroup.layout(ViewGroup.java:6022)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
at android.view.View.layout(View.java:19393)
at android.view.ViewGroup.layout(ViewGroup.java:6022)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1791)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1635)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1544)
at android.view.View.layout(View.java:19393)
at android.view.ViewGroup.layout(ViewGroup.java:6022)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
at android.view.View.layout(View.java:19393)
at android.view.ViewGroup.layout(ViewGroup.java:6022)
at
com.android.internal.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:493)
at android.view.View.layout(View.java:19393)
at android.view.ViewGroup.layout(ViewGroup.java:6022)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
at com.android.internal.policy.DecorView.onLayout(DecorView.java:736)
at android.view.View.layout(View.java:19393)
at android.view.ViewGroup.layout(ViewGroup.java:6022)
at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2480)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2199)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1385)
at
android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6722)
at
android.view.Choreographer$CallbackRecord.run(Choreographer.java:886)
at android.view.Choreographer.doCallbacks(Choreographer.java:698)
at android.view.Choreographer.doFrame(Choreographer.java:633)
at
android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:872)
at android.os.Handler.handleCallback(Handler.java:769)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6540)
at java.lang.reflect.Method.invoke(Native Method)
at
com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
编辑 2:
昨晚我用日志做了更多调试,看来我的 recyclerView 正在正确初始化,适配器正在 运行ning,它会创建一个 ViewHolder 并在抛出错误之前绑定它。我仍然不确定确切的错误发生在哪里,但我计划在我的应用程序流程中的更多位置添加日志语句以找出问题发生的位置。
编辑 3:
这是我的适配器代码:
class HomeOptionsAdapter(val items:List<String>, val itemListener: View.OnClickListener): RecyclerView.Adapter<HomeCardViewHolder>() {
private val TAG = HomeOptionsAdapter::class.java.simpleName
override fun onBindViewHolder(viewHolder: HomeCardViewHolder, position: Int) = viewHolder.bind(items[position], itemListener)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeCardViewHolder {
Log.d(TAG, "[onCreateViewHolder]")
val inflater = LayoutInflater.from(parent.context)
val binding = HomeCardBinding.inflate(inflater, parent, false)
return HomeCardViewHolder(binding.homeCardTitle, binding)
}
override fun getItemCount(): Int = items.size
}
还有我的 View Holder 代码:
class HomeCardViewHolder constructor(itemView: View, binding: HomeCardBinding): RecyclerView.ViewHolder(itemView) {
private val TAG = HomeCardViewHolder::class.java.simpleName
private var mBinding: HomeCardBinding
init {
mBinding = binding
}
fun bind(title: String, listener: View.OnClickListener) {
Log.d(TAG, "[bind]")
mBinding.homeCardTitle.text = title
Log.d(TAG, title)
mBinding.homeCardTitle.setOnClickListener(listener)
}
}
您正在使用 HomeCardViewHolder(binding.homeCardTitle, binding)
创建视图容器。 itemView
参数应该是根视图。从名称来看,homeCardTitle
可能是绑定本身层次结构中的一个视图。因此它已经有一个父对象,不能添加到回收器视图中。
因此,改为使用 HomeCardViewHolder(binding.root, binding)
.
实例化视图持有者
或者只为视图持有者单独提供绑定。
class HomeCardViewHolder constructor(val binding: HomeCardBinding): RecyclerView.ViewHolder(binding.root) {
private val TAG = HomeCardViewHolder::class.java.simpleName
fun bind(title: String, listener: View.OnClickListener) {
Log.d(TAG, "[bind]")
binding.homeCardTitle.text = title
Log.d(TAG, title)
binding.homeCardTitle.setOnClickListener(listener)
}
}
描述
我正在尝试使用 android 数据绑定在 Kotlin 中使用 RecyclerView 设置一个 Android 屏幕。我有一个包含片段的 activity,片段包含我使用数据绑定初始化的 RecyclerView。
问题是,当我尝试设置回收器视图的布局管理器时,应用程序抛出 IllegalStateException 并抱怨指定的子项(我假设这意味着 RecyclerView 或 LayoutManager)已经有一个父项,而我需要 运行 removeView()。我尝试从 RecyclerView 中删除所有视图,但无济于事。
这是我的 activity:
class HomeActivity : BaseActivity() {
private lateinit var mBinding: ActivityHomeBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_home)
if(savedInstanceState != null){
return
}
val fragment = HomeFragment.newInstance()
supportFragmentManager.beginTransaction().add(R.id.home_fragment_container, fragment).commit()
}
}
这是我的片段:
class HomeFragment : BaseFragment() {
private lateinit var mBinding: FragmentHomeBinding
private lateinit var mRecyclerView: RecyclerView
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val testList = listOf("TEST TITLE")
mBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_home, container, false)
mRecyclerView = mBinding.homeRecyclerview
mRecyclerView.adapter = HomeOptionsAdapter(testList, View.OnClickListener { Log.d("[onCreateView]:", "I've been clicked") })
mRecyclerView.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
return mBinding.root
}
companion object {
fun newInstance(): HomeFragment {
val fragment = HomeFragment()
return fragment
}
}
}
这是我的片段布局:
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="joseph.kinler.mtgutility.fragments.HomeFragment">
<android.support.v7.widget.RecyclerView
android:id="@+id/home_recyclerview"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
</layout>
当我尝试 运行 时,当我尝试设置 LayoutManager 时,我的 recyclerview 出现错误:
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
所以我的问题是:我做错了什么?我是否错误地使用了数据绑定? Kotlin 只是不能很好地处理数据绑定吗?如有任何帮助或反馈,我们将不胜感激。
编辑:
这是错误的完整堆栈跟踪:
06-14 21:34:44.876 3631-3631/com.kinler.mtgutility E/AndroidRuntime: FATAL EXCEPTION: main Process: com.kinler.mtgutility, PID: 3631 java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first. at android.view.ViewGroup.addViewInner(ViewGroup.java:4885) at android.view.ViewGroup.addView(ViewGroup.java:4716) at android.view.ViewGroup.addView(ViewGroup.java:4656) at android.support.v7.widget.RecyclerView.addView(RecyclerView.java:751) at android.support.v7.widget.ChildHelper.addView(ChildHelper.java:107) at android.support.v7.widget.RecyclerView$LayoutManager.addViewInt(RecyclerView.java:7995) at android.support.v7.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:7953) at android.support.v7.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:7941) at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1552) at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1498) at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:591) at android.support.v7.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3691) at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:3408) at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:3960) at android.view.View.layout(View.java:19393) at android.view.ViewGroup.layout(ViewGroup.java:6022) at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323) at android.widget.FrameLayout.onLayout(FrameLayout.java:261) at android.view.View.layout(View.java:19393) at android.view.ViewGroup.layout(ViewGroup.java:6022) at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323) at android.widget.FrameLayout.onLayout(FrameLayout.java:261) at android.view.View.layout(View.java:19393) at android.view.ViewGroup.layout(ViewGroup.java:6022) at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1791) at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1635) at android.widget.LinearLayout.onLayout(LinearLayout.java:1544) at android.view.View.layout(View.java:19393) at android.view.ViewGroup.layout(ViewGroup.java:6022) at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323) at android.widget.FrameLayout.onLayout(FrameLayout.java:261) at android.view.View.layout(View.java:19393) at android.view.ViewGroup.layout(ViewGroup.java:6022) at com.android.internal.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:493) at android.view.View.layout(View.java:19393) at android.view.ViewGroup.layout(ViewGroup.java:6022) at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323) at android.widget.FrameLayout.onLayout(FrameLayout.java:261) at com.android.internal.policy.DecorView.onLayout(DecorView.java:736) at android.view.View.layout(View.java:19393) at android.view.ViewGroup.layout(ViewGroup.java:6022) at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2480) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2199) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1385) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6722) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:886) at android.view.Choreographer.doCallbacks(Choreographer.java:698) at android.view.Choreographer.doFrame(Choreographer.java:633) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:872) at android.os.Handler.handleCallback(Handler.java:769) at android.os.Handler.dispatchMessage(Handler.java:98) at android.os.Looper.loop(Looper.java:164) at android.app.ActivityThread.main(ActivityThread.java:6540) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
编辑 2:
昨晚我用日志做了更多调试,看来我的 recyclerView 正在正确初始化,适配器正在 运行ning,它会创建一个 ViewHolder 并在抛出错误之前绑定它。我仍然不确定确切的错误发生在哪里,但我计划在我的应用程序流程中的更多位置添加日志语句以找出问题发生的位置。
编辑 3:
这是我的适配器代码:
class HomeOptionsAdapter(val items:List<String>, val itemListener: View.OnClickListener): RecyclerView.Adapter<HomeCardViewHolder>() {
private val TAG = HomeOptionsAdapter::class.java.simpleName
override fun onBindViewHolder(viewHolder: HomeCardViewHolder, position: Int) = viewHolder.bind(items[position], itemListener)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeCardViewHolder {
Log.d(TAG, "[onCreateViewHolder]")
val inflater = LayoutInflater.from(parent.context)
val binding = HomeCardBinding.inflate(inflater, parent, false)
return HomeCardViewHolder(binding.homeCardTitle, binding)
}
override fun getItemCount(): Int = items.size
}
还有我的 View Holder 代码:
class HomeCardViewHolder constructor(itemView: View, binding: HomeCardBinding): RecyclerView.ViewHolder(itemView) {
private val TAG = HomeCardViewHolder::class.java.simpleName
private var mBinding: HomeCardBinding
init {
mBinding = binding
}
fun bind(title: String, listener: View.OnClickListener) {
Log.d(TAG, "[bind]")
mBinding.homeCardTitle.text = title
Log.d(TAG, title)
mBinding.homeCardTitle.setOnClickListener(listener)
}
}
您正在使用 HomeCardViewHolder(binding.homeCardTitle, binding)
创建视图容器。 itemView
参数应该是根视图。从名称来看,homeCardTitle
可能是绑定本身层次结构中的一个视图。因此它已经有一个父对象,不能添加到回收器视图中。
因此,改为使用 HomeCardViewHolder(binding.root, binding)
.
或者只为视图持有者单独提供绑定。
class HomeCardViewHolder constructor(val binding: HomeCardBinding): RecyclerView.ViewHolder(binding.root) {
private val TAG = HomeCardViewHolder::class.java.simpleName
fun bind(title: String, listener: View.OnClickListener) {
Log.d(TAG, "[bind]")
binding.homeCardTitle.text = title
Log.d(TAG, title)
binding.homeCardTitle.setOnClickListener(listener)
}
}