《转》ScrollView中嵌套ListView和Viewpager问题

转自:qq_16404309 详情看链接:http://blog.csdn.net/qq_16404309/article/details/46043801?locationNum=12 以下为转载内容:

转载:http://blog.csdn.NET/hellovytas/article/details/13018205


Android默认是不支持ScrollView里嵌套ListView的(Android懒就懒吧,还找那么多借口~还有脸用“never”这种词!?)。

但是在开发中,ScrollView嵌套ListView的情况还是很多的,谁让那些大艺术家(交互设计师)一意孤行非要这么干呢。在网上查了很多资料,几乎把百度谷歌翻了个底朝天,用一种方法解决了一种问题,但却衍生出更多问题。就这样拆东墙补西墙,补到最后,终于看似完美了。

为了感谢网上那些乐于分享代码经验的程序猿们,我也在此把自己的解决方案分享给后来者,让大家少走些弯路。



我们公司产品的UI设计样式 异常丧心病狂,已经不是ScrollView嵌套一个ListView那么小的问题了。

我把所有的问题列一下:

1.解决ScrollView嵌套ListView时,ListView高度显示异常。

2.解决ScrollView嵌套ListView,ListView的拉到底部加载更多的解决方案。解决ScrollView自动调至底部的问题。

3.解决ScrollView嵌套ViewPager时的滑动冲突。

1.解决ScrollView嵌套ListView时,ListView高度显示异常。

ScrollView嵌套ListView后,由于冲突,ListView会只显示一两行的高度。尝试过各种解决方法,最后觉得计算ListView每项的高度,然后给它设置高度是最好的解决方案。下面这个方法是国外论坛上的大神写的,但是使用后会发现一些问题。


如果ListView的getView方法很复杂,并且LISTVIEW的数据很多,那么这个方法中的For循环会造成相当长时间的UI线程拥堵。这个问题好解决,把方法中的耗时操作放在线程中就OK了。因为setLayoutParams(params)是UI操作,所以放在线程外。下面是我修改过的使用方法:

  1. new Thread(new Runnable() {   
  2.                 public void run() {   
  3.                     setListViewHeightBasedOnChildren(mAdapter,mListView);  
  4.                     mHandler.sendEmptyMessage(UI_SHOW_LIST);  
  5.                 }  
  6.             }).start();   
  7. //待setListViewHeightBasedOnChildren计算好高度后,mHandler里的UI_SHOW_LIST要做的就是下面这句。  
  8. mListView.setLayoutParams(params);  
  1. private ViewGroup.LayoutParams params;  
  2. public void setListViewHeightBasedOnChildren(MyAdapter mAdapter,ListView listView) {    
  3.     if (mAdapter == null) {    
  4.         return;    
  5.     }    
  6.     int totalHeight = 0;    
  7.     for (int i = 0; i < mAdapter.getCount(); i++) {  
  8.         View listItem = mAdapter.getView(i, null, listView);   
  9.         int desiredWidth = MeasureSpec.makeMeasureSpec(mListView.getWidth(), MeasureSpec.AT_MOST);  
  10.         listItem.measure(desiredWidth, 0);    
  11.         totalHeight += listItem.getMeasuredHeight();    
  12.     }    
  13.   
  14.     params = listView.getLayoutParams();    
  15.     params.height = totalHeight + (listView.getDividerHeight() * (mAdapter.getCount() - 1));  
  16. }    

最后,在布局文件中,给ScrollView添加  android:fillViewport=”true”   这个属性。


2.解决ScrollView嵌套ListView,ListView的拉至底部加载更多的解决方案。

解决完listview的高度问题,那么就得面对一个新的问题,在冲突面前,ListView已经无法用以前的方式来响应加载更多了。

其实加载更多很简单,只用在listview的adapter里,给数据源增加相应的数据,然后重新调用setListViewHeightBasedOnChildren方法即可。

