Google不建议在ScrollView中嵌套其他滚动的组件,会导致滚动时间的冲突,但是在实际开发中,很显然这样的应用场景是非常多的,比如在应用中个人资料界面,如果有资料,个人动态,或者其他Fragment需要在ViewPager进行切换,而ScrollView也有滚动事件,两者之间很显然会产生冲突,应用场景如下所示
如何解决scrollView和ViewPager之间的冲突?
第一种.自定义ScrollView
package com.xlzj.widget;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import com.xlzj.widget.LazyScrollView.OnScrollListener;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.webkit.WebView;
import android.widget.ScrollView;
import android.widget.Scroller;
/**
* 自定义ScrollView,解决:ScrollView嵌套ViewPager,导致ViewPager不能滑动的问题
*/
public class CustomScrollView extends ScrollView {
private GestureDetector mGestureDetector;
private int Scroll_height = 0;
private int view_height = 0;
protected Field scrollView_mScroller;
private static final String TAG = "CustomScrollView";
public CustomScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
mGestureDetector = new GestureDetector(context, new YScrollDetector());
setFadingEdgeLength(0);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
stopAnim();
}
boolean ret = super.onInterceptTouchEvent(ev);
boolean ret2 = mGestureDetector.onTouchEvent(ev);
return ret && ret2;
}
// Return false if we're scrolling in the x direction
class YScrollDetector extends SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (Math.abs(distanceY) > Math.abs(distanceX)) {
return true;
}
return false;
}
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
boolean stop = false;
if (Scroll_height - view_height == t) {
stop = true;
}
if (t == 0 || stop == true) {
try {
if (scrollView_mScroller == null) {
scrollView_mScroller = getDeclaredField(this, "mScroller");
}
Object ob = scrollView_mScroller.get(this);
if (ob == null || !(ob instanceof Scroller)) {
return;
}
Scroller sc = (Scroller) ob;
sc.abortAnimation();
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
}
super.onScrollChanged(l, t, oldl, oldt);
if(onScrollListener != null){
onScrollListener.onAutoScroll(l, t, oldl, oldt);
}
}
private OnScrollListener onScrollListener;
public void setOnScrollListener(OnScrollListener onScrollListener) {
this.onScrollListener = onScrollListener;
}
private void stopAnim() {
try {
if (scrollView_mScroller == null) {
scrollView_mScroller = getDeclaredField(this, "mScroller");
}
Object ob = scrollView_mScroller.get(this);
if (ob == null) {
return;
}
Method method = ob.getClass().getMethod("abortAnimation");
method.invoke(ob);
} catch (Exception ex) {
Log.e(TAG, ex.getMessage());
}
}
@Override
protected int computeVerticalScrollRange() {
Scroll_height = super.computeVerticalScrollRange();
return Scroll_height;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (changed == true) {
view_height = b - t;
}
}
@Override
public void requestChildFocus(View child, View focused) {
if (focused != null && focused instanceof WebView) {
return;
}
super.requestChildFocus(child, focused);
}
/**
* 获取一个对象隐藏的属性,并设置属性为public属性允许直接访问
*
* @return {@link Field} 如果无法读取,返回null;返回的Field需要使用者自己缓存,本方法不做缓存�?
*/
public static Field getDeclaredField(Object object, String field_name) {
Class<?> cla = object.getClass();
Field field = null;
for (; cla != Object.class; cla = cla.getSuperclass()) {
try {
field = cla.getDeclaredField(field_name);
field.setAccessible(true);
return field;
} catch (Exception e) {
}
}
return null;
}
}
第二种方法.自定义ViewPager,重写onMeasure()方法,但是如果在上述的应用场景中,每一个ViewPager的高度都是不确定的,需要动态给ViewPager设置高度,
所以我采用以上两种方法结合,在自定义ViewPager调用的onMeasure()的时候,计算出ViewPager每一个Fragment的高度值,用hashMap把测量高度存储起来
package com.xlzj.widget;
import java.util.HashMap;
import java.util.Map;
import android.annotation.SuppressLint;
import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class CustomViewPager extends ViewPager {
private boolean enabled=true;//false;//默认不可滑动
public CustomViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
//this.enabled = true;
}
//触摸没有反应就可以了
@Override
public boolean onTouchEvent(MotionEvent event) {
if (this.enabled) {
try {
return super.onTouchEvent(event);
} catch (IllegalArgumentException ex) {
ex.printStackTrace();
}
}
return false;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (this.enabled) {
try {
return super.onInterceptTouchEvent(event);
} catch (IllegalArgumentException ex) {
ex.printStackTrace();
}
}
return false;
}
public void setPagingEnabled(boolean enabled) {
this.enabled = enabled;
}
@SuppressLint("UseSparseArrays")
private Map<Integer, Integer> map = new HashMap<Integer, Integer>();
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int h = child.getMeasuredHeight();
map.put(i, h);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
public Map< Integer, Integer> getMap(){
return this.map;
}
}
使用
viewPager.setOnPageChangeListener(new MyOnPageChangeListener());
private class MyOnPageChangeListener implements OnPageChangeListener {
public void onPageScrollStateChanged(int index) {
}
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
public void onPageSelected(int index) {
resetViewPagerHeight(index);
}
}
/**
* 重新设置viewPager高度
*
* @param position
*/
@SuppressLint("WrongCall")
public void resetViewPagerHeight(int position) {
View child = viewPager.getChildAt(position);
if (child != null) {
ViewGroup.LayoutParams params = viewPager.getLayoutParams();
params.height = viewPager.getMap().get(position);
viewPager.setLayoutParams(params);
}
}
如果Fragment里嵌套的有ListView,同样把ListView总高度计算出来
public static void setListViewHeightBasedOnChildren(ListView listView) {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
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);
}
/**
* 获取Listview的高度,然后设置ViewPager的高度
* @param listView
* @return
*/
public static int setListViewHeightBasedOnChildren1(ListView listView) {
//获取ListView对应的Adapter
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
// pre-condition
return 0;
}
int totalHeight = 0;
for (int i = 0, len = listAdapter.getCount(); i < len; i++) { //listAdapter.getCount()返回数据项的数目
View listItem = listAdapter.getView(i, null, listView);
listItem.measure(0, 0); //计算子项View 的宽高
totalHeight += listItem.getMeasuredHeight(); //统计所有子项的总高度
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
//listView.getDividerHeight()获取子项间分隔符占用的高度
//params.height最后得到整个ListView完整显示需要的高度
return params.height;
}