天天看点

ScrollView嵌套ListView,GridView,ViewPager,以及这些控件自动滚动到底部问题的解决

Google是不推荐在ScrollView 中放入一个可滚动的菜单的,比如放置一个ListView、GridView、ViewPager这些控件的,尽量不要让两者嵌套,但有时候还是有这个需求,先不管它合不合理。如果直接在ScrollView中嵌套只会出现一行,然后在其中滚动,这样不是很好,下面是我的整理,希望对大家有帮助,我也是从网上摘抄的,当然加入了一些我自己的东西。

一、在ScrollView中嵌套ListView,有两张方法

第一种是自定义View,继承ListView代码如下:

[java] view plain copy print ?

ScrollView嵌套ListView,GridView,ViewPager,以及这些控件自动滚动到底部问题的解决
ScrollView嵌套ListView,GridView,ViewPager,以及这些控件自动滚动到底部问题的解决
  1. import android.content.Context;  
  2. import android.util.AttributeSet;  
  3. import android.widget.ListView;  
  4. public class MyListView extends ListView {  
  5.     public MyListView(Context context) {  
  6.         super(context);  
  7.     }  
  8.     public MyListView(Context context, AttributeSet attrs, int defStyle) {  
  9.         super(context, attrs, defStyle);  
  10.     }  
  11.     public MyListView(Context context, AttributeSet attrs) {  
  12.         super(context, attrs);  
  13.     }  
  14.     @Override  
  15.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  16.          int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,    
  17.                  MeasureSpec.AT_MOST);      
  18.         super.onMeasure(widthMeasureSpec, expandSpec);  
  19.     }  
  20. }  
ScrollView嵌套ListView,GridView,ViewPager,以及这些控件自动滚动到底部问题的解决
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ListView;

public class MyListView extends ListView {

	public MyListView(Context context) {
		super(context);
	}
	public MyListView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}
	public MyListView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		 int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,  
			     MeasureSpec.AT_MOST);    
		super.onMeasure(widthMeasureSpec, expandSpec);
	}

}
           

第二种

[java] view plain copy print ?

ScrollView嵌套ListView,GridView,ViewPager,以及这些控件自动滚动到底部问题的解决
ScrollView嵌套ListView,GridView,ViewPager,以及这些控件自动滚动到底部问题的解决
  1. import android.view.View;  
  2. import android.view.ViewGroup;  
  3. import android.widget.ListAdapter;  
  4. import android.widget.ListView;  
  5. public class Utility {  
  6.     public static void setListViewHeightBasedOnChildren(ListView listView) {  
  7.         ListAdapter listAdapter = listView.getAdapter();  
  8.         if (listAdapter == null) {  
  9.             // pre-condition  
  10.             return;  
  11.         }  
  12.         int totalHeight = 0;  
  13.         for (int i = 0; i < listAdapter.getCount(); i++) {  
  14.             View listItem = listAdapter.getView(i, null, listView);  
  15.             listItem.measure(0, 0);  
  16.             totalHeight += listItem.getMeasuredHeight();  
  17.         }  
  18.         ViewGroup.LayoutParams params = listView.getLayoutParams();  
  19.         params.height = totalHeight  
  20.                 + (listView.getDividerHeight() * (listAdapter.getCount() - 1));  
  21.         listView.setLayoutParams(params);  
  22.     }  
  23. }  
ScrollView嵌套ListView,GridView,ViewPager,以及这些控件自动滚动到底部问题的解决
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListAdapter;
import android.widget.ListView;

public class Utility {
	public static void setListViewHeightBasedOnChildren(ListView listView) {
		ListAdapter listAdapter = listView.getAdapter();
		if (listAdapter == null) {
			// pre-condition
			return;
		}

		int totalHeight = 0;
		for (int i = 0; i < listAdapter.getCount(); i++) {
			View listItem = listAdapter.getView(i, null, listView);
			listItem.measure(0, 0);
			totalHeight += listItem.getMeasuredHeight();
		}

		ViewGroup.LayoutParams params = listView.getLayoutParams();
		params.height = totalHeight
				+ (listView.getDividerHeight() * (listAdapter.getCount() - 1));
		listView.setLayoutParams(params);
	}
}
           

