ScrollView嵌套ViewPager的滚动冲突,以及ViewPage中的高度自适应问题解决

本文介绍了解决ScrollView嵌套ViewPager时出现的滚动冲突问题的方法。提供了两种解决方案:一是自定义ScrollView,二是结合自定义ViewPager并动态调整高度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值