我继续收到错误消息“指定的孩子已经有一个父母。您必须首先在该孩子的父母上调用removeView()(Android)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我继续收到错误消息“指定的孩子已经有一个父母。您必须首先在该孩子的父母上调用removeView()(Android)相关的知识,希望对你有一定的参考价值。
我正在开发一个日历应用程序,该应用程序在由DayViewFragment夸大的DayView对象上显示日历事件。
这是fragment_day_view布局的摘要:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1">
<com.linkedin.android.tachyon.DayView
android:id="@+id/dayView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/large_padding"
app:dividerHeight="@dimen/divider_height"
app:endHour="@integer/end_hour"
app:eventMargin="@dimen/small_padding"
app:halfHourDividerColor="@color/half_hour_divider"
app:halfHourHeight="@dimen/half_hour_height"
app:hourDividerColor="@color/hour_divider"
app:hourLabelMarginEnd="@dimen/large_padding"
app:hourLabelWidth="@dimen/hour_label_width"
app:startHour="@integer/start_hour"
/>
</ScrollView>
下面是使用上述布局的DayViewFragment的内容:
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
mainView = inflater.inflate(R.layout.fragment_day_view, container, false)
//return inflater.inflate(R.layout.fragment_day_view, container, false)
return mainView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initView()
onDayChange()
}
方法initView()和onDayChange()用于从xml布局初始化我的视图,并在发生数据更改时更新视图的内容。
下面是initView()和onDayChang()的代码片段:
private fun initView() {
binding = FragmentDayViewBinding.bind(mainView)
scrollView = binding.scrollView
dayView = binding.dayView
// Inflate the layout for this fragment
with(day){
set(Calendar.HOUR_OF_DAY, 0)
set(Calendar.MINUTE, 0)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}
val config = Config.newInstance(this.requireContext())
dateFormatter = config.dateFormat
timeFormatter = config.timeFormat
val hour = day.clone() as Calendar
val hourLabelViews = mutableListOf<View>()
for (i in dayView.startHour.rangeTo(dayView.endHour)){
hour.set(Calendar.HOUR_OF_DAY, i)
val hourLabelView: TextView = layoutInflater.inflate(R.layout.hour_label, dayView, false) as TextView
hourLabelView.text = timeFormatter.format(hour.time)
hourLabelViews.add(hourLabelView)
Log.d(TAG, "{hourLabelView at $i is: ${hourLabelView.text}}")
}
Log.d(TAG, "hourLabelViews has size: ${hourLabelViews.size}")
dayView.setHourLabelViews(hourLabelViews)
}
private fun onEventsChange() {
// The day view needs a list of event views and a corresponding list of event time ranges
val eventViews: MutableList<View> = ArrayList<View>()
val eventTimeRanges: MutableList<DayView.EventTimeRange> = ArrayList<DayView.EventTimeRange>()
//Querying all events for this day needs the day start and end time in millis
//start of this day
val start: Calendar = day.clone() as Calendar
start.apply {
set(Calendar.HOUR_OF_DAY, 0)
set(Calendar.MINUTE, 0)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}
//end of day
val end: Calendar = day.clone() as Calendar
end.apply {
set(Calendar.HOUR_OF_DAY, 23)
set(Calendar.MINUTE, 0)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}
val time1 = day.clone() as Calendar
time1.apply {
set(Calendar.HOUR_OF_DAY, 14)
set(Calendar.MINUTE, 30)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}
val time2 = day.clone() as Calendar
time2.apply {
set(Calendar.HOUR_OF_DAY, 15)
set(Calendar.MINUTE, 30)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}
val time3 = day.clone() as Calendar
time3.apply {
set(Calendar.HOUR_OF_DAY, 10)
set(Calendar.MINUTE, 0)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}
val time4 = day.clone() as Calendar
time4.apply {
set(Calendar.HOUR_OF_DAY, 11)
set(Calendar.MINUTE, 30)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}
/** Dummy events start and end days represented in milliseconds **/
val startHour = time1.get(Calendar.HOUR_OF_DAY)
val startMinute = time1.get(Calendar.MINUTE)
val totalStartMinute = time1.get(Calendar.HOUR_OF_DAY).times(60) .plus(time1.get(Calendar.MINUTE)) //870
val totalEndMinute = time2.get(Calendar.HOUR_OF_DAY).times(60).plus(time2.get(Calendar.MINUTE)) //930
val duration = totalEndMinute.minus(totalStartMinute) //60
val totalStartMillis = totalStartMinute.times(60).times(1000).toLong() //522 * 10^5
val totalEndMillis = totalEndMinute.times(60).times(1000).toLong() //558 ^5
Log.d(TAG, "totalStartMinute: $totalStartMinute")
Log.d(TAG, "totalEndMinute: $totalEndMinute")
Log.d(TAG, "duration: $duration")
val startHour1 = time3.get(Calendar.HOUR_OF_DAY) //10
val startMinute1 = time3.get(Calendar.MINUTE) //00
val totalStartMinute1 = time3.get(Calendar.HOUR_OF_DAY).times(60) .plus(time3.get(Calendar.MINUTE)) //600
val totalEndMinute1 = time4.get(Calendar.HOUR_OF_DAY).times(60).plus(time4.get(Calendar.MINUTE)) //690
val duration1 = totalEndMinute1.minus(totalStartMinute1) //90
val totalStartMillis1 = totalStartMinute1.times(60).times(1000).toLong() //360 x 10^5
val totalEndMillis1 = totalEndMinute1.times(60).times(1000).toLong() //414 x 10^5
Log.d(TAG, "totalStartMinute1: $totalStartMinute1")
Log.d(TAG, "totalEndMinute1: $totalEndMinute1")
Log.d(TAG, "duration1: $duration1")
/** start and end day represented in milliseconds **/
val startDayMinute = start[Calendar.HOUR_OF_DAY].times(60).plus(start[Calendar.MINUTE])
val startDayMillis = startDayMinute.times(60).times(1000).toLong() //00
val endDayMinute = end[Calendar.HOUR_OF_DAY].times(60).plus(start[Calendar.MINUTE])
val endDayMillis = endDayMinute.times(60).times(1000).toLong() // 82, 800, 000
Log.d(TAG, "startDayMillis: $startDayMillis ")
Log.d(TAG, "endDayMillis: $endDayMillis ")
/** Format time in milliseconds **/
//val events = arrayListOf<Event>(Event("Money Heist", "Bank of spain", totalStartMillis, totalEndMillis, startHour, startMinute, duration))
// Use the 'by viewModels()' Kotlin property delegate
// from the activity-ktx artifact
val model: SharedViewModel by viewModels()
//Log.d(TAG, "Events has size:${eventsList.size}")
// Log.d(TAG, "Events size ${listEvents.size}")
// for(event in listEvents) {
// Log.d(TAG, "Event Title: ${event.title}") //Go for walk
// Log.d(TAG, "Event Location: ${event.location}") //Neighborhood
// Log.d(TAG, "Event start_time in Millis: ${event.start_time}") //360 x 10^5
// Log.d(TAG, "Event end_time in Millis: ${event.end_time}") //414 x 10^5
// Log.d(TAG, "Event startHour: ${event.start_hour}") //10
// Log.d(TAG, "Event startMinute: ${event.start_minute}") //00
// Log.d(TAG, "Event Duration: ${event.duration}") //90
// }
//Query All Events of today
model.queryAll(startDayMillis, endDayMillis).observe(this, androidx.lifecycle.Observer { events ->
Collections.sort(
events,
Comparator<Event> { e1, e2 ->
if (e1.start_hour < e2.start_hour) -1
else if (e1.start_hour == e2.start_hour)
if (e1.start_minute < e2.start_minute) -1
else if (e1.start_minute == e2.start_minute) 0
else 1 else 1
})
//Reclaim all of the existing views so we can reuse them if needed
val recycled = dayView.removeEventViews()
var remaining: Int = recycled?.size ?: 0
for (event in events) {
// Try to recycle an existing event view if there are enough left, otherwise inflate a new one
val eventView = if (remaining > 0) recycled!![--remaining] else layoutInflater.inflate(
R.layout.event,
dayView,
false)
//When an event is clicked, go to update destination
eventView.run {
findViewById<TextView>(R.id.event_title)?.text = event.title
findViewById<TextView>(R.id.event_location)?.text = event.location
setBackgroundResource(R.color.kk_primary_dark)
setOnClickListener {
val action = DayViewFragmentDirections.actionDayViewDestToUpdateDest(event)
requireActivity().findNavController(R.id.nav_host_fragment).navigate(action)
}
}
eventViews.add(eventView)
// The day view needs the event time ranges in the start minute/end minute format,
// so calculate those here
val startMinute = event.start_minute
val endMinute = event.duration + startMinute
eventTimeRanges.add(EventTimeRange(startMinute, endMinute))
}
// Update the day view with the new events
dayView.setEventViews(eventViews, eventTimeRanges) // <-- Here is where the Logcat points me to!
})
无论该应用程序是第一次运行,都没有问题,但是在再次运行时,它是日志中的崩溃输出:
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:3967)
at android.view.ViewGroup.addView(ViewGroup.java:3817)
at android.view.ViewGroup.addView(ViewGroup.java:3758)
at android.view.ViewGroup.addView(ViewGroup.java:3731)
at com.linkedin.android.tachyon.DayView.setEventViews(DayView.java:222)
at com.project.kamitkalenda.DayViewFragment$onEventsChange$7.onChanged(DayViewFragment.kt:272)
at com.project.kamitkalenda.DayViewFragment$onEventsChange$7.onChanged(DayViewFragment.kt:27)
请提出任何想法?
此行// Try to recycle an existing event view if there are enough left, otherwise inflate a new one
val eventView = if (remaining > 0) recycled!![--remaining] else layoutInflater.inflate(
R.layout.event,
dayView,
false)
在扩展视图时,您将dayView
作为父级传递,这实际上是将视图添加到dayView中。稍后再调用setEventViews()
时,它将尝试在内部将视图添加到其他viewGroup中,从而导致崩溃。我不确定dayView
是什么或布局看起来如何,但为防止崩溃,可以在充气时传递null而不是dayView
。
以上是关于我继续收到错误消息“指定的孩子已经有一个父母。您必须首先在该孩子的父母上调用removeView()(Android)的主要内容,如果未能解决你的问题,请参考以下文章