第二种在setAdapter之后调用Utility.setListViewHeightBasedOnChildren(listview)这个方法,有些资料说只能item的根布局要LinearLayout,但是我的跟布局为RelativeLayout也可以的。

这两种都可以,但是在有些情况下中能用第一种,比如你显示ListView是一个ListFragment就只能用第二种方法了。

二、在ScrollView中嵌套GridView

[java] view plain copy print ?

ScrollView嵌套ListView,GridView,ViewPager,以及这些控件自动滚动到底部问题的解决
ScrollView嵌套ListView,GridView,ViewPager,以及这些控件自动滚动到底部问题的解决
  1. import android.content.Context;  
  2. import android.util.AttributeSet;  
  3. import android.widget.GridView;  
  4. public class NoScrollGridView extends GridView {    
  5.     public NoScrollGridView(Context context) {    
  6.         super(context);    
  7.     }    
  8.     public NoScrollGridView(Context context, AttributeSet attrs) {    
  9.         super(context, attrs);    
  10.     }    
  11.     @Override    
  12.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    
  13.         int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,MeasureSpec.AT_MOST);    
  14.         super.onMeasure(widthMeasureSpec, expandSpec);    
  15.     }    
  16. }    
ScrollView嵌套ListView,GridView,ViewPager,以及这些控件自动滚动到底部问题的解决
import android.content.Context;
import android.util.AttributeSet;
import android.widget.GridView;

public class NoScrollGridView extends GridView {  
	  
    public NoScrollGridView(Context context) {  
        super(context);  
          
    }  
  
	public NoScrollGridView(Context context, AttributeSet attrs) {  
        super(context, attrs);  
    }  
      
    @Override  
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,MeasureSpec.AT_MOST);  
        super.onMeasure(widthMeasureSpec, expandSpec);  
    }  
  
}  
           

二、在ScrollView中嵌套 ViewPager,也有两张方法

第一种自定义ScrollView

[java] view plain copy print ?

ScrollView嵌套ListView,GridView,ViewPager,以及这些控件自动滚动到底部问题的解决
ScrollView嵌套ListView,GridView,ViewPager,以及这些控件自动滚动到底部问题的解决
  1. import android.content.Context;  
  2. import android.util.AttributeSet;  
  3. import android.view.MotionEvent;  
  4. import android.widget.ScrollView;  
  5. public class ScrollViewExtend extends ScrollView {  
  6.     // 滑动距离及坐标  
  7.     private float xDistance, yDistance, xLast, yLast;  
  8.     public ScrollViewExtend(Context context, AttributeSet attrs) {  
  9.         super(context, attrs);  
  10.     }  
  11.     @Override  
  12.     public boolean onInterceptTouchEvent(MotionEvent ev) {  
  13.         switch (ev.getAction()) {  
  14.         case MotionEvent.ACTION_DOWN:  
  15.             xDistance = yDistance = 0f;  
  16.             xLast = ev.getX();  
  17.             yLast = ev.getY();  
  18.             break;  
  19.         case MotionEvent.ACTION_MOVE:  
  20.             final float curX = ev.getX();  
  21.             final float curY = ev.getY();  
  22.             xDistance += Math.abs(curX - xLast);  
  23.             yDistance += Math.abs(curY - yLast);  
  24.             xLast = curX;  
  25.             yLast = curY;  
  26.             if (xDistance > yDistance) {  
  27.                 return false;  
  28.             }  
  29.         }  
  30.         return super.onInterceptTouchEvent(ev);  
  31.     }  
  32. }  
ScrollView嵌套ListView,GridView,ViewPager,以及这些控件自动滚动到底部问题的解决
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.ScrollView;

/**
 * 能够兼容ViewPager的ScrollView
 * 
 * @Description: 解决了ViewPager在ScrollView中的滑动反弹问题
 */
