本篇文章的相关内容需结合上文:从ScrollView嵌套EditText的滑动事件冲突分析触摸事件的分发机制以及TextView的简要实现和冲突的解决办法
在说完了如何解决ScrollView嵌套EditText的滑动事件冲突之后,我们接下来说一下如何实现它们两者之间的联带滑动。什么是联带滑动呢,就是当EditText滑动到底部的时候,这时就应该让外部的ScrollView跟着滑动,好让它们之间完成连贯的滑动事件,就是我们这篇文章的目的。具体效果就像下面这样:
上图是一个GIF图片,所以有些卡顿的效果,实际上效果是非常流畅的。那么实现这种效果该怎么做呢?
我们知道,子View可以根据requestDisallowInterceptTouchEvent方法来请求是否允许其祖父布局拦截本次的触摸事件,那么,我们就有了初步的解决办法,就是,在需要的时候,不要让祖父布局拦截事件,在不需要的时候,让它们拦截,这时,事件就会被交给祖父布局来处理,并会让ScrollView滑动起来。
上篇文章,我们已经对这一步做了基本的处理,如果你已经按照上文实现了的话,发现并没有按照想象中的那样实现本效果。那么是哪里出现了问题,是哪里还有问题吗?
细心的同学可以发现,我们在onScrollChanged方法中对到达顶部和底部时做了处理,允许祖父布局对事件进行拦截。但是,如果做了调试的话,onScrollChanged方法调用之后onTouchEvent方法也调用了一次requestDisallowInterceptTouchEvent,并设置的参数还是true,也就是说,刚才在onScrollChanged方法中做的处理被取消了。所以,这时我们需要加个标志,用于帮助onTouchEvent方法中的requestDisallowInterceptTouchEvent方法进行合理的调用。
So,我们的问题就解决了。
不过还需要一些后续工作,就是在dispatchTouchEvent判断时候有新一轮的事件被传递过来,这时,我们还需要将这个标志重新初始化一下。
全部的实现代码如下:
<span style="font-family:'Microsoft YaHei';font-size:14px;">package com.example.sahadev.gridlayout;
import android.content.Context;
import android.text.Layout;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.EditText;
/**
* Created by Sahadev on 2016/4/20.
*/
public class MyEditText extends EditText {
//滑动距离的最大边界
private int mOffsetHeight;
//是否到顶或者到底的标志
private boolean mBottomFlag = false;
public MyEditText(Context context) {
super(context);
init();
}
public MyEditText(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MyEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
}
@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();
//计算滑动距离的边界
mOffsetHeight = mLayoutHeight + paddingTop + paddingBottom - mHeight;
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN)
//如果是新的按下事件,则对mBottomFlag重新初始化
mBottomFlag = false;
//如果已经不要这次事件,则传出取消的信号,这里的作用不大
if (mBottomFlag)
event.setAction(MotionEvent.ACTION_CANCEL);
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean result = super.onTouchEvent(event);
//如果是需要拦截,则再拦截,这个方法会在onScrollChanged方法之后再调用一次
if (!mBottomFlag)
getParent().requestDisallowInterceptTouchEvent(true);
return result;
}
@Override
protected void onScrollChanged(int horiz, int vert, int oldHoriz, int oldVert) {
super.onScrollChanged(horiz, vert, oldHoriz, oldVert);
if (vert == mOffsetHeight || vert == 0) {
//这里触发父布局或祖父布局的滑动事件
getParent().requestDisallowInterceptTouchEvent(false);
mBottomFlag = true;
}
}
}
</span>
代码,相比上篇文章进行了稍微的改良。看起来更加明确。
所以,上面就是我们常常看到的对不同View之间进行连贯性滑动的解决办法。
PS:对于这种事件类的调试,如果身边没有源码可以调试的话,那么打印日志是一个好的解决办法。
经过这两篇文章,相信你一定会触摸事件之间的关系有了更进一步的了解。