首页 > 解决方案 > Kotlin 中嵌套 ExpandableListView 的问题

问题描述

我正在开发一个应用程序,允许人们使用 Google Directions API 计划和跟踪公共交通旅程。我试图显示使用三层嵌套 ExpandableListView 所采取的步骤,但我有这两个问题:

1. 父列表的子视图显示相同的项目(groupPosition 始终为 0)。

2.子列表的子视图根本不显示。

这是我的代码(对于格式很抱歉,这很长):

父列表主视图 (journey_detail_list_view)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/journey_title_text_view"
        android:textSize="40dp"/>
    <ExpandableListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/journey_expandable_list_view"
        android:layout_below="@id/journey_title_text_view"
        />

</LinearLayout>

父列表组视图 (leg_detail_group_view)

<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/leg_list_title_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:padding="20dp"
        android:text="TextView" />
</com.google.android.material.card.MaterialCardView>

父列表子视图/子列表主视图(steps_nested_list_view)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ExpandableListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/steps_nested_expandable_list_view" />

</LinearLayout>

子列表组视图 (steps_nested_group_view)

<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/steps_nested_group_title_tv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="TextView"
        android:padding="20dp"/>
</com.google.android.material.card.MaterialCardView>

子列表子视图(step_detail_view)

<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_height="match_parent"
    android:layout_width="match_parent">
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="20dp">
        <View
            android:id="@+id/step_line_colour_view"
            android:layout_width="10dp"
            android:layout_height="match_parent" />
        <TextView
            android:id="@+id/step_item_tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_toEndOf="@id/step_line_colour_view"
            android:text="TextView" />
    </RelativeLayout>
</com.google.android.material.card.MaterialCardView>

旅程细节片段

class JourneyDetailFragment(var journey: Journey, var act: MainActivity): Fragment() {
    private lateinit var listView: ExpandableListView
    private lateinit var titleTV: TextView
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        //Establish list
        val v = inflater.inflate(R.layout.journey_detail_list_view, container, false)
        listView = v.findViewById(R.id.journey_expandable_list_view)
        titleTV = v.findViewById(R.id.journey_title_text_view)
        val adapter = JourneyLegExpandableListAdapter(act, journey.legs)
        listView.setAdapter(adapter)
        titleTV.text = journey.summary
        return v
    }
}

父级可扩展列表适配器 (JourneyLegExpandableListAdapter)

class JourneyLegExpandableListAdapter(var con: Context, var parentList: ArrayList<Leg>): BaseExpandableListAdapter() {
    override fun getGroupCount(): Int {
        return parentList.size
    }

    override fun getChildrenCount(groupPosition: Int): Int {
        return parentList.get(groupPosition).steps.size
    }

    override fun getGroup(groupPosition: Int): Any {
        return parentList.get(groupPosition)
    }