public class ScrollViewExtend extends ScrollView {
	// 滑动距离及坐标
	private float xDistance, yDistance, xLast, yLast;

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

	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:
			xDistance = yDistance = 0f;
			xLast = ev.getX();
			yLast = ev.getY();
			break;
		case MotionEvent.ACTION_MOVE:
			final float curX = ev.getX();
			final float curY = ev.getY();

			xDistance += Math.abs(curX - xLast);
			yDistance += Math.abs(curY - yLast);
			xLast = curX;
			yLast = curY;

			if (xDistance > yDistance) {
				return false;
			}
		}
		return super.onInterceptTouchEvent(ev);
	}
}
           

第二种是自定义ViewPager

[java] view plain copy print ?

ScrollView嵌套ListView,GridView,ViewPager,以及这些控件自动滚动到底部问题的解决
ScrollView嵌套ListView,GridView,ViewPager,以及这些控件自动滚动到底部问题的解决
  1. import android.content.Context;  
  2. import android.graphics.PointF;  
  3. import android.support.v4.view.ViewPager;  
  4. import android.util.AttributeSet;  
  5. import android.view.MotionEvent;  
  6. public class ChildViewPager extends ViewPager {  
  7.     PointF downP = new PointF();  
  8.     PointF curP = new PointF();  
  9.     OnSingleTouchListener onSingleTouchListener;  
  10.     public ChildViewPager(Context context, AttributeSet attrs) {  
  11.         super(context, attrs);  
  12.         // TODO Auto-generated constructor stub  
  13.     }  
  14.     public ChildViewPager(Context context) {  
  15.         super(context);  
  16.         // TODO Auto-generated constructor stub  
  17.     }  
  18.     @Override  
  19.     public boolean onInterceptTouchEvent(MotionEvent arg0) {  
  20.         // TODO Auto-generated method stub  
  21.         // 当拦截触摸事件到达此位置的时候,返回true,  
  22.         // 说明将onTouch拦截在此控件,进而执行此控件的onTouchEvent  
  23.         return true;  
  24.     }  
  25.     @Override  
  26.     public boolean onTouchEvent(MotionEvent arg0) {  
  27.         // TODO Auto-generated method stub  
  28.         // 每次进行onTouch事件都记录当前的按下的坐标  
  29.         curP.x = arg0.getX();  
  30.         curP.y = arg0.getY();  
  31.         if (arg0.getAction() == MotionEvent.ACTION_DOWN) {  
  32.             // 记录按下时候的坐标  
  33.             // 切记不可用 downP = curP ,这样在改变curP的时候,downP也会改变  
  34.             downP.x = arg0.getX();  
  35.             downP.y = arg0.getY();  
  36.             // 此句代码是为了通知他的父ViewPager现在进行的是本控件的操作,不要对我的操作进行干扰  
  37.             getParent().requestDisallowInterceptTouchEvent(true);  
  38.         }  
  39.         if (arg0.getAction() == MotionEvent.ACTION_MOVE) {  
  40.             // 此句代码是为了通知他的父ViewPager现在进行的是本控件的操作,不要对我的操作进行干扰  
  41.             getParent().requestDisallowInterceptTouchEvent(true);  
  42.         }  
  43.         if (arg0.getAction() == MotionEvent.ACTION_UP) {  
  44.             // 在up时判断是否按下和松手的坐标为一个点  
  45.             // 如果是一个点,将执行点击事件,这是我自己写的点击事件,而不是onclick  
  46.             if (downP.x == curP.x && downP.y == curP.y) {  
  47.                 onSingleTouch();  
  48.                 return true;  
  49.             }  
  50.         }  
  51.         return super.onTouchEvent(arg0);  
  52.     }  
  53.     public void onSingleTouch() {  
  54.         if (onSingleTouchListener != null) {  
  55.             onSingleTouchListener.onSingleTouch();  
  56.         }  
  57.     }  
  58.     public interface OnSingleTouchListener {  
  59.         public void onSingleTouch();  
  60.     }  
  61.     public void setOnSingleTouchListener(  
  62.             OnSingleTouchListener onSingleTouchListener) {  
  63.         this.onSingleTouchListener = onSingleTouchListener;  
  64.     }  
  65. }  
