Google是不推荐在ScrollView 中放入一个可滚动的菜单的,比如放置一个ListView、GridView、ViewPager这些控件的,尽量不要让两者嵌套,但有时候还是有这个需求,先不管它合不合理。如果直接在ScrollView中嵌套只会出现一行,然后在其中滚动,这样不是很好,下面是我的整理,希望对大家有帮助,我也是从网上摘抄的,当然加入了一些我自己的东西。
一、在ScrollView中嵌套ListView,有两张方法
第一种是自定义View,继承ListView代码如下:
[java] view plain copy print ?
- 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);
- }
- }
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 ?
- 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);
- }
- }
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 ?
- 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);
- }
- }
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 ?
- import android.content.Context;
- import android.util.AttributeSet;
- import android.view.MotionEvent;
- import android.widget.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);
- }
- }
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 ?
- import android.content.Context;
- import android.graphics.PointF;
- import android.support.v4.view.ViewPager;
- import android.util.AttributeSet;
- import android.view.MotionEvent;
- 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();
- }
- }
- public interface OnSingleTouchListener {
- public void onSingleTouch();
- }
- public void setOnSingleTouchListener(
- OnSingleTouchListener onSingleTouchListener) {
- this.onSingleTouchListener = onSingleTouchListener;
- }
- }
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 ?
- view.setFocusable(true);
- view.setFocusableInTouchMode(true);
- view.requestFocus();
view.setFocusable(true);
view.setFocusableInTouchMode(true);
view.requestFocus();
这段代码在初始化的时候就让该界面的顶部的某一个控件获得焦点,滚动条自然就显示到顶部了,顺便附一下我找到答案的地方http://blog.csdn.net/studyalllife/article/details/42970975这上面写的很混乱,大家可以参考一下,大功告成。