    override fun getChild(groupPosition: Int, childPosition: Int): Any {
        return parentList.get(groupPosition).steps.get(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 {
        //Create group view
        val v: View
        if(convertView == null)
        {
            v = LayoutInflater.from(con).inflate(R.layout.leg_detail_group_view, parent, false)
        }
        else
        {
            v = convertView
        }
        val tv: TextView = v.findViewById(R.id.leg_list_title_tv)
        val sb: StringBuilder = StringBuilder()
        val leg = parentList.get(groupPosition)
        if (leg.steps.first().startStop == null)
        {
            sb.append(leg.startAddress)
        }
        else
        {
            sb.append(leg.steps.first().startStop!!.name)
        }
        sb.append(" -> ")
        if (leg.steps.last().endStop == null)
        {
            sb.append(leg.endAddress)
        }
        else
        {
            sb.append(leg.steps.last().endStop!!.name)
        }
        tv.text = sb.toString()
        return v
    }

    override fun getChildView(
        groupPosition: Int,
        childPosition: Int,
        isLastChild: Boolean,
        convertView: View?,
        parent: ViewGroup?
    ): View {
        val v: View
        //Create child view
        if (convertView == null)
        {
            v = LayoutInflater.from(con).inflate(R.layout.steps_nested_list_view, parent, false)
        }
        else
        {
            v = convertView
        }
        val childList: ExpandableListView = v.findViewById(R.id.steps_nested_expandable_list_view)
        val adapter = StepsExpandableListAdapter(con, parentList.get(groupPosition).steps)
        childList.setAdapter(adapter)
        return v
    }

    override fun isChildSelectable(groupPosition: Int, childPosition: Int): Boolean {
        return !parentList.get(groupPosition).steps.get(childPosition).subSteps.isNullOrEmpty()
    }
}

子可扩展列表适配器 (StepsExpandableListAdapter)

class StepsExpandableListAdapter(var con: Context, var parentList: ArrayList<Step>): BaseExpandableListAdapter() {
    override fun getGroupCount(): Int {
        return parentList.size
    }

    override fun getChildrenCount(groupPosition: Int): Int {
        return parentList.get(groupPosition).subSteps!!.size
    }

    override fun getGroup(groupPosition: Int): Any {
        return parentList.get(groupPosition)
    }

    override fun getChild(groupPosition: Int, childPosition: Int): Any {
        return parentList.get(groupPosition).subSteps!!.get(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 {
        //Create group view
        val v: View;
        if(convertView == null)
        {
            v = LayoutInflater.from(con).inflate(R.layout.steps_nested_group_view, parent, false)
        }
        else
        {
            v = convertView
        }
        val tv = v.findViewById<TextView>(R.id.steps_nested_group_title_tv)
        tv.text = parentList.get(groupPosition).instructions
        return v;
    }

    override fun getChildView(
        groupPosition: Int,
        childPosition: Int,
        isLastChild: Boolean,
        convertView: View?,
        parent: ViewGroup?
    ): View {

        val v: View
        if(convertView == null)
        {
            v = LayoutInflater.from(con).inflate(R.layout.step_detail_view, parent, false)
        }
        else
        {
            v = convertView
        }
        val subStep = parentList.get(groupPosition).subSteps!!.get(childPosition)
        val stepLineView = v.findViewById<View>(R.id.step_line_colour_view)
        val stepTextView = v.findViewById<TextView>(R.id.step_item_tv)
        stepLineView.setBackgroundColor(Color.parseColor(subStep.line!!.lineColour))
        if(subStep.endStop == null)
        {
            stepTextView.text = subStep.instructions
        }
        else
        {
            stepTextView.text = subStep.endStop!!.name
        }
        return v;
    }

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

旅程对象

class Journey(legs: ArrayList<Leg>, sum: String, f: Fare?, p: Polyline, c: String) {

    var legs: ArrayList<Leg> = legs;
    var summary: String = sum;
    var fare: Fare? = f;
    var polyline: Polyline = p
    var copyright: String = c
}

腿对象

class Leg(di: Distance, at: Date, dt: Date, st: ArrayList<Step>, du: Duration, sl: LatLng, el: LatLng, sa: String, ea: String) {
    var distance: Distance = di;
    var arrivalTime: Date = at;
    var departureTime: Date = dt;
    var steps: ArrayList<Step> = st;
    var duration: Duration = du;
    var startLoc: LatLng = sl;
    var endLoc: LatLng = el;
    var startAddress: String = sa;
    var endAddress: String = ea;

}

步骤对象

class Step (i: String?, di: Distance?, du: Duration?, st: LatLng?, en: LatLng?, su: ArrayList<Step>?, m: String?, es: Stop?, ss: Stop?, ar: Date?, de: Date?, hd: Double?, he: String?, ns: Int?, li: Line?, p: Polyline?){
    var instructions: String? = i;
    var distance: Distance? = di;
    var duration: Duration? = du;
    var startLocation: LatLng? = st;
    var endLocation: LatLng? = en;
    var subSteps: ArrayList<Step>? = su;
    var maneuver: String? = m;
    var endStop: Stop? = es;
    var startStop: Stop? = ss;
    var arrivalTime: Date? = ar;
    var departureTime: Date? = de;
    var headway: Double? = hd;
    var headsign: String? = he;
    var numStops: Int? = ns;
    var line: Line? = li;
    var polyline: Polyline? = p

}

我错过了什么吗?

标签: androidkotlinexpandablelistview

解决方案


我创建了一个自定义列表视图用作子列表视图:

父列表子视图/子列表主视图(steps_nested_list_view):

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

    <NestedExpandableListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/steps_nested_expandable_list_view" />

</LinearLayout>

自定义类的代码:

class NestedExpandableListView(var con: Context, attributeSet: AttributeSet): ExpandableListView(con, attributeSet)
{
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val int = Int.MAX_VALUE.shr(2)
        super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(int, MeasureSpec.AT_MOST))
    }
}

推荐阅读