Android ListView静态标题显示所有可用项目而不滚动。

39

我试图实现一个布局,但遇到了一些困难:我想要一个列表。这个列表不需要滚动,但应该完全显示出来。但是如果总内容高度比屏幕高,整个页面应该可以滚动(包括列表)。

<ScrollView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="wrap_content"
     >

     <LinearLayout
         xmlns:android="http://schemas.android.com/apk/res/android"
         android:id="@+id/linear_layout"
         android:orientation="vertical"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:layout_weight="1"
         android:background="#ff181818"
         >
           <Textview android:id="@+id/my_text" text="header contents goes here" android:layout_width="fill_parent" android:layout_height="wrap_content"/>
           <Textview android:id="@+id/headertext" text="header contents goes here" android:layout_width="fill_parent" android:layout_height="wrap_content"/>

          <ListView
               android:id="@+id/my_list1"
               android:layout_height="wrap_content"
               android:layout_width="fill_parent"
          /> 
     </LinearLayout> 

</ScrollView>

它只使用屏幕的一小部分(每个列表大约2行),而不是填充可用高度,列表本身可以滚动。我该如何更改布局以始终显示整个列表但使屏幕可滚动?


在另一个论坛上,我发现了这样的评论:“由于ListView已经具有滚动功能。因此,我已经重写了我的自定义列表,使其继承自LinearLayout,在ScrollView中可以正常工作。我不知道这是唯一的方法,但它实现了我目前想要做的事情。”你们能帮我编写上述场景的代码吗? - d-man
请问您能否提供一些代码和XML布局,以实现整个屏幕的滚动?谢谢。 - i_raqz
13个回答

65

我使用的解决方案是用LinearLayout替换ListView。你可以在LinearLayout中创建所有的项,它们都会被显示出来。因此,实际上没有必要使用ListView。

LinearLayout list = (LinearLayout)findViewById(R.id.list_recycled_parts);
for (int i=0; i<products.size(); i++) {
  Product product = products.get(i);
  View vi = inflater.inflate(R.layout.product_item, null);
  list.addView(vi);
}

51
线性布局并非替代品。列表视图具有内置的适配器(用于填充项视图),项之间的分隔符,项目状态和项目单击事件。 - Alex
1
但是我们如何处理OnItemClick? - user1902830
6
此外,ListView 通过重用行视图在性能方面有优势。如果您有大量行,则线性布局将无法工作,滚动会非常卡顿。 - Justin
ListView的好处呢?因为滚动条而全部消失了吗? - Aritz
1
这是一个不好的方法,因为ListView可以使用Adapter,而LinearLayout则不能。 - Fire
显示剩余5条评论

48

正如@Alex在被接受的答案中指出LinearLayout很难替代。我有一个问题,其中LinearLayout不是一个选项,那就是我遇到这个博客的时候。为了参考,我将在此处放置代码。希望它能帮助到某个人!

public class UIUtils {

    /**
     * Sets ListView height dynamically based on the height of the items.
     *
     * @param listView to be resized
     * @return true if the listView is successfully resized, false otherwise
     */
    public static boolean setListViewHeightBasedOnItems(ListView listView) {

        ListAdapter listAdapter = listView.getAdapter();
        if (listAdapter != null) {

            int numberOfItems = listAdapter.getCount();

            // Get total height of all items.
            int totalItemsHeight = 0;
            for (int itemPos = 0; itemPos < numberOfItems; itemPos++) {
                View item = listAdapter.getView(itemPos, null, listView);
                item.measure(0, 0);
                totalItemsHeight += item.getMeasuredHeight();
            }

            // Get total height of all item dividers.
            int totalDividersHeight = listView.getDividerHeight() *
                    (numberOfItems - 1);

            // Set list height.
            ViewGroup.LayoutParams params = listView.getLayoutParams();
            params.height = totalItemsHeight + totalDividersHeight;
            listView.setLayoutParams(params);
            listView.requestLayout();

            return true;

        } else {
            return false;
        }

    }
}