ScrollView嵌套ListView,GridView,ViewPager,以及这些控件自动滚动到底部问题的解决
import android.content.Context;
import android.graphics.PointF;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;

/**
 * 嵌套在ScrollView中的ViewPager,解决冲突
 * @author m3
 *
 */
public class ChildViewPager extends ViewPager {
	/** 触摸时按下的点 **/
	PointF downP = new PointF();
	/** 触摸时当前的点 **/
	PointF curP = new PointF();
	OnSingleTouchListener onSingleTouchListener;

	public ChildViewPager(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
	}

	public ChildViewPager(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
	}

	@Override
	public boolean onInterceptTouchEvent(MotionEvent arg0) {
		// TODO Auto-generated method stub
		// 当拦截触摸事件到达此位置的时候,返回true,
		// 说明将onTouch拦截在此控件,进而执行此控件的onTouchEvent
		return true;
	}

	@Override
	public boolean onTouchEvent(MotionEvent arg0) {
		// TODO Auto-generated method stub
		// 每次进行onTouch事件都记录当前的按下的坐标
		curP.x = arg0.getX();
		curP.y = arg0.getY();

		if (arg0.getAction() == MotionEvent.ACTION_DOWN) {
			// 记录按下时候的坐标
			// 切记不可用 downP = curP ,这样在改变curP的时候,downP也会改变
			downP.x = arg0.getX();
			downP.y = arg0.getY();
			// 此句代码是为了通知他的父ViewPager现在进行的是本控件的操作,不要对我的操作进行干扰
			getParent().requestDisallowInterceptTouchEvent(true);
		}

		if (arg0.getAction() == MotionEvent.ACTION_MOVE) {
			// 此句代码是为了通知他的父ViewPager现在进行的是本控件的操作,不要对我的操作进行干扰
			getParent().requestDisallowInterceptTouchEvent(true);
		}

		if (arg0.getAction() == MotionEvent.ACTION_UP) {
			// 在up时判断是否按下和松手的坐标为一个点
			// 如果是一个点,将执行点击事件,这是我自己写的点击事件,而不是onclick
			if (downP.x == curP.x && downP.y == curP.y) {
				onSingleTouch();
				return true;
			}
		}

		return super.onTouchEvent(arg0);
	}

	/**
	 * 单击
	 */
	public void onSingleTouch() {
		if (onSingleTouchListener != null) {

			onSingleTouchListener.onSingleTouch();
		}
	}

	/**
	 * 创建点击事件接口
	 * 
	 * @author wanpg
	 * 
	 */
	public interface OnSingleTouchListener {
		public void onSingleTouch();
	}

	public void setOnSingleTouchListener(
			OnSingleTouchListener onSingleTouchListener) {
		this.onSingleTouchListener = onSingleTouchListener;
	}

}
           

第二个问题在ScrollView嵌套ListView,GridView,如果这些子控件很长超出了屏幕的高度,那么ScrollView会自动滚到底部,但是我们需要默认在顶部,我们要在ListView和GridView上面的view中加入,一下代码即可解决:

[java] view plain copy print ?

ScrollView嵌套ListView,GridView,ViewPager,以及这些控件自动滚动到底部问题的解决
ScrollView嵌套ListView,GridView,ViewPager,以及这些控件自动滚动到底部问题的解决
  1. view.setFocusable(true);  
  2. view.setFocusableInTouchMode(true);  
  3. view.requestFocus();  
ScrollView嵌套ListView,GridView,ViewPager,以及这些控件自动滚动到底部问题的解决
view.setFocusable(true);
view.setFocusableInTouchMode(true);
view.requestFocus();
           

这段代码在初始化的时候就让该界面的顶部的某一个控件获得焦点,滚动条自然就显示到顶部了,顺便附一下我找到答案的地方http://blog.csdn.net/studyalllife/article/details/42970975这上面写的很混乱,大家可以参考一下,大功告成。