android 自定义EditText,解决ScrollView嵌套EditText滚动冲突问题

首先,说一下这个控件实现的功能。

1.EditText中的内容可上下滚动,通过截取ScrollView的触摸事件

2.当EditText中内容滚动到了底部,继续上滑则外面的ScrollView向上滚动

3.当EditText中内容滚动到顶部,继续下滑则外面的ScrollView向下滚动

4.当EditText中无内容,手指在EditText中滑动可以控制ScrollView的滚动


实现上述几个功能之后,基本就可以在项目中使用了。


其实,这个知识点在网上已经有很多种实现了,之前在开发中为了赶时间我也是在网上找了段代码直接用的,结果发现有问题,上述的2,3两点功能未实现,于是自己就优化了一下。

从网络来的当然就得回归到网络,所以就在此分享一下。


实现思路:

1.你得知道TextView中有一个onScrollChanged()方法,这个就是用来查看控件中文字滚动状态的。而EditText继承于TextView。

有了这个方法,我们就可以知道EditText中的文字滚动的距离

2.在onMeasure()方法中,获取EditText可以滚动的最大距离H_scroll。一个是内容的高度H_content,一个是控件的高度H_view, (H_content - H_view = H_scroll)

这一点有些抽象,大家自己体会体会吧。

3.每次手指按下都重置控件,重新判断是否可以滚动。需复写dispatchTouchEvent()。

4.判断谁来执行滚动。即判断是EditText的内容滚动,还是ScrollView中的内容滚动。而要实现这一点很简单:

/**
 * 支持EditText中内容上下滑动,解决与ScrollView滑动冲突的问题
 */
public class ScrollEditText extends EditText {

    private final int MOVE_SLOP = 20; //移动距离临界

    //滑动距离的最大边界
    private int mOffsetHeight;

    //是否到顶或者到底的标志
    private boolean mBottomFlag = false;
    private boolean isCanScroll = false;//标记内容是否触发了滚动
    private float lastY = 0;

    public ScrollEditText(Context context) {
        this(context,null);
    }

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

    public ScrollEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int paddingTop;
        int paddingBottom;
        int mHeight;
        int mLayoutHeight;

        //获得内容面板
        Layout mLayout = getLayout();
        mLayoutHeight = mLayout.getHeight();
        
        paddingTop = getTotalPaddingTop();
        paddingBottom = getTotalPaddingBottom();

        //获得控件的实际高度
        mHeight = getHeight();

        //计算滑动距离的边界(H_content - H_view = H_scroll)
        mOffsetHeight = mLayoutHeight + paddingTop + paddingBottom - mHeight;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN){
            //手指按下事件,重置状态
            mBottomFlag = false;
            isCanScroll = false;
            lastY=0;
        }
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        boolean result = super.onTouchEvent(event);
        //如果是需要拦截,则再拦截,这个方法会在onScrollChanged方法之后再调用一次
        if (!mBottomFlag)
            getParent().requestDisallowInterceptTouchEvent(true);
        if (event.getAction() == MotionEvent.ACTION_MOVE){
            if (lastY == 0){
                lastY = event.getRawY();
            }
            //条件:手指move了一段距离,但是onScrollChanged函数未调用,说明文字无法滚动了,则将触摸处理权交还给ParentView
            if (Math.abs(lastY - event.getRawY()) > MOVE_SLOP){
                if (!isCanScroll){
                    getParent().requestDisallowInterceptTouchEvent(false);
                }
            }
            //Log.d("Javine","ActionMove: "+ lastY + "," + event.getRawY());
        }
        return result;
    }

    @Override
    protected void onScrollChanged(int horiz, int vert, int oldHoriz, int oldVert) {
        super.onScrollChanged(horiz, vert, oldHoriz, oldVert);
        isCanScroll = true;
        //Log.d("Javine","onScrolled "+vert);
        if (vert == mOffsetHeight || vert == 0) {
            //这里将处理权交还给父控件
            getParent().requestDisallowInterceptTouchEvent(false);
            mBottomFlag = true;
        }
    }
}

 
 
 
 
 

猜你喜欢

转载自blog.csdn.net/pxcz110112/article/details/80423851