• 【问题汇总】ScrollView嵌套ListView的问题


    因产品的需求,需要在ScrollView中嵌套ListView来达到效果。众所周知,ScrollVIew和ListView都是可滑动的容器,嵌套使用一定会出现一些问题。
    1. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    2.     android:layout_width="fill_parent"  
    3.     android:layout_height="fill_parent" >  
    4.   
    5.     <ScrollView  
    6.         android:id="@+id/scrollview"  
    7.         android:layout_width="fill_parent"  
    8.         android:layout_height="fill_parent" >  
    9.   
    10.         <ListView  
    11.             android:id="@+id/listview"  
    12.             android:layout_width="fill_parent"  
    13.             android:layout_height="wrap_content" >  
    14.         </ListView>  
    15.     </ScrollView>  
    16.   
    17. </FrameLayout>  
    ScrollView中唯一可存在的子View是一个ListView。但是这么写会出现问题。就是ListView会显示不全,只能显示ListView的部分内容,同时,ListView也是不可滑动的。

    然后我发现了ScrollView有一个属性,叫 android:fillViewport="true",如果设置这个属性为true,那么ListView会全屏显示,但是依旧不可滑动。先看看这个属性是什么意思。
    XML Attributes
    Attribute NameRelated MethodDescription
    android:fillViewportsetFillViewport(boolean)Defines whether the scrollview should stretch its content to fill the viewport. 
    意思大概是,定义scrollview是否把它的内容拉伸去充满整个窗口。
    1. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    2.     android:layout_width="fill_parent"  
    3.     android:layout_height="fill_parent" >  
    4.   
    5.     <ScrollView  
    6.         android:id="@+id/scrollview"  
    7.         android:layout_width="fill_parent"  
    8.         android:layout_height="fill_parent"  
    9.         <span style="color:#ff0000;">android:fillViewport="true"</span> >  
    10.   
    11.         <ListView  
    12.             android:id="@+id/listview"  
    13.             android:layout_width="fill_parent"  
    14.             android:layout_height="wrap_content" >  
    15.         </ListView>  
    16.     </ScrollView>  
    17.   
    18. </FrameLayout>  
    有网友反应上述的属性在他们的测试机上面不起作用。然后搜索了一下stackoverflow.com,国外有一个牛人写了一个方法,用于对Listview重新布局。
    1. public class Utility {  
    2.         public static void setListViewHeightBasedOnChildren(ListView listView) {  
    3.               ListAdapter listAdapter = listView.getAdapter();  
    4.             if (listAdapter == null) {  
    5.             // pre-condition  
    6.                   return;  
    7.             }  
    8.   
    9.             int totalHeight = listView.getPaddingTop() + listView.getPaddingBottom();  
    10.             for (int i = 0; i < listAdapter.getCount(); i++) {  
    11.                  View listItem = listAdapter.getView(i, null, listView);  
    12.                  if (listItem instanceof ViewGroup) {  
    13.                     listItem.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));  
    14.                  }  
    15.                  listItem.measure(00);  
    16.                  totalHeight += listItem.getMeasuredHeight();  
    17.             }  
    18.   
    19.             ViewGroup.LayoutParams params = listView.getLayoutParams();  
    20.             params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));  
    21.                       listView.setLayoutParams(params);  
    22.         }  
    23.      }  
    注意:ListView的item必须是LinearLayout,才能起作用。

    好了,解决了ListView显示不全的问题,下一步要解决的就是ListView滑动的问题了。

    有大神说可以重写ScrollView,拦截相关的点击事件,已达到listview可以滑动的效果。
    1. public class VerticalScrollview extends ScrollView {  
    2.     public VerticalScrollview(Context context) {  
    3.         super(context);  
    4.     }  
    5.   
    6.     public VerticalScrollview(Context context, AttributeSet attrs) {  
    7.         super(context, attrs);  
    8.     }  
    9.   
    10.     public VerticalScrollview(Context context, AttributeSet attrs, int defStyle) {  
    11.         super(context, attrs, defStyle);  
    12.     }  
    13.   
    14.     @Override  
    15.     public boolean onInterceptTouchEvent(MotionEvent ev) {  
    16.         final int action = ev.getAction();  
    17.         switch (action) {  
    18.         case MotionEvent.ACTION_DOWN:  
    19.             Log.i("VerticalScrollview",  
    20.                     "onInterceptTouchEvent: DOWN super false");  
    21.             super.onTouchEvent(ev);  
    22.             break;  
    23.   
    24.         case MotionEvent.ACTION_MOVE:  
    25.             return false// redirect MotionEvents to ourself  
    26.   
    27.         case MotionEvent.ACTION_CANCEL:  
    28.             Log.i("VerticalScrollview",  
    29.                     "onInterceptTouchEvent: CANCEL super false");  
    30.             super.onTouchEvent(ev);  
    31.             break;  
    32.   
    33.         case MotionEvent.ACTION_UP:  
    34.             Log.i("VerticalScrollview""onInterceptTouchEvent: UP super false");  
    35.             return false;  
    36.   
    37.         default:  
    38.             Log.i("VerticalScrollview""onInterceptTouchEvent: " + action);  
    39.             break;  
    40.         }  
    41.   
    42.         return false;  
    43.     }  
    44.   
    45.     @Override  
    46.     public boolean onTouchEvent(MotionEvent ev) {  
    47.         super.onTouchEvent(ev);  
    48.         Log.i("VerticalScrollview""onTouchEvent. action: " + ev.getAction());  
    49.         return true;  
    50.     }  
    51. }  
    以上代码测试成功,在ScrollView中的ListView可以滑动。

    另外,还可以对ListView设置onTouchListener,在onTouch中去控制外层ScrollView是否拦截触摸事件。
    1. mListView.setOnTouchListener(new OnTouchListener() {  
    2.   
    3.     @Override  
    4.     public boolean onTouch(View v, MotionEvent event) {  
    5.         int action = event.getAction();  
    6.         switch (action) {  
    7.         case MotionEvent.ACTION_DOWN:  
    8.             // Disallow ScrollView to intercept touch events.  
    9.             v.getParent().requestDisallowInterceptTouchEvent(true);  
    10.             break;  
    11.   
    12.         case MotionEvent.ACTION_UP:  
    13.             // Allow ScrollView to intercept touch events.  
    14.             v.getParent().requestDisallowInterceptTouchEvent(false);  
    15.             break;  
    16.         }  
    17.   
    18.         // Handle ListView touch events.  
    19.         v.onTouchEvent(event);  
    20.         return true;  
    21.     }  
    22. });  
    以上的所有代码确实解决了ListView滑动的问题。但是,ScrollView的滑动问题没有解决。如果ScrollView中除了ListView还有其他View,那么其他View可能显示不出来。

    可以重写ListView去解决这个问题,这个是我所知道的最好的解决方案。
    1. public class NestedListView extends ListView implements OnTouchListener,  
    2.         OnScrollListener {  
    3.   
    4.     private int listViewTouchAction;  
    5.     private static final int MAXIMUM_LIST_ITEMS_VIEWABLE = 99;  
    6.   
    7.     public NestedListView(Context context, AttributeSet attrs) {  
    8.         super(context, attrs);  
    9.         listViewTouchAction = -1;  
    10.         setOnScrollListener(this);  
    11.         setOnTouchListener(this);  
    12.     }  
    13.   
    14.     @Override  
    15.     public void onScroll(AbsListView view, int firstVisibleItem,  
    16.             int visibleItemCount, int totalItemCount) {  
    17.         if (getAdapter() != null  
    18.                 && getAdapter().getCount() > MAXIMUM_LIST_ITEMS_VIEWABLE) {  
    19.             if (listViewTouchAction == MotionEvent.ACTION_MOVE) {  
    20.                 scrollBy(0, -1);  
    21.             }  
    22.         }  
    23.     }  
    24.   
    25.     @Override  
    26.     public void onScrollStateChanged(AbsListView view, int scrollState) {  
    27.     }  
    28.   
    29.     @Override  
    30.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
    31.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
    32.   
    33.         int newHeight = 0;  
    34.         final int heightMode = MeasureSpec.getMode(heightMeasureSpec);  
    35.         int heightSize = MeasureSpec.getSize(heightMeasureSpec);  
    36.         if (heightMode != MeasureSpec.EXACTLY) {  
    37.             ListAdapter listAdapter = getAdapter();  
    38.             if (listAdapter != null && !listAdapter.isEmpty()) {  
    39.                 int listPosition = 0;  
    40.                 for (listPosition = 0; listPosition < listAdapter.getCount()  
    41.                         && listPosition < MAXIMUM_LIST_ITEMS_VIEWABLE; listPosition++) {  
    42.                     View listItem = listAdapter.getView(listPosition, null,  
    43.                             this);  
    44.                     // now it will not throw a NPE if listItem is a ViewGroup  
    45.                     // instance  
    46.                     if (listItem instanceof ViewGroup) {  
    47.                         listItem.setLayoutParams(new LayoutParams(  
    48.                                 LayoutParams.WRAP_CONTENT,  
    49.                                 LayoutParams.WRAP_CONTENT));  
    50.                     }  
    51.                     listItem.measure(widthMeasureSpec, heightMeasureSpec);  
    52.                     newHeight += listItem.getMeasuredHeight();  
    53.                 }  
    54.                 newHeight += getDividerHeight() * listPosition;  
    55.             }  
    56.             if ((heightMode == MeasureSpec.AT_MOST) && (newHeight > heightSize)) {  
    57.                 if (newHeight > heightSize) {  
    58.                     newHeight = heightSize;  
    59.                 }  
    60.             }  
    61.         } else {  
    62.             newHeight = getMeasuredHeight();  
    63.         }  
    64.         setMeasuredDimension(getMeasuredWidth(), newHeight);  
    65.     }  
    66.   
    67.     @Override  
    68.     public boolean onTouch(View v, MotionEvent event) {  
    69.         if (getAdapter() != null  
    70.                 && getAdapter().getCount() > MAXIMUM_LIST_ITEMS_VIEWABLE) {  
    71.             if (listViewTouchAction == MotionEvent.ACTION_MOVE) {  
    72.                 scrollBy(01);  
    73.             }  
    74.         }  
    75.         return false;  
    76.     }  
    77. }  


    参考资料

    http://stackoverflow.com/questions/6210895/listview-inside-scrollview-is-not-scrolling-on-android

    http://stackoverflow.com/questions/3495890/how-can-i-put-a-listview-into-a-scrollview-without-it-collapsing

    http://developer.android.com/reference/android/widget/ScrollView.html

  • 相关阅读:
    Response.Redirect引起的性能问题分析
    Html5中 视频 音频标签 进度条问题
    GIS 地理坐标分类
    函数指针理解最透彻的文章
    python安装第三方包之后无法导入相应模块(一个容易忽略的bug)
    git使用入门
    OpenSSL中HMAC,MD5以及对称加密算法的应用
    OpenSSL库中加密组件使用的相关链接
    Ubuntu 12.04LTS下配置OpenSSL和gmp环境
    编程写作注意事项!
  • 原文地址:https://www.cnblogs.com/xiaomaohai/p/6158022.html
Copyright © 2020-2023  润新知