Search code examples
androidkotlinlistviewandroid-fragments

Kotlin: ListVew inside a fragment


i am trying to create a very simple listview inside a fragment but there is something that i am doing wrong and the list view doesnt show on the screen.

this is my fragmentWifi.kt:

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.Button
import android.widget.ListView

// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"

/**
 * A simple [Fragment] subclass.
 * Use the [fragmentWifi.newInstance] factory method to
 * create an instance of this fragment.
 */
class fragmentWifi : Fragment() {
    // TODO: Rename and change types of parameters
    private var param1: String? = null
    private var param2: String? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            param1 = it.getString(ARG_PARAM1)
            param2 = it.getString(ARG_PARAM2)
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_wifi, container, false)

        // Inflate the layout for this fragment
        //return inflater.inflate(android.R.layout.fragment_wifi, container, false)

        val contentView: View = inflater.inflate(R.layout.fragment_wifi, container, false)
        val listView: ListView = contentView.findViewById(R.id.wifiList)
        val buttonScan: Button = contentView.findViewById(R.id.scanBtn)


        // sample data
        val list: MutableList<String> = ArrayList()
        for (i in 0..99) list.add("Item $i")
        //val listAdapter = customAdapter(this, list)
        //val listAdapter = ArrayAdapter(this, R.layout.simple_list_item, list)
        val listAdapter = ArrayAdapter(this.requireContext(), R.layout.simple_list_item, list)
        listView.adapter = listAdapter
        return contentView
    }







    companion object {
        /**
         * Use this factory method to create a new instance of
         * this fragment using the provided parameters.
         *
         * @param param1 Parameter 1.
         * @param param2 Parameter 2.
         * @return A new instance of fragment fragmentwifi.
         */
        // TODO: Rename and change types and number of parameters
        @JvmStatic
        fun newInstance(param1: String, param2: String) =
            fragmentWifi().apply {
                arguments = Bundle().apply {
                    putString(ARG_PARAM1, param1)
                    putString(ARG_PARAM2, param2)
                }
            }
    }
}

this is my fragment_wifi.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:padding="10dp"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ListView
        android:id="@+id/wifiList"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="0.97"/>

    <Button
        android:id="@+id/scanBtn"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_gravity="bottom"
        android:layout_margin="15dp"
        android:background="@android:color/holo_red_light"
        android:text="Scan WiFi networks"/>
</LinearLayout>

and this is R.layout.simple_list_item :

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2006 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/text1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textAppearance="?android:attr/textAppearanceListItemSmall"
    android:gravity="center_vertical"
    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
    android:minHeight="?android:attr/listPreferredItemHeightSmall" />

i think the proble is in the fragmentWifi.kt class, line 55:

val listAdapter = ArrayAdapter(this.requireContext(), R.layout.simple_list_item, list)

am i passing the wrong context? i also tried super.requireContext() but nothing changes. i even have tried creating a custom arrayadapter and the outcome is always the same :c


Solution

  • Just to explain what's going on

        override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View? {
            // Inflate the layout for this fragment
            return inflater.inflate(R.layout.fragment_wifi, container, false)
    
            // ------------ end of method -----------
    
            val contentView: View = inflater.inflate(R.layout.fragment_wifi, container, false)
            val listView: ListView = contentView.findViewById(R.id.wifiList)
            val buttonScan: Button = contentView.findViewById(R.id.scanBtn)
    
    
            // sample data
            val list: MutableList<String> = ArrayList()
            for (i in 0..99) list.add("Item $i")
            //val listAdapter = customAdapter(this, list)
            //val listAdapter = ArrayAdapter(this, R.layout.simple_list_item, list)
            val listAdapter = ArrayAdapter(this.requireContext(), R.layout.simple_list_item, list)
            listView.adapter = listAdapter
            return contentView
        }
    

    See where I've put end of method? You just returned a value, which exits the method. That always runs too (there's no if check that only returns if some condition is met) so there's no way the rest of the code will ever run. And the IDE should be warning you about that, highlighting the rest of the method and saying unreachable code or similar when you hover over it


    But there's another thing to be careful about, which I've seen people run into on here. Say you didn't immediately return that first layout you inflated, so the rest of the method can run - you're still inflating a second layout.

    Not only is that unnecessary, now you have to be careful about which one you're using - if you configure the second but return the first, the one that gets displayed is the one without the list etc. Or if you accidentally configure some stuff on one layout, and other stuff on the other, in the end neither layout is set up right, so whichever you return it'll have problems.

    This kind of problem can arise in a lot of different situations, so it's more about being mindful of what you're doing. Inflating two separate layouts should be a red flag (unless you have a specific, intentional reason to do it of course!) - same as creating two Adapters for the same ListView or RecyclerView, etc. You get the idea - it might be something you do accidentally, but it should look suspicious when you actually go looking for the cause of a problem