使用方法:

//initializing the adapter
listView.setAdapter(adapter);
UIUtils.setListViewHeightBasedOnItems(listView);

//whenever the data changes
adapter.notifyDataSetChanged();
UIUtils.setListViewHeightBasedOnItems(listView);

这个问题的最佳解决方案 :) - AITAALI_ABDERRAHMANE
2
运行得很好,但是 UIUtils.setListViewHeightBasedOnItems(adapter); 不应该是 UIUtils.setListViewHeightBasedOnItems(listView); 吗? - Hibbem
看起来可以工作。我认为这与https://dev59.com/4Gct5IYBdhLWcg3wQ7bP#27818661基本相同。 - hBrent
哦,太棒了。你真是太厉害了,它按照要求工作了。@d-man 这应该是被采纳的答案。 - Naveed Ahmad
这个很好用!当你使用相对充气布局时需要进行小修改:在测量之前:v.setLayoutParams(new ViewGroup.LayoutParams(0,0)); - Rafael Moreno
显示剩余2条评论

17

你可以自定义自己的列表视图(它可以扩展ListView/ExpandableListView/GridView),并覆盖onMeasure方法。这样,你就永远不需要调用任何函数或其他内容。只需在xml中使用即可。

@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
            MeasureSpec.AT_MOST);
    super.onMeasure(widthMeasureSpec, expandSpec);
}

这是一个更好的答案,因为它是一种可扩展和清晰的方法。 - Gautham C.
看起来非常干净。我想知道这个能否在ScrollView内放置ListView? - anhtuannd
1
@anhtuannd 我还没有尝试过那个... 但唯一改变的是onMeasure方法,ListView的所有滚动方法仍然有效,但没有任何东西可以滚动。因此,如果ListView在ScrollView中,我认为它仍将覆盖这些方法。我会寻找另一种方法来解决这个问题。 - Francisco

13

我的布局中有一个ListView,但我想使用一个库,它不能处理这里的ListView,因为它将其包装在ScrollView中。对我来说,最好的解决方案基于Fedor的答案。

既然我已经有了ListViewArrayAdapter,我想重复使用它:

LinearLayout listViewReplacement = (LinearLayout) findViewById(R.id.listViewReplacement);
NamesRowItemAdapter adapter = new NamesRowItemAdapter(this, namesInList);
for (int i = 0; i < adapter.getCount(); i++) {
    View view = adapter.getView(i, null, listViewReplacement);
    listViewReplacement.addView(view);
}

对我来说这很好用,因为我只需要显示1到5个元素不同的动态数据。我只需要添加自己的分隔符。


1
这是制作包含所有可见项目的静态列表的最佳方法。 - Wackaloon

5
如果仍有人遇到问题,您可以创建customList并添加onMesure()方法,就像我实现的那样:
public class ScrolleDisabledListView extends ListView {

private int mPosition;

public ScrolleDisabledListView(Context context) {
    super(context);
}

public ScrolleDisabledListView(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public ScrolleDisabledListView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    final int actionMasked = ev.getActionMasked() & MotionEvent.ACTION_MASK;

    if (actionMasked == MotionEvent.ACTION_DOWN) {
        // Record the position the list the touch landed on
        mPosition = pointToPosition((int) ev.getX(), (int) ev.getY());
        return super.dispatchTouchEvent(ev);
    }

    if (actionMasked == MotionEvent.ACTION_MOVE) {
        // Ignore move events
        return true;
    }

    if (actionMasked == MotionEvent.ACTION_UP) {
        // Check if we are still within the same view
        if (pointToPosition((int) ev.getX(), (int) ev.getY()) == mPosition) {
            super.dispatchTouchEvent(ev);
        } else {
            // Clear pressed state, cancel the action
            setPressed(false);
            invalidate();
            return true;
        }
    }

    return super.dispatchTouchEvent(ev);
}
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
            MeasureSpec.AT_MOST);
    super.onMeasure(widthMeasureSpec, expandSpec);
}
}

