【问题标题】:ExpandableListView expandable list not expanding when its height is wrap_content, and something is below itExpandableListView 可扩展列表在其高度为 wrap_content 时不扩展,并且在其下方
【发布时间】:2015-06-09 12:22:46
【问题描述】:

这是我的布局代码。当我单击可展开的列表视图时,它不会展开。但是,如果我增加列表的高度,它会扩展。有没有办法通过代码动态增加列表的高度?我可以让它在我的布局中间扩展吗?因为如果我增加高度,它只会在折叠列表中添加不必要的空白。

代码:

<ScrollView 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">

    <RelativeLayout 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"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context="com.pocketcash.pocketcash.HomeActivity">

        <Switch
            android:id="@+id/detailedCashInfoSwitch"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentEnd="true"
            android:layout_alignParentRight="true"
            android:layout_alignParentTop="true"
            android:onClick="cashToggleSwitchClicked"
            android:text="Toggle Detailed Cash Info" />

        <Button
            android:id="@+id/addCashButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/expandableListView2"
            android:layout_centerHorizontal="true"
            android:onClick="launchAddCash"
            android:text="Add cash" />

        <Button
            android:id="@+id/addExpenditureButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/addCashButton"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="25dp"
            android:onClick="launchAddExpenditure"
            android:text="Add expenditure" />

        <TextView
            android:id="@+id/recentLogsText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true"
            android:layout_below="@+id/addExpenditureButton"
            android:layout_marginTop="50dp"
            android:text="Recent Logs:"
            android:textAppearance="?android:attr/textAppearanceMedium" />

        <TextView
            android:id="@+id/logText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true"
            android:layout_below="@+id/recentLogsText"
            android:layout_marginTop="0dp"
            android:text="Small Text"
            android:textAppearance="?android:attr/textAppearanceSmall" />

        <TextView
            android:id="@+id/recentViewAllText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignTop="@+id/recentLogsText"
            android:layout_marginLeft="10dp"
            android:layout_toEndOf="@+id/recentLogsText"
            android:layout_toRightOf="@+id/recentLogsText"
            android:onClick="recentViewLogClicked"
            android:text="(view all)"
            android:textAppearance="?android:attr/textAppearanceMedium" />

        <TextView
            android:id="@+id/addViewAllText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/addCashButton"
            android:layout_centerHorizontal="true"
            android:onClick="addViewLogClicked"
            android:text="(view logs)"
            android:textAppearance="?android:attr/textAppearanceSmall" />

        <TextView
            android:id="@+id/expenseViewAllText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/addExpenditureButton"
            android:layout_centerHorizontal="true"
            android:onClick="expenseViewLogClicked"
            android:text="(view logs)"
            android:textAppearance="?android:attr/textAppearanceSmall" />

        <ImageView
            android:id="@+id/userImage"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true"
            android:layout_below="@+id/detailedCashInfoSwitch"
            android:layout_marginTop="34dp"
            android:background="@drawable/default_user" />

        <TextView
            android:id="@+id/nameText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignTop="@+id/userImage"
            android:layout_marginLeft="25dp"
            android:layout_toEndOf="@+id/userImage"
            android:layout_toRightOf="@+id/userImage"
            android:text="Name"
            android:textAppearance="?android:attr/textAppearanceLarge" />

        <TextView
            android:id="@+id/logoutText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignLeft="@+id/nameText"
            android:layout_alignStart="@+id/nameText"
            android:layout_below="@+id/nameText"
            android:clickable="true"
            android:onClick="logUserOut"
            android:text="logout"
            android:textAppearance="?android:attr/textAppearanceSmall" />

        <ImageButton
            android:id="@+id/settingsButton"
            android:layout_width="25dp"
            android:layout_height="25dp"
            android:layout_alignTop="@+id/nameText"
            android:layout_marginLeft="25dp"
            android:layout_toEndOf="@+id/nameText"
            android:layout_toRightOf="@+id/nameText"
            android:background="@drawable/settingsicon"
            android:onClick="launchSettingsActivity" />

        <fragment
            android:id="@+id/fragment_currentBalanceInfo"
            android:name="com.pocketcash.pocketcash.CurrentBalanceInfoFragment"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/userImage"
            android:layout_centerHorizontal="true"
            tools:layout="@layout/fragment_current_balance_info" />

        <ExpandableListView
            android:id="@+id/expandableListView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/fragment_currentBalanceInfo"
            android:layout_centerHorizontal="true"
            android:clickable="true" />

    </RelativeLayout>
