Google是不推薦在ScrollView 中放入一個可滾動的菜單的,比如放置一個ListView、GridView、ViewPager這些控件的,儘量不要讓兩者嵌套,但有時候還是有這個需求,先不管它合不合理。如果直接在ScrollView中嵌套只會出現一行,然後在其中滾動,這樣不是很好,下面是我的整理,希望對大家有幫助,我也是從網上摘抄的,當然加入了一些我自己的東西。
一、在ScrollView中嵌套ListView,有兩張方法
第一種是自定義View,繼承ListView代碼如下:
- 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);
}
}
第二種
- 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
- 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
- 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);
- }
- }
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
- 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;
- }
- }
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中加入,一下代碼即可解決:
view.setFocusable(true);
view.setFocusableInTouchMode(true);
view.requestFocus();
這段代碼在初始化的時候就讓該界面的頂部的某一個控件獲得焦點,滾動條自然就顯示到頂部了,順便附一下我找到答案的地方http://blog.csdn.net/studyalllife/article/details/42970975這上面寫的很混亂,大家可以參考一下,大功告成。