问题就在于如果知道ListView已经滑动到底部了。我的方案是监听ScrollView的onScrollChanged方法,因为onScrollChanged是protected的,所以只能去重写ScrollView。


重写的ScrollView如下:

  1. public class MyScrollView extends ScrollView {  
  2.     private ScrollChangedListener mScrollChangedListener;  
  3.       
  4.     public interface ScrollChangedListener  
  5.     {  
  6.         void onScrollChanged(int y);  
  7.     }  
  8.       
  9.     public void setScrollChangedListener(ScrollChangedListener l)  
  10.     {  
  11.         mScrollChangedListener = l;  
  12.     }  
  13.       
  14.     public MyScrollView(Context context) {  
  15.         super(context, null);  
  16.         setFadingEdgeLength(0);  
  17.     }  
  18.       
  19.     public MyScrollView(Context context, AttributeSet attrs) {  
  20.         super(context, attrs);  
  21.         setFadingEdgeLength(0);  
  22.     }  
  23.   
  24.     @Override  
  25.     public boolean onInterceptTouchEvent(MotionEvent ev) {  
  26.          return super.onInterceptTouchEvent(ev);  
  27.     }  
  28.       
  29.     @Override  
  30.     protected void onScrollChanged(int x, int y, int oldx, int oldy) {  
  31.         super.onScrollChanged(x, y, oldx, oldy);  
  32.         if(mScrollChangedListener != null)  
  33.             mScrollChangedListener.onScrollChanged(y);  
  34.     }  
  35.       
  36. }  

在Activity中进行处理:
  1. boolean mEnableFlag=true;  
  1. private int SCROLL_Y=0;  
  2. scrollView = (MyScrollView)findViewById(R.id.scroll_view);  
  3.         scrollView.setScrollChangedListener(mScrollChangedListener);  
  4.   
  5. private ScrollChangedListener mScrollChangedListener = new ScrollChangedListener() {  
  6.     @Override  
  7.     public void onScrollChanged(int y) {  
  8.         int height=scrollView.getHeight();  
  9.         int scrollViewMeasuredHeight=scrollView.getChildAt(0).getMeasuredHeight();  
  10.         //System.out.println(“>>>>>>>>>>>> ”+”scrollY=”+y+”,height=”+height+”,scrollViewMeasuredHeight=”+scrollViewMeasuredHeight);  
  11.         SCROLL_Y=y;  
  12.         if((y+height)>=scrollViewMeasuredHeight) {  
  13.             if(mEnableFlag)  
  14.             {  
  15.                 mEnableFlag=false;  
  16.                 // GO TO Load More!!!  
  17.             }  
  18.         }  
  19.     }  
  20. };  
我的思路是当ScrollView滑动到底部时,调用加载更多的方法。

为了防止上一次加载更多未完成时,重复触发,所以加了个mEnableFalg标记进行控制,大家在加载更多完成后,记得打开标记。

这样的解决方案会遇到一个问题,onResume时,由于ListView的高度已经算给它,所以它会去进行它的getView,这就会导致页面自动跳转到最底部。

为了解决这个问题,我加了SCROLL_Y变量用以记录当前ScrollView的Y轴坐标。
然后在onResume方法中添加如下代码:

  1. @Override  
  2. protected void onResume() {  
  3.     super.onResume();  
  4.     scrollView.smoothScrollTo(0, SCROLL_Y);  
  5. }  

 当然还有一种最简单的解决方法,mListView.setFocusable(false);。


3.解决ScrollView嵌套ViewPager时的滑动冲突。
  1. mViewPager.setOnTouchListener(new OnTouchListener() {  
  2.             @Override  
  3.             public boolean onTouch(View v, MotionEvent event) {  
  4.                 switch (event.getAction()) {  
  5.                 case MotionEvent.ACTION_MOVE:   
  6.                     scrollView.requestDisallowInterceptTouchEvent(true);  
  7.                     break;  
  8.                 case MotionEvent.ACTION_UP:  
  9.                 case MotionEvent.ACTION_CANCEL:  
  10.                     scrollView.requestDisallowInterceptTouchEvent(false);  
  11.                     break;  
  12.                 }  
  13.             return false;  
  14.         }  
  15.     });  
这种解决方法虽然解决了两者的冲突,但是的缺点在于,在Viewpager里上下滑动无法让ScrollView响应到。虽然我个人觉得没啥必要,但大家非要解决的话,可以对ACTIOM_MOVE进行判断,可以对XY轴的移动坐标进行比较,如果Y轴移动量是X轴的2倍及以上,则算作上下滑动,则让ScrollView去滑去。


转载:http://blog.csdn.Net/liudemingbingyu/article/details/8944439


2268人阅读 评论(3) 收藏 举报

  1.   
google官方是不提倡在UI中嵌套可滚动控件,如ScrollView,ViewPager等,但有时候为了实现一定的效果也不得不用,其实很多知名App都是这样弄的。下面是我总结的两种嵌套情形。

一.当ScrollView中嵌套ViewPager的情形

这种比较常见,一般是在界面的最顶部放置一个ViewPager,用来展示图片还有一些文字说明,下面则是List,像之前版本的网易新闻客户端就是这样的效果。List滚动的同时ViewPager也会跟着滚动,这时候就要用到ScrollView中嵌套ViewPager。

由于ScrollView是垂直滚动的,ViewPager是水平滑动的。当在ViewPager上滑动的时候,如果在水平方向上的偏移量较垂直方向上的偏移量不是太大的时候,会有明显的ScrollView滚动问题,很影响用户体验。解决的方法我总结了两种:

1.用自定义ScrollView,通过覆写dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent等方法,来人为的控制TouchEvent。于是在网上搜了下,从点击打开链接找到了一个方法。覆写ScrollView的onInterceptTouchEvent方法,通过对水平和垂直方向上的偏移量进行计算,让ScrollView决定是拦截TouchEvent还是传递给子View。试了下,效果挺好的,代码如下:

  1. @Override    
  2.     public boolean onInterceptTouchEvent(MotionEvent ev) {  
  3.           
  4.         switch (ev.getAction()) {    
  5.             case MotionEvent.ACTION_DOWN:    
  6.                 xDistance = yDistance = 0f;    
  7.                 xLast = ev.getX();    
  8.                 yLast = ev.getY();    
  9.                 break;    
  10.             case MotionEvent.ACTION_MOVE:    
  11.                 final float curX = ev.getX();    
  12.                 final float curY = ev.getY();    
  13.                     
  14.                 xDistance += Math.abs(curX - xLast);    
  15.                 yDistance += Math.abs(curY - yLast);    
  16.                 xLast = curX;    
  17.                 yLast = curY;    
  18.                     
  19.                 if(xDistance > yDistance){    
  20.                     return false;    
  21.                 }      
  22.         }  
  23.     
  24.         return super.onInterceptTouchEvent(ev);    
  25.     }  
2.自定义ViewPager,覆写dispatchTouchEvent方法。该方法来自: 点击打开链接代码如下:
  1. boolean ret = super.dispatchTouchEvent(ev);  
  2.           if(ret)   
  3.           {  
  4.             requestDisallowInterceptTouchEvent(true);  
  5.           }  
  6.           return ret;  
当ret为true时,就设置不要父控件及祖先控件不允许拦截TouchEvent。该请求会被传递给该ViewPager的所有父控件。并且会持续从ACTION_DOWN到ACTION_UP整个过程。我感觉这种方法也简单些,效果也很好。


二.ViewPager中嵌套ScrollView,ScrollView中嵌套ViewPager

这种情况下,在ScrollView中嵌套的子ViewPager上滑动的时候该ViewPager并不会切换,而是切换父ViewPager。这时候,只能自定义ViewPager了,方法跟情形一中的第二种方法是一样的,效果也挺好的。使该子ViewPager获得TouchEvent而不传递给父及祖先控件。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值