I'm currently running into an error where my custom calendar view will not run on my fragment layout. For some reason, I run into this InstantiationException error, "java.lang.InstantiationException: The layout involves creation of com.example.road2success.CustomCalendarView over 100 levels deep. Infinite recursion?" And this is what the actually error says:
java.lang.InstantiationException: The layout involves creation of com.example.road2success.CustomCalendarView over 100 levels deep. Infinite recursion?
at org.jetbrains.android.uipreview.ViewLoader.loadClass(ViewLoader.java:181)
at org.jetbrains.android.uipreview.ViewLoader.loadView(ViewLoader.java:144)
at com.android.tools.idea.rendering.LayoutlibCallbackImpl.loadView(LayoutlibCallbackImpl.java:309)
at android.view.BridgeInflater.loadCustomView(BridgeInflater.java:418)
at android.view.BridgeInflater.loadCustomView(BridgeInflater.java:429)
at android.view.BridgeInflater.createViewFromTag(BridgeInflater.java:333)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:730)
at android.view.LayoutInflater.rInflate_Original(LayoutInflater.java:863)
at android.view.LayoutInflater_Delegate.rInflate(LayoutInflater_Delegate.java:72)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:837)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:824)
at android.view.LayoutInflater.inflate(LayoutInflater.java:515)
at android.view.LayoutInflater.inflate(LayoutInflater.java:423)
at com.example.road2success.CustomCalendarView.<init>(CustomCalendarView.kt:44)
at sun.reflect.GeneratedConstructorAccessor130.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.jetbrains.android.uipreview.ViewLoader.createNewInstance(ViewLoader.java:403)
at org.jetbrains.android.uipreview.ViewLoader.loadClass(ViewLoader.java:186)
at org.jetbrains.android.uipreview.ViewLoader.loadView(ViewLoader.java:144)
at com.android.tools.idea.rendering.LayoutlibCallbackImpl.loadView(LayoutlibCallbackImpl.java:309)
at android.view.BridgeInflater.loadCustomView(BridgeInflater.java:418)
at android.view.BridgeInflater.loadCustomView(BridgeInflater.java:429)
at android.view.BridgeInflater.createViewFromTag(BridgeInflater.java:333)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:730)
at android.view.LayoutInflater.rInflate_Original(LayoutInflater.java:863)
at android.view.LayoutInflater_Delegate.rInflate(LayoutInflater_Delegate.java:72)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:837)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:824)
at android.view.LayoutInflater.inflate(LayoutInflater.java:515)
at android.view.LayoutInflater.inflate(LayoutInflater.java:423)
at com.example.road2success.CustomCalendarView.<init>(CustomCalendarView.kt:44)
at sun.reflect.GeneratedConstructorAccessor130.newInstance(Unknown Source)
(couldn't add the rest but it just repeats this over and over)
I've tried many different things to stop the infinite recursion but I'm not sure what is actually causing this, so if anybody could possibly help me figure it out that would be great. Also line 44 in the error statement refers to the view instantiation(var view).
CustomCalendarView.kt
package com.example.road2success
import android.annotation.SuppressLint
import android.app.AlertDialog
import android.content.Context
import android.database.Cursor
import android.database.sqlite.SQLiteDatabase
import android.util.AttributeSet
import android.util.Log.d
import android.view.LayoutInflater
import android.view.View
import android.widget.*
import androidx.annotation.Nullable
import androidx.core.content.getSystemService
import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import java.text.SimpleDateFormat
import java.util.*
import kotlin.collections.ArrayList
class CustomCalendarView: LinearLayout{
constructor(context: Context?):super(context)
constructor(context: Context?, @Nullable attr: AttributeSet?):super(context, attr)
var stuck = 1
private var nextButton: ImageButton? = null
private var prevButton: ImageButton? = null
private var currentDate: TextView? = null
private var grid: GridView? = null
val maxDays = 42
var calendar: Calendar = Calendar.getInstance(Locale.ENGLISH)
var dates: ArrayList<Date> = ArrayList()
var eventList: ArrayList<Events> = ArrayList()
var dateFormat: SimpleDateFormat = SimpleDateFormat("MMMM yyyy", Locale.ENGLISH)
var monthFormat: SimpleDateFormat = SimpleDateFormat("MMMM", Locale.ENGLISH)
var yearFormat: SimpleDateFormat = SimpleDateFormat("yyyy", Locale.ENGLISH)
var eventFormat: SimpleDateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH)
var myGrid: MyGridAdapter? = null
init {
var inflater=
context!!.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
var view= inflater.inflate(R.layout.frag_home, this,true)
nextButton = view.findViewById(R.id.rightButton)
prevButton = view.findViewById(R.id.leftButton)
currentDate = view.findViewById(R.id.dateText)
grid = view.findViewById(R.id.calendar)
prevButton?.setOnClickListener {
calendar.add(Calendar.MONTH, -1)
setUpCalendar()
}
nextButton?.setOnClickListener {
calendar.add(Calendar.MONTH, 1)
setUpCalendar()
}
grid?.setOnItemClickListener { adapterView: AdapterView<*>, view1: View, i: Int, l: Long ->
var builder: AlertDialog.Builder = AlertDialog.Builder(context)
builder.setCancelable(true)
var addView: View =
LayoutInflater.from(context).inflate(R.layout.create_a_journey, null)
var stages: Spinner = addView.findViewById(R.id.interview)
var stageOther: EditText = addView.findViewById(R.id.stageother)
var dText: EditText = addView.findViewById(R.id.dateText)
var day = eventFormat.format(dates.get(i))
var month = monthFormat.format(dates.get(i))
var year = yearFormat.format(dates.get(i))
dText.setText(day + "/" + month + "/" + year)
var cText: EditText = addView.findViewById(R.id.company)
var add: ImageButton = addView.findViewById(R.id.addButton)
var cancel: ImageButton = addView.findViewById(R.id.cancelButton)
var time: TimePicker = addView.findViewById(R.id.timeClock)
var timeSet = EditText(context)
var myAdapter: ArrayAdapter<String> = ArrayAdapter(
context,
android.R.layout.simple_list_item_1,
resources.getStringArray(R.array.stages)
)
myAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
stages.adapter = myAdapter
time.setOnTimeChangedListener { view, hourOfDay, minute ->
timeSet.setText(hourOfDay.toString() + ":" + minute.toString())
}
stages?.setOnClickListener {
stageOther.isVisible = stages?.selectedItem.equals("Other")
}
add?.setOnClickListener {
if (cText.text.isEmpty() || stages.selectedItem.equals(null)) {
Toast.makeText(
this.context,
"WARNING: Please fill in the stage and the company!",
Toast.LENGTH_LONG
).show()
} else {
if (stages?.selectedItem.equals("Other")) {
saveEvent(
cText.toString(),
stageOther.toString(),
timeSet.toString(),
dText.toString(),
month.toString(),
year.toString()
)
} else {
saveEvent(
cText.toString(),
stages.selectedItem.toString(),
timeSet.toString(),
dText.toString(),
month.toString(),
year.toString()
)
}
}
}
cancel?.setOnClickListener {
Toast.makeText(this.context, "Canceled", Toast.LENGTH_LONG).show()
}
builder.setView(addView)
builder.create()
builder.show()
}
grid?.setOnItemLongClickListener { parent, view, position, id ->
var date = eventFormat.format(dates.get(position))
var builder: AlertDialog.Builder = AlertDialog.Builder(context)
builder.setCancelable(true)
var addView: View =
LayoutInflater.from(context).inflate(R.layout.showjourneys, null)
var EventT: RecyclerView = addView.findViewById(R.id.eventsBoard)
var layout: RecyclerView.LayoutManager = LinearLayoutManager(addView.context)
EventT.layoutManager = layout
EventT.setHasFixedSize(true)
var EventTA = EventRecyclerView(CollectJourneys(date))
EventT.adapter = EventTA
EventTA.notifyDataSetChanged()
builder.setView(addView)
builder.create()
builder.show()
return@setOnItemLongClickListener true
}
}
private fun CollectJourneys(date: String?): ArrayList<Events>{
var aList: ArrayList<Events> = ArrayList()
var db = DBHelper(context)
var database: SQLiteDatabase = db.readableDatabase
var cursor: Cursor? = db.ReadEvents(date,database)
while(cursor?.moveToNext()!!){
var company = cursor.getString(cursor.getColumnIndex(DBStructure.company))
var stage = cursor.getString(cursor.getColumnIndex(DBStructure.stage))
var time = cursor.getString(cursor.getColumnIndex(DBStructure.time))
var date = cursor.getString(cursor.getColumnIndex(DBStructure.date))
var month = cursor.getString(cursor.getColumnIndex(DBStructure.month))
var year = cursor.getString(cursor.getColumnIndex(DBStructure.year))
var event = Events(company,stage,time,date,month,year)
aList.add(event)
}
cursor.close()
database.close()
return aList
}
private fun saveEvent(company:String?,stage:String?,time:String?, date:String?, month:String?, year:String?){
var db = DBHelper(context)
var database: SQLiteDatabase = db.writableDatabase
db.saveEvent(company,stage,time,date,month,year,database)
db.close()
Toast.makeText(context,"Event Added", Toast.LENGTH_LONG).show()
}
private fun setUpCalendar() {
var selectedDate = dateFormat.format(calendar.time)
currentDate?.setText(selectedDate)
dates.clear()
var monthCalendar: Calendar = calendar.clone() as Calendar
monthCalendar.set(Calendar.DAY_OF_MONTH,1)
var firstDayOfMonth = monthCalendar.get(Calendar.DAY_OF_WEEK)-1
monthCalendar.add(Calendar.DAY_OF_MONTH,-firstDayOfMonth)
CollectEventsPerMonth(monthFormat.format(calendar.time),yearFormat.format(calendar.time))
while(dates.size < maxDays){
dates.add(monthCalendar.time)
monthCalendar.add(Calendar.DAY_OF_MONTH,1)
}
myGrid = MyGridAdapter(context,dates,calendar,eventList)
grid?.adapter = myGrid
}
private fun CollectEventsPerMonth(month: String?,year: String?){
var db = DBHelper(context)
var database: SQLiteDatabase = db.readableDatabase
var cursor: Cursor? = db.ReadEventsPerMonth(year,month,database)
while(cursor?.moveToNext()!!){
var company = cursor.getString(cursor.getColumnIndex(DBStructure.company))
var stage = cursor.getString(cursor.getColumnIndex(DBStructure.stage))
var time = cursor.getString(cursor.getColumnIndex(DBStructure.time))
var date = cursor.getString(cursor.getColumnIndex(DBStructure.date))
var month = cursor.getString(cursor.getColumnIndex(DBStructure.month))
var year = cursor.getString(cursor.getColumnIndex(DBStructure.year))
var event = Events(company,stage,time,date,month,year)
eventList.add(event)
}
cursor.close()
database.close()
}
}
frag_home.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.road2success.CustomCalendarView
android:id="@+id/customCalView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="28dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
HomeFragment.kt:
package com.example.road2success.home
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import com.example.road2success.CustomCalendarView
import com.example.road2success.R
class HomeFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val root = inflater.inflate(R.layout.frag_home, container, false)
val customCal: CustomCalendarView = root.findViewById(R.id.customCalView)
return root
}
}
MainActivity.kt(if needed):
package com.example.road2success
import android.os.Bundle
import android.view.Menu
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.navigateUp
import androidx.navigation.ui.setupActionBarWithNavController
import androidx.navigation.ui.setupWithNavController
import androidx.drawerlayout.widget.DrawerLayout
import com.google.android.material.navigation.NavigationView
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
class MainActivity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val toolbar: Toolbar = findViewById(R.id.toolBar)
setSupportActionBar(toolbar)
val drawerLayout: DrawerLayout = findViewById(R.id.drawMain)
val navView: NavigationView = findViewById(R.id.NavView)
val navController = findNavController(R.id.nav_host_fragment)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
appBarConfiguration = AppBarConfiguration(
setOf(
R.id.nav_home, R.id.nav_home_day, R.id.nav_home_month,
R.id.nav_home_week, R.id.nav_home_year, R.id.nav_settings,
R.id.nav_stats, R.id.nav_create), drawerLayout
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.nav_host_fragment)
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
}
Again any help is appreciated and this is my first post so sorry for any mistakes.
Your custom View
is inflating a layout that contains an instance of your custom View
. So:
View
(instance #1)View
(instance #2)View
(instance #3)View
(instance #4)Since your layout is named frag_home
, and since it does not contain any of the widgets that your custom View
expects, my guess is that you are inflating the wrong layout.