笔者是面霸,面试200+场 当过考官:面过别人300+场 去过500强,也呆过初创公司。
关注我就能达到大师级水平,这话我终于敢说了, 年薪60万不是梦!
斩获腾讯、华为、字节跳动,蚂蚁金服,oppo,VIVO,安卓岗offer!我有一套速通大厂技巧分享给你!
请问简书的格式怎么排版??每次写完都变格式了,非常影响阅读。非常苦恼
面试五连环
1.滑动冲突的几种情况
2.滑动冲突解决方案和源码分析
3.滑动冲突的几种解决方案的使用场景?什么时候用内部拦截?
4.webview滑动冲突如何解决?x5 webview如何拦截?
5.滑动冲突实践
1.滑动冲突的几种情况
1).点击时间和滑动事件(比如悬浮球)
2).外层与内层滑动方向不一致,外层ViewGroup是可以横向滑动的,内层View是可以竖向滑动的(类似ViewPager,每个页面里面是ListView)
3).外层与内层滑动方向一致,外层ViewGroup是可以竖向滑动的,内层View同样也是竖向滑动的(类似ScrollView包裹ListView)
2.滑动冲突解决方案和源码分析
针对上面第一种场景,onTouch和onTouchEvent事件,即可
针对上面第二种场景,由于外部与内部的滑动方向不一致,那么我们可以根据当前滑动方向,水平还是垂直来判断这个事件到底该交给谁来处理。至于如何获得滑动方向,我们可以得到滑动过程中的两个点的坐标。如竖直距离与横向距离的大小比较;
针对第三种场景,由于外部与内部的滑动方向一致,那么不能根据滑动角度、距离差或者速度差来判断。这种情况下必需通过业务逻辑来进行判断。比较常见ScrollView嵌套了ListView。
套路一 外部拦截法:
即父View根据需要对事件进行拦截。逻辑处理放在父View的onInterceptTouchEvent方法中。我们只需要重写父View的onInterceptTouchEvent方法,并根据逻辑需要做相应的拦截即可。
publicbooleanonInterceptTouchEvent(MotionEvent event){booleanintercepted =false;intx = (int) event.getX();inty = (int) event.getY();switch(event.getAction()) {caseMotionEvent.ACTION_DOWN: { intercepted =false;break; }caseMotionEvent.ACTION_MOVE: {if(满足父容器的拦截要求) { intercepted =true; }else{ intercepted =false; }break; }caseMotionEvent.ACTION_UP: { intercepted =false;break; }default:break; } mLastXIntercept = x; mLastYIntercept = y;returnintercepted; }
上面伪代码表示外部拦截法的处理思路,需要注意下面几点(down和up一样不拦截,move根据条件拦截)
ACTION_DOWN 一定返回false,不要拦截它,否则根据View事件分发机制,后续ACTION_MOVE 与 ACTION_UP事件都将默认交给父View去处理!
在ACTION_MOVE方法中进行判断,根据业务逻辑需要,如果需要父View处理则返回true,否则返回false,事件分发给子View去处理。
原则上ACTION_UP也需要返回false,如果返回true,并且滑动事件交给子View处理,那么子View将接收不到ACTION_UP事件,子View的onClick事件也无法触发。而父View不一样,如果父View在ACTION_MOVE中开始拦截事件,那么后续ACTION_UP也将默认交给父View处理!
套路二 内部拦截法:
即父View不拦截任何事件,所有事件都传递给子View,子View根据需要决定是自己消费事件还是给父View处理。这需要子View使用requestDisallowInterceptTouchEvent方法才能正常工作。下面是子View的dispatchTouchEvent方法的伪代码:
父View需要重写onInterceptTouchEvent方法:
为什么要重写父类的onInterceptTouchEvent?因为默认不拦截,你需要的是拦截它
publicbooleanonInterceptTouchEvent(MotionEvent event){intaction = event.getAction();if(action == MotionEvent.ACTION_DOWN) {//down不拦截,给子类returnfalse; }else{//拦截returntrue; } }
重写子类的dispatchTouchEvent()方法
public boolean dispatchTouchEvent(MotionEvent event) {intx = (int) event.getX();inty = (int) event.getY();switch(event.getAction()) {caseMotionEvent.ACTION_DOWN: { parent.requestDisallowInterceptTouchEvent(true);break; }caseMotionEvent.ACTION_MOVE: {intdeltaX = x - mLastX;intdeltaY = y - mLastY;if(父容器需要此类点击事件) { parent.requestDisallowInterceptTouchEvent(false); }break; }caseMotionEvent.ACTION_UP: {break; }default:break; } mLastX = x; mLastY = y;returnsuper.dispatchTouchEvent(event); }
内部拦截法要求父View不能拦截ACTION_DOWN事件,由于ACTION_DOWN不受FLAG_DISALLOW_INTERCEPT标志位控制,一旦父容器拦截ACTION_DOWN那么所有的事件都不会传递给子View。
滑动策略的逻辑放在子View的dispatchTouchEvent方法的ACTION_MOVE中,如果父容器需要获取点击事件则调用 parent.requestDisallowInterceptTouchEvent(false)方法,让父容器去拦截事件。
你好,内部拦截法中父容器 onInterceptTouchEvent 方法 ACTION_MOVE、ACTION_UP返回true,子view 的dispatchTouchEvent 方法中ACTION_MOVE、ACTION_UP事件还能接受到吗,新手,这里不是很懂,求指教?
原理分析:
内部拦截法也叫View分发反向制约的方法?
拦截不拦截,由2个东西决定的。一个是requestDisllowIntercepter和onInterceptTouchEvent()2个决定的。
源码如下。
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mInputEventConsistencyVerifier !=null) {
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
}
// If the event targets the accessibility focused view and this is it, start
// normal event dispatch. Maybe a descendant is what will handle the click.
if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
ev.setTargetAccessibilityFocus(false);
}
if (actionMasked == MotionEvent.ACTION_DOWN
||mFirstTouchTarget !=null) {
final boolean disallowIntercept = (mGroupFlags &FLAG_DISALLOW_INTERCEPT) !=0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
}else {
intercepted =false;
}
原因:但是子元素可以通过requestDisallowInterceptTouchEvent来干预父元素的分发过程,但是down事件除外(因为down事件方法里,会清除所有的标志位)。
3.滑动冲突的几种解决方案的使用场景
1).onTouch和绘制不管。只管事件的拦截和分发,所以重要的方法是:VIewGourp的onInterceptTouchEvent和View的dispatchTouchEvent
2.down,move ,Up,都是否需要拦截?
ACTION_DOWN,都不要拦截子类
在这里,首先down事件父容器必须返回false ,因为若是返回true,也就是拦截了down事件,
那么后续的move和up事件就都会传递给父容器,子元素就没有机会处理事件了。其次是up事件也返回了false,一是因为up事件对父容器没什么意义,其次是因为若事件是子元素处理的,却没有收到up事件会让子元素的onClick事件无法触发。
3.要在MotionEvent.ACTION_MOVE根据情况,是父类滑动还是子类滑动
4.2种方式都要重写父View需要重写onInterceptTouchEvent方法:
可以看出外部拦截法实现起来更加简单,而且也符合View的正常事件分发机制,所以推荐使用外部拦截法(重写父View的onInterceptTouchEvent,父View决定是否拦截)来处理滑动冲突
外部拦截下面列子:
1.down 不拦截,否则up收不到,点击事件也会没有
2.move 更加业务判定。得到子view,滑动的距离和item的位置决定
3.up 不拦截
外部拦截法代码:
@OverridepublicbooleanonInterceptTouchEvent(MotionEventevent){boolean intercepted=false;inty=(int)event.getY();switch(event.getAction()){caseMotionEvent.ACTION_DOWN:{nowY=y;intercepted=super.onInterceptTouchEvent(event);break;}caseMotionEvent.ACTION_MOVE:{if(mListView.getFirstVisiblePosition()==0&&y>nowY){intercepted=true;break;}elseif(mListView.getLastVisiblePosition()==mListView.getCount()-1&&y<nowY){intercepted=true;break;}intercepted=false;break;}caseMotionEvent.ACTION_UP:{intercepted=false;break;}default:break;}returnintercepted;}
什么时候用内部拦截? 主要用内部拦截,系统里面的,horscorrlview和,比如recyleview.
什么时候用外部拦截? 主要看你哪个View是你自己的
面试有一个人问道:
一个ScrowView(父类)和一个RecycleView(子类)
他说重写子类的onIntecepter方法,让子类拦截,消费掉
5.滑动冲突实践
实战案例一:
ListView下拉刷新,需要ListView自身滑动,
但是当滑动到头部时需要ListView和Header一起滑动,也就是整个父容器的滑动。如果不处理好滑动冲突,就会出现各种意想不到情况。
实战案例二:
https://www.cnblogs.com/qhyuan1992/p/5385335.html
实战案例三:
.ViewPager中嵌套ViewPager怎么处理滑动冲突?
1.重写canScroll()方法
2.自己手写
https://blog.csdn.net/weixin_43917449/article/details/86519726
实战案例四:
一个scorview和一个日期选择器
自己通过实践,进行重绘写的
private void doMove(MotionEvent event)
{
mMoveLen += (event.getY() -mLastDownY);
if (mMoveLen >MARGIN_ALPHA *mMinTextSize /2)
{
// 往下滑超过离开距离
moveTailToHead();
mMoveLen =mMoveLen -MARGIN_ALPHA *mMinTextSize;
}else if (mMoveLen < -MARGIN_ALPHA *mMinTextSize /2)
{
// 往上滑超过离开距离
moveHeadToTail();
mMoveLen =mMoveLen +MARGIN_ALPHA *mMinTextSize;
}
mLastDownY = event.getY();
invalidate();
}
参考:
网友评论