</ScrollView>

【问题讨论】:

    标签: android xml android-studio expandablelistview android-wrap-content


    【解决方案1】:

    经过对该主题的大量研究,我只能得出结论,不能在另一个可滚动视图中声明像可扩展列表视图这样的可滚动对象。 不过,为了在运行时解决这个问题,我稍微调整了我的 java 代码。

        expListView.setOnGroupExpandListener(new ExpandableListView.OnGroupExpandListener() {
            @Override
            public void onGroupExpand(int groupPosition) {
                int height = 0;
                for (int i = 0; i < expListView.getChildCount(); i++) {
                    height += expListView.getChildAt(i).getMeasuredHeight();
                    height += expListView.getDividerHeight();
                }
                expListView.getLayoutParams().height = (height+6)*10;
            }
        });
    
        // Listview Group collapsed listener
        expListView.setOnGroupCollapseListener(new ExpandableListView.OnGroupCollapseListener() {
    
            @Override
            public void onGroupCollapse(int groupPosition) {
                expListView.getLayoutParams().height = 61;
            }
        });
    

    这样我在展开或折叠时动态调整了小部件的高度。它不是一种可靠的方法,它适用于命中和试验,所以我最终不得不将此小部件移动到一个全新的活动中。然而,这确实完美无缺。但如果有人找到更好的答案,请分享。

    【讨论】:

    • 对于折叠它应该是:int height = holder.expandableListView.getHeight(); holder.expandableListView.getLayoutParams().height = height;
    • @CODE1010101010101 是什么holder
    【解决方案2】:

    正如Romain Guy(谷歌工程师致力于UI工具包)在他的帖子中所说的

    通过将宽度设置为wrap_content,您是在告诉 ListView 与其最宽的子视图一样宽。 ListView 因此必须测量它的项目并获取它必须在适配器上调用getView() 的项目。这可能会发生多次,具体取决于布局传递的数量、父布局的行为等。

    因此,如果您将 ListView 的布局宽度或布局高度设置为 wrap_contentListView 将尝试测量附加到它的每个视图 - 这绝对不是您想要的。

    记住:避免始终为ListViewsGridViewsExpandableListView设置wrap_content,有关更多详细信息,请参阅Google I/O video谈论列表视图的世界

    所以将ExpandableListView 改为match_parent

    【讨论】:

    • match_parent 不适用于我的代码,因为它被其他几个似乎阻止其扩展的小部件包围
    • 但是android工程师推荐使用match_parent,否则会导致性能问题。看看这个链接stackoverflow.com/questions/1661293/…
    • 我确定他们这样做了,但这并不能解决我的问题,这就是我所说的!
    • 这个问题的任何成功,我都有同样的问题,在我的 ExpandableList 视图上方我有一些其他视图,它的 layout_height 应该是“wrap_content”。在这种情况下,我只看到第一个展开标题。
    • @JaiSaxena 你能发布你的代码以便我可以更好地帮助你
    【解决方案3】:

    我知道这个问题已经得到解答,但我有另一个解决方案可以适用于更多情况:我通过计算项目数来设置列表视图的初始高度,然后在分组折叠/展开时更新此高度检索展开/折叠组的子项计数。这是一个示例代码:

    int item_size = 50;  // 50px
    int sub_item_size = 40;  // 40px
    expListView.getLayoutParams().height = item_size*myAdapter.getGroupCount();
    expenseListView.setAdapter(myAdapter);
    // ListView Group Expand Listener
    expenseListView.setOnGroupExpandListener(new ExpandableListView.OnGroupExpandListener() {
        @Override
        public void onGroupExpand(int groupPosition) {
            int nb_children = myAdapter.getChildrenCount(groupPosition);
            expenseListView.getLayoutParams().height += sub_item_size*nb_children;
        }
    });
    
    // Listview Group collapsed listener
    expenseListView.setOnGroupCollapseListener(new ExpandableListView.OnGroupCollapseListener() {
        @Override
        public void onGroupCollapse(int groupPosition) {
            int nb_children = myAdapter.getChildrenCount(groupPosition);
            expenseListView.getLayoutParams().height -= sub_item_size*nb_children;
        }
    });
    

    【讨论】:

      【解决方案4】:

      通常我们不能在 Scrollview 中使用 ListView、GridView 或 ExpandableListView。如果需要,则必须为可展开的 ListView 提供固定高度。

      private void setListViewHeight(final ExpandableListView listView, final int groupPosition) {
          final ExpandableListAdapter listAdapter = (ExpandableListAdapter) listView.getExpandableListAdapter();
          int totalHeight = 0;
          // be careful with unspecified width measure spec, it will ignore all layout params and even
          // screen dimensions, so to get correct height, first get maximum width View can use and
          // call measure() this way
          final int desiredWidth = View.MeasureSpec.makeMeasureSpec(listView.getWidth(), View.MeasureSpec.EXACTLY);
          for (int i = 0; i < listAdapter.getGroupCount(); i++) {
              final View groupItem = listAdapter.getGroupView(i, false, null, listView);
              groupItem.measure(desiredWidth, View.MeasureSpec.UNSPECIFIED);
      
              totalHeight += groupItem.getMeasuredHeight();
              // count only expanded other groups or the clicked collapsed one
              if (((listView.isGroupExpanded(i)) && (i != groupPosition))
                      || ((!listView.isGroupExpanded(i)) && (i == groupPosition))) {
                  for (int j = 0, childrenNumber = listAdapter.getChildrenCount(i); j < childrenNumber; j++) {
      
                      final View listItem = listAdapter.getChildView(i, j, j == childrenNumber - 1, null, listView);
                      listItem.measure(desiredWidth, View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
                      totalHeight += listItem.getMeasuredHeight();
                  }
              }
          }
          final ViewGroup.LayoutParams params = listView.getLayoutParams();
          final int height = totalHeight
                  + (listView.getDividerHeight() * (listAdapter.getGroupCount() - 1));
          params.height = height;
          listView.setLayoutParams(params);
          listView.requestLayout();
      }
      

      并将此方法分配给列表组单击:

      expandableListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
          @Override
          public boolean onGroupClick(final ExpandableListView expandableListView, final View view, final int groupPosition, final long id) {
              setListViewHeight(expandableListView, groupPosition);
              return false;
          }
      });
      

      重要提示:虽然这段代码在大多数情况下应该可以工作,但是当子列表项是带有android:layout_height="wrap_content" 的 TextViews 时,我遇到了一些问题,因此它们的高度取决于它们的宽度,这可能与此有关问题:ViewGroup{TextView,...}.getMeasuredHeight gives wrong value is smaller than real height。如果您设法修复它,请写评论。

      【讨论】:

        【解决方案5】:

        不幸的是,似乎没有办法使用wrap_content 作为ExpandableListView 的高度。相反,设置固定高度和android:nestedScrollingEnabled="true" 对我有用。

        【讨论】:

          【解决方案6】:

          我找到了一种更好的解决方案,可以使用以下不太复杂且效率更高的 Kotlin 代码来实现这一现象。

          首先要记住几点:

          1. 使用固定的height Parent 和 Child 视图。
          2. 将 Parent 和 Child 的高度提取到 dimens.xml 文件中。
          3. 在 ExpandableListView 中设置divider='@null'scrollbars="none"groupIndicator="@null"(用于customGroupIndicator,否则忽略)。
          4. 切勿使用 paddingVertical,包括 paddingToppaddingBottom,将它们替换为 layoutMargins 以获得动态高度的视图。

          代码逻辑(对我来说,这个块是名为 initUi(uiObject) 的 ViewModel 函数的一部分)

          //Here `ui` is a DataBinding object.
          //You can use ViewBinding or simple Android findViewById as per your comfort.
          
          val data: List<ModelClass> = listObject
          val context = ui.root.context
          
          ui.expandableList.apply {
              val mAdapter = MyExpandableListAdapter(data, context)
              setAdapter(mAdapter)
          
              //fetching parent height from dimens.xml
              val parentViewHeight = ceil(
                  context.resources.getDimension(R.dimen.layout_menu_parent_height)
              ).toInt()
          
              //fetching child height from dimens.xml
              val childViewHeight = ceil(
                  context.resources.getDimension(R.dimen.layout_menu_child_height)
              ).toInt()
          
              //Setting up the initial height of view when All parents are collapsed
              layoutParams.height = data.size * parentViewHeight
          
              setOnGroupExpandListener { groupPosition ->
                  //Increasing the height for Expanded SubCategory
                  layoutParams.height += mAdapter.getChildrenCount(groupPosition) * childViewHeight
              }
          
              setOnGroupCollapseListener { groupPosition ->
                  //Decreasing the height for Collapsed SubCategory
                  layoutParams.height -= mAdapter.getChildrenCount(groupPosition) * childViewHeight
              }
          }
          

          MyExpandableListAdapter.kt

          class FragmentMenuExpandableListAdapter(
              val data: List<Data>, val context: Context
          ): BaseExpandableListAdapter() {
              //Declaring inflater Once can save a lot of CPU and RAM uses... 
              private val inflater = LayoutInflater.from(context)
          
              override fun getGroupCount(): Int = data.size
          
              override fun getChildrenCount(groupPosition: Int): Int = data[groupPosition].subcategories.size
          
              override fun getGroup(groupPosition: Int): Data = data[groupPosition]
          
              override fun getChild(groupPosition: Int, childPosition: Int) = data[groupPosition].subcategories[childPosition]
          
              override fun getGroupId(groupPosition: Int): Long = groupPosition.toLong()
          
              override fun getChildId(groupPosition: Int, childPosition: Int): Long = "$groupPosition$childPosition0".toLong()
          
              override fun hasStableIds(): Boolean = true
          
              override fun getGroupView(
                  groupPosition: Int,
                  isExpanded: Boolean,
                  convertView: View?,
                  parent: ViewGroup
              ): View {
                  val binding = LayoutMenuParentBinding.inflate(inflater, parent, false)
                  binding.apply {
                      val temp = getGroup(groupPosition)
                      title.text = temp.category_name
          
                      //here I used a custom ImageView as GroupIndicator
                      //So following code chunk is Optional
                      if(temp.subcategories.isEmpty()) dropdownIcon.visibility = View.GONE
                      else dropdownIcon.let { icon ->
                          val view = parent as ExpandableListView
                          icon.setImageResource(
                              if(view.isGroupExpanded(groupPosition)) {
                                  icon.setOnClickListener { view.collapseGroup(groupPosition) }
                                  R.drawable.ic_state_expanded
                              }
                              else {
                                  icon.setOnClickListener {
                                      view.expandGroup(groupPosition)
                                  }
                                  R.drawable.ic_state_collapsed
                              }
                          )
                      }
          
                      root.setOnClickListener {
                          TODO("Do on Parent click")
                      }
                  }
                  return binding.root
              }
          
              override fun getChildView(
                  groupPosition: Int,
                  childPosition: Int,
                  isLastChild: Boolean,
                  convertView: View?,
                  parent: ViewGroup?
              ): View {
                  val binding = LayoutMenuChildBinding.inflate(
                      inflater, parent, false
                  ).apply {
                      val temp = getChild(groupPosition, childPosition)
                      title.text = temp.categoryName
          
                      root.setOnClickListener {
                          TODO("Do on Child click")
                      }
                  }
                  return binding.root
              }
          
              override fun isChildSelectable(groupPosition: Int, childPosition: Int): Boolean = true
          }
          

          【讨论】:

            猜你喜欢
            • 2015-11-26
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-10-18
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2022-09-28
            相关资源
            最近更新 更多