我到处寻找这个问题的解决方案,我能找到的唯一答案似乎是“不要把一个ListView放入一个ScrollView”。不过,我还没有看到任何真正的解释。我能找到的唯一原因似乎是谷歌认为你不应该这么做。我知道,所以我做了。
问题是,如何将ListView放到ScrollView中而不缩到最小高度?
我到处寻找这个问题的解决方案,我能找到的唯一答案似乎是“不要把一个ListView放入一个ScrollView”。不过,我还没有看到任何真正的解释。我能找到的唯一原因似乎是谷歌认为你不应该这么做。我知道,所以我做了。
问题是,如何将ListView放到ScrollView中而不缩到最小高度?
当前回答
在ScrollView中使用ListView有两个问题。
ListView必须完全展开到它的子节点高度。这个ListView解决这个问题:
public class ListViewExpanded extends ListView
{
public ListViewExpanded(Context context, AttributeSet attrs)
{
super(context, attrs);
setDividerHeight(0);
}
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST));
}
}
分隔高度必须为0,在行中使用填充代替。
2- ListView消耗触摸事件,所以ScrollView不能像往常一样滚动。这个ScrollView解决了这个问题:
public class ScrollViewInterceptor extends ScrollView
{
float startY;
public ScrollViewInterceptor(Context context, AttributeSet attrs)
{
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent e)
{
onTouchEvent(e);
if (e.getAction() == MotionEvent.ACTION_DOWN) startY = e.getY();
return (e.getAction() == MotionEvent.ACTION_MOVE) && (Math.abs(startY - e.getY()) > 50);
}
}
这是我发现的最好的方法!
其他回答
这是DougW、Good Guy Greg和Paul的答案组合。我发现当尝试使用自定义列表视图适配器和非标准列表项时,这都是需要的,否则列表视图会导致应用程序崩溃(也会导致Nex的答案崩溃):
public void setListViewHeightBasedOnChildren(ListView listView) {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
return;
}
int totalHeight = listView.getPaddingTop() + listView.getPaddingBottom();
for (int i = 0; i < listAdapter.getCount(); i++) {
View listItem = listAdapter.getView(i, null, listView);
if (listItem instanceof ViewGroup)
listItem.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
listItem.measure(0, 0);
totalHeight += listItem.getMeasuredHeight();
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
listView.setLayoutParams(params);
}
你不应该把ListView放到ScrollView中因为ListView已经是一个ScrollView了。这就像把一个ScrollView放到一个ScrollView中。
你想要完成什么?
ListView实际上已经能够测量自身的高度以显示所有项,但是当您简单地指定wrap_content (measurespect . unspecified)时,它不能这样做。当给出一个带有measurespect . at_most的高度时,它将这样做。有了这些知识,你可以创建一个非常简单的子类来解决这个问题,它比上面发布的任何解决方案都要好得多。您仍然应该对这个子类使用wrap_content。
public class ListViewForEmbeddingInScrollView extends ListView {
public ListViewForEmbeddingInScrollView(Context context) {
super(context);
}
public ListViewForEmbeddingInScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ListViewForEmbeddingInScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 4, MeasureSpec.AT_MOST));
}
}
操纵heightMeasureSpec为AT_MOST,具有非常大的大小(整数。MAX_VALUE >> 4)导致ListView测量它的所有子节点直到给定的(非常大的)高度,并相应地设置它的高度。
这比其他解决方案更好,原因如下:
它可以正确测量所有内容(填充,分隔线) 它在测量过程中测量ListView 由于#2,它正确地处理宽度或项目数量的变化,而不需要任何额外的代码
缺点是,您可能会认为这样做依赖于SDK中未记录的行为,而这些行为可能会更改。另一方面,你可能认为这就是wrap_content与ListView一起工作的方式,而当前的wrap_content行为只是被破坏了。
如果你担心行为可能会在未来发生变化,你应该简单地复制onMeasure函数和相关函数从ListView.java和到你自己的子类,然后使AT_MOST路径通过onMeasure运行为未指定的以及。
顺便说一下,我相信当您处理少量列表项时,这是一种非常有效的方法。与线性布局相比,它可能效率较低,但当项目数量较少时,使用线性布局是不必要的优化,因此也会造成不必要的复杂性。
下面是我的计算列表视图总高度的代码版本。这个对我很有用:
public static void setListViewHeightBasedOnChildren(ListView listView) {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null || listAdapter.getCount() < 2) {
// pre-condition
return;
}
int totalHeight = 0;
int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(BCTDApp.getDisplaySize().width, View.MeasureSpec.AT_MOST);
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
for (int i = 0; i < listAdapter.getCount(); i++) {
View listItem = listAdapter.getView(i, null, listView);
if (listItem instanceof ViewGroup) listItem.setLayoutParams(lp);
listItem.measure(widthMeasureSpec, heightMeasureSpec);
totalHeight += listItem.getMeasuredHeight();
}
totalHeight += listView.getPaddingTop() + listView.getPaddingBottom();
totalHeight += (listView.getDividerHeight() * (listAdapter.getCount() - 1));
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight;
listView.setLayoutParams(params);
listView.requestLayout();
}
这些答案都是错的!! 如果你想把一个列表视图放在滚动视图中,你应该重新考虑你的设计。你试图把一个ScrollView放到一个ScrollView中。干扰列表将损害列表性能。Android就是这样设计的。
如果你真的想让列表和其他元素在同一个滚动中,你所要做的就是在适配器中使用一个简单的switch语句将其他项添加到列表的顶部:
class MyAdapter extends ArrayAdapter{
public MyAdapter(Context context, int resource, List objects) {
super(context, resource, objects);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewItem viewType = getItem(position);
switch(viewType.type){
case TEXTVIEW:
convertView = layouteInflater.inflate(R.layout.textView1, parent, false);
break;
case LISTITEM:
convertView = layouteInflater.inflate(R.layout.listItem, parent, false);
break; }
return convertView;
}
}
列表适配器可以处理所有内容,因为它只呈现可见的内容。