0

I have a simple Kotlin class WarningFragment in which I set an adapter for ExpandableListView and pass list headers and details of every list header inside. For the record, here's the code of the class:

class WarningFragment : Fragment() {

    private var _binding: FragmentWarningBinding? = null
    private val binding get() = _binding!!

    private val header: List<String> = WarningData.warningData.keys.toList()
    private val body: List<List<String>> = WarningData.warningData.values.toList()

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = FragmentWarningBinding.inflate(inflater, container, false)
        val root: View = binding.root

        return root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val warningHeader : TextView = binding.warningTextView
        warningHeader.text = WarningData.warningHeader

        val expandableListViewWarning : ExpandableListView = binding.expandableListViewWarning
        expandableListViewWarning.setAdapter(WarningExpandableListAdapter(requireActivity().applicationContext, expandableListViewWarning, header, body))
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

The code of list adapter class:

class WarningExpandableListAdapter(var context: Context,
                                   var expandableListView: ExpandableListView,
                                   var header: List<String>,
                                   var body: List<List<String>>) : BaseExpandableListAdapter() {

    override fun getGroupCount(): Int {
        return header.size
    }

    override fun getChildrenCount(groupPosition: Int): Int {
        return body[groupPosition].size
    }

    override fun getGroup(groupPosition: Int): String {
        return header[groupPosition]
    }

    override fun getChild(groupPosition: Int, childPosition: Int): String {
        return body[groupPosition][childPosition]
    }

    override fun getGroupId(groupPosition: Int): Long {
        return groupPosition.toLong()
    }

    override fun getChildId(groupPosition: Int, childPosition: Int): Long {
        return childPosition.toLong()
    }

    override fun hasStableIds(): Boolean {
        return false
    }

    override fun getGroupView(
        groupPosition: Int,
        isExpanded: Boolean,
        convertView: View?,
        parent: ViewGroup?
    ): View? {
        var convertView = convertView
        if (convertView == null) {
            val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
            convertView = inflater.inflate(R.layout.warning_list_row_group, null)
        }
        val title = convertView?.findViewById<TextView>(R.id.text_view_warning_list_row_group)
        title?.text = getGroup(groupPosition)
        //title?.textColors = R.attr.colorOnSurface
        //title?.text = getGroup(groupPosition)
        title?.setOnClickListener {
            if (expandableListView.isGroupExpanded(groupPosition))
                expandableListView.collapseGroup(groupPosition)
            else
                expandableListView.expandGroup(groupPosition)
            //Toast.makeText(context, getGroup(groupPosition), Toast.LENGTH_SHORT).show()
        }
        return convertView
    }

    override fun getChildView(
        groupPosition: Int,
        childPosition: Int,
        isLastChild: Boolean,
        convertView: View?,
        parent: ViewGroup?
    ): View? {
        var convertView = convertView
        if (convertView == null) {
            val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
            convertView = inflater.inflate(R.layout.warning_list_row_child, null)
        }
        val title = convertView?.findViewById<TextView>(R.id.text_view_warning_list_row_child)
        title?.text = getChild(groupPosition, childPosition)
        //title?.setTextColor(R.attr.colorOnSurface)
        title?.setOnClickListener {
            //Toast.makeText(context, getChild(groupPosition, childPosition), Toast.LENGTH_SHORT).show()
        }
        return convertView
    }

    override fun isChildSelectable(groupPosition: Int, childPosition: Int): Boolean {
        return true
    }
}

Everything worked fine until I've tried to change the color of the TextView in the XML file representing the list header item (group) or details item (child) with this line: android:textColor="?attr/colorOnSurface"

Here's the XML code example of the list header item:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="8dp">

    <TextView
        android:id="@+id/text_view_warning_list_row_group"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingStart="8dp"
        android:paddingTop="8dp"
        android:paddingEnd="8dp"
        android:paddingBottom="8dp"
        android:fontFamily="sans-serif-condensed-medium"
        android:textColor="?attr/colorOnSurface"
        tools:text="ZMIANY W SAMOPOCZUCIU FIZYCZNYM" />

</LinearLayout>

Now, when I launch my app and go to my Warning Fragment the app crashes and throws the android.view.InflateException with "Error inflating class android.widget.TextView" info. The exception stack shows this line (in WarningExpandableListAdapter class) causes the exception:

convertView = inflater.inflate(R.layout.warning_list_row_group, null)

Respectively, if I set the same textColor attribute in XML file representing child, then the bugged line:

convertView = inflater.inflate(R.layout.warning_list_row_child, null)

I've tried to hardcode the textColor attribute to @color/black. In this case the app works well but this text color remains black after swithing to dark mode (I want the color to change in case of changing app theme, that's why i use ?attr/...) I've also tried to set the text color programmatically but I don't know why the TextView color sets to purple, not black or white.

Is there any way to get rid of that problem or changing the text color depending on dark or light mode?

Thank you in advance for any help.

  • `WarningExpandableListAdapter(requireActivity().applicationContext, …)` – Get rid of `applicationContext` there; use just `requireActivity()`. You want to use the `Activity` for the `Context`, because the application `Context` is not going to have the right theme on it. – Mike M. Mar 22 '22 at 16:41
  • @MikeM. And that's all I needed. Thanks a lot, it works! I never supposed that the clue might be hidden in this line. Thank you once again. – Mateusz Pawłowski Mar 22 '22 at 16:51
  • No problem. In the future, you'll probably need to include the complete stack trace for issues like this. The `InflateException` alone doesn't tell us much; just that something went wrong during inflation. The root cause will be much further down. Your description of the issue and your debugging attempts were spot on, though, so I was able to make an educated guess in this case, but oftentimes we'll need more of the trace. Just FYI. Anyhoo, glad you got it working. Cheers! – Mike M. Mar 22 '22 at 16:59

0 Answers0