这正是我要找的!我有一个 ListView,它包含 3-4 个项。我不能使用静态元素,因为我需要 ListView 的所有功能,当我将其放在 ScrollView 中时,问题得到了解决! - Геннадий Курбесов

1

我刚刚使用ListView的设置参数完成了它。

public static void setListViewHeightBasedOnChildren(ListView listView) {

    //this comes from value from xml tag of each item
    final int HEIGHT_LARGE=75;
    final int HEIGHT_LARGE=50;
    final int HEIGHT_LARGE=35;
    ViewGroup.LayoutParams params = listView.getLayoutParams();

    int screenSize = getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;

    switch(screenSize) {

    case Configuration.SCREENLAYOUT_SIZE_LARGE:
         params.height =(int) (HEIGHT_LARGE*size);
         break;
    case Configuration.SCREENLAYOUT_SIZE_NORMAL:
         params.height =(int) (HEIGHT_NORMAL*size);
         break;
    case Configuration.SCREENLAYOUT_SIZE_SMALL:
          params.height =(int) (HEIGHT_SMALL*size);
          break;
    }
    listView.setLayoutParams(params);  
}

ListView 有使用 Adapter 的优势。而使用线性布局则无法实现这个方便的特性。 - Ivan
2
你不仅可以使用线性布局,还可以使用ListView。 - Ivan

1
看看这个: ListView忽略wrap_content 对我来说,使用android:layout_height和android:layout_weight解决了这个问题:
<ListView
    android:layout_height="0dp"
    android:layout_weight="1"
    />

1

我没有一个固定的标题,但是借鉴了HussoM的帖子,我能够让它正常工作。在我的情况下,列表中项目的高度是不均匀的,因为每个项目中都有不同长度的文本句子,并且我正在使用wrap_content作为高度和match_parent作为宽度。

public class NonScrollableListView extends ListView {

  public NonScrollableListView(Context context) {
      super(context);
  }

  public NonScrollableListView(Context context, AttributeSet attrs) {
      super(context, attrs);
  }

  public NonScrollableListView(Context context, AttributeSet attrs, int defStyleAttr) {
      super(context, attrs, defStyleAttr);
  }

  public NonScrollableListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
      super(context, attrs, defStyleAttr, defStyleRes);
  }

  /**
   * Measure the height of all the items in the list and set that to be the height of this
   * view, so it appears as full size and doesn't need to scroll.
   * @param widthMeasureSpec
   * @param heightMeasureSpec
   */
  @Override
  public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
      ListAdapter adapter = this.getAdapter();
      if (adapter == null) {
          // we don't have an adapter yet, so probably initializing.
          super.onMeasure(widthMeasureSpec, heightMeasureSpec);
          return;
      }

      int totalHeight = 0;

      // compute the height of all the items
      int itemCount = adapter.getCount();
      for (int index=0; index<itemCount; index++) {
          View item = adapter.getView(index, null, this);
          // set the width so it can figure out the height
          item.measure(widthMeasureSpec, 0);
          totalHeight += item.getMeasuredHeight();
      }

      // add any dividers to the height
      if (this.getDividerHeight() > 0) {
          totalHeight += this.getDividerHeight() * Math.max(0, itemCount - 1);
      }

      // make it so
      this.setMeasuredDimension(widthMeasureSpec,
              MeasureSpec.makeMeasureSpec(totalHeight, MeasureSpec.EXACTLY));
  }

}


0
在我的情况下,我在ScrollView中放置了一个ListView,并且默认情况下scrollview会缩小listview。所以我只需在我的ScrollView中添加这个代码,它对我很有效。
android:fillViewport="true"

0

我很惊讶没有人看到这一点。在同一布局中,您不能有两个滚动条。首先,您需要一个ScrollView,然后您需要一个列表,我敢打赌您正在破坏一些Android的良好实践。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接