Search code examples
androidandroid-studiokotlinandroid-buildkotlin-android

Could someone take a look at my project and tell me why it errors before running?


Hi and thanks for reading in advance, I have built a small project testing an adapter to populate a main activity view with a custom xml template [this template holds some fields that are populated by some simple test data and images inside the project] The build is fine afaik, I have debugged it as best I can to yield no errors and I cant actually find the error as i follow the build in debug so it's likely my noob-like experience with kotlin, android studio and maybe the later development approach changes. My best guess after following it through a few (way more ha) times, at this stage it seemed to be going wrong during the android part of the build versus any prep and adapter code so it could be some settings I havent invoked maybe... Also probably importantly: I saw it pull in the recyclerView during debug(prior to any customisation from the imported xml layout im using), the visual then disappears, but i do see all the code being populated by the functions ive created in MainActivity and the adapter does seem to return to MainActivity with the respective data constructed. I enclose the project zipped as there is no discernable error code or post runtime exit code that i can find [thats not to say there isn't one, I'm newoob to the IDE :( ] I'm targeting an Android 7.0 600*1024 mdpi using API 24 on a 7" (LH flip)portrait orientation x86 emulator

I'm using Android Studio 2021.1.1 patch 3 if that's important to know too

Thanks again for anyone who can take a peek at it to help me along, im proper stuck, been so for couple days now, thinking of a complete redo again :/ :) I enclose a link to the zipped file below [due to no errors in debug to provide here]

[Edit: Cripes I forgot to mention I'm also using CircleImageViewer, not sure if that causing an issue but it doesn't appear to be ]

[Final edit: As can be seen below this was answered by Tyler V who understood my problem better than I could pose the question - please see below for a complete resolution/ refactor of code by them that works correctly - thanks again to Tyler V]

project on googledrive


Solution

  • I took a look at you app, the error it is throwing (look in the Logcat tab!) is this

    Process: com.example.kayakthing, PID: 4289
    java.lang.IndexOutOfBoundsException: Index: 5, Size: 5
        at java.util.ArrayList.get(ArrayList.java:411)
        at com.example.kayakthing.HiresAdapter.onBindViewHolder(HiresAdapter.kt:34)
        at com.example.kayakthing.HiresAdapter.onBindViewHolder(HiresAdapter.kt:11)
    

    Problem

    The reason it is throwing this is because you are supplying different length arrays to your adapter. Take a look at the adapter code below - in your current code the lengths of nameList and clientDetailsList are both 7 but the length of hireImageList is 5. Since you define getItemCount based on the length of nameList - the adapter crashes when trying to show values for positions past the end of the hireImageList size.

    override fun onBindViewHolder(holder: HiresViewHolder, position: Int) {
        holder.tvClientName.text = nameList[position]
        holder.tvClientDetails.text = clientDetailsList[position]
        holder.imageView.setImageResource(hireImageList[position])
    }
    
    override fun getItemCount(): Int {
        return nameList.size
    }
    

    Solution

    I recommend you 1) learn to read the Logcat tab to find errors, and 2) define a data class to hold the three items you need in your adapter, so you can pass in a single list of that data class instead of three lists. That way there is no way to pass in lists with mis-matched lengths.

    For example, use something like this and have the adapter take a single List<HiresAdapter.Data> instead of three lists.

    class HiresAdapter(
        private var adapterData: ArrayList<Data>,
        private var context: Context) : RecyclerView.Adapter<HiresAdapter.HiresViewHolder>() {
    
        // Define a data class in the adapter to hold the data
        // needed for each row
        data class Data(val name: String, val details: String, val image: Int)
        
        class HiresViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
            var tvClientName : TextView = itemView.findViewById(R.id.tvClientName)
            var tvClientDetails : TextView = itemView.findViewById(R.id.tvClientDetail)
            var imageView : CircleImageView = itemView.findViewById(R.id.hireImageView)
        }
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) : HiresViewHolder {
            this.context = parent.context
            val view : View = LayoutInflater.from(this.context).inflate(R.layout.rv_card_design,parent, false)
            return HiresViewHolder(view)
        }
    
        override fun onBindViewHolder(holder: HiresViewHolder, position: Int) {
            holder.tvClientName.text = adapterData[position].name
            holder.tvClientDetails.text = adapterData[position].details
            holder.imageView.setImageResource(adapterData[position].image)
        }
    
        override fun getItemCount(): Int {
            return adapterData.size
        }
    }
    

    and populate it like this

    val adapterData = ArrayList<HiresAdapter.Data>()
    adapterData.add(HiresAdapter.Data("Geoff",  "Geoff",  R.drawable.solo))
    adapterData.add(HiresAdapter.Data("Frogme", "Frogme", R.drawable.tandem))
    adapterData.add(HiresAdapter.Data("Jenny",  "Jenny",  R.drawable.quattro))
    adapterData.add(HiresAdapter.Data("Benny",  "Benny",  R.drawable.sup))
    adapterData.add(HiresAdapter.Data("Sylvia", "Sylvia", R.drawable.croc))
    
    adapter = HiresAdapter(adapterData, this@MainActivity)