完美解决Android中,ScrollView嵌套ListView的冲突。

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操作,所以放在线程外。下面是我修改过的使用方法:

new Thread(new Runnable() { 
				public void run() { 
					setListViewHeightBasedOnChildren(mAdapter,mListView);
					mHandler.sendEmptyMessage(UI_SHOW_LIST);
				}
			}).start(); 
//待setListViewHeightBasedOnChildren计算好高度后,mHandler里的UI_SHOW_LIST要做的就是下面这句。
mListView.setLayoutParams(params);

private ViewGroup.LayoutParams params;
public void setListViewHeightBasedOnChildren(MyAdapter mAdapter,ListView listView) {  
	if (mAdapter == null) {  
		return;  
	}  
	int totalHeight = 0;  
	for (int i = 0; i < mAdapter.getCount(); i++) {
		View listItem = mAdapter.getView(i, null, listView); 
		int desiredWidth = MeasureSpec.makeMeasureSpec(mListView.getWidth(), MeasureSpec.AT_MOST);
		listItem.measure(desiredWidth, 0);  
		totalHeight += listItem.getMeasuredHeight();  
	}  

	params = listView.getLayoutParams();  
	params.height = totalHeight + (listView.getDividerHeight() * (mAdapter.getCount() - 1));
}  

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


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

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

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

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


重写的ScrollView如下:

public class MyScrollView extends ScrollView {
    private ScrollChangedListener mScrollChangedListener;
    
    public interface ScrollChangedListener
	{
		void onScrollChanged(int y);
	}
    
	public void setScrollChangedListener(ScrollChangedListener l)
	{
		mScrollChangedListener = l;
	}
    
    public MyScrollView(Context context) {
        super(context, null);
        setFadingEdgeLength(0);
    }
    
    public MyScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setFadingEdgeLength(0);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
    	 return super.onInterceptTouchEvent(ev);
    }
    
    @Override
    protected void onScrollChanged(int x, int y, int oldx, int oldy) {
        super.onScrollChanged(x, y, oldx, oldy);
        if(mScrollChangedListener != null)
        	mScrollChangedListener.onScrollChanged(y);
    }
    
}

在Activity中进行处理:

boolean mEnableFlag=true;
private int SCROLL_Y=0;
scrollView = (MyScrollView)findViewById(R.id.scroll_view);
		scrollView.setScrollChangedListener(mScrollChangedListener);

private ScrollChangedListener mScrollChangedListener = new ScrollChangedListener() {
	@Override
	public void onScrollChanged(int y) {
		int height=scrollView.getHeight();
		int scrollViewMeasuredHeight=scrollView.getChildAt(0).getMeasuredHeight();
		//System.out.println(">>>>>>>>>>>> "+"scrollY="+y+",height="+height+",scrollViewMeasuredHeight="+scrollViewMeasuredHeight);
		SCROLL_Y=y;
		if((y+height)>=scrollViewMeasuredHeight) {
			if(mEnableFlag)
			{
				mEnableFlag=false;
				// GO TO Load More!!!
			}
		}
	}
};
我的思路是当ScrollView滑动到底部时,调用加载更多的方法。

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

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

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

	@Override
	protected void onResume() {
		super.onResume();
		scrollView.smoothScrollTo(0, SCROLL_Y);
	}

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


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

mViewPager.setOnTouchListener(new OnTouchListener() {
			@Override
			public boolean onTouch(View v, MotionEvent event) {
				switch (event.getAction()) {
			    case MotionEvent.ACTION_MOVE: 
			    	scrollView.requestDisallowInterceptTouchEvent(true);
			        break;
			    case MotionEvent.ACTION_UP:
			    case MotionEvent.ACTION_CANCEL:
			    	scrollView.requestDisallowInterceptTouchEvent(false);
			        break;
			    }
			return false;
		}
	});
这种解决方法虽然解决了两者的冲突,但是的缺点在于,在Viewpager里上下滑动无法让ScrollView响应到。

虽然我个人觉得没啥必要,但大家非要解决的话,可以对ACTIOM_MOVE进行判断,可以对XY轴的移动坐标进行比较,如果Y轴移动量是X轴的2倍及以上,则算作上下滑动,则让ScrollView去滑去。


————————————————

转载时请注明出处!谢谢合作!

我将随时更新更好的解决内容,以方便更多猿类。


  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值