1.先来看看原始需求,自定义一个seekbar
关键代码如下,
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
lastX = event.rawX.toInt()
if (isStartAnimator) {
return false
}
// check finger is touch above slide
if (event.x > slideLeft && event.x < slideBitmap.width + slideLeft) {
slideIsMoving = true
return true
}
}
MotionEvent.ACTION_MOVE -> {
val dx = event.rawX.toInt() - lastX
slideLeft += dx
if (slideLeft < 0) {
slideLeft = 0
}
if (slideLeft > slideBgWidth + margin * 2 - slideBitmap.width) {
slideLeft = slideBgWidth + margin * 2 - slideBitmap.width
}
if (slideIsMoving) {
invalidate()
lastX = event.rawX.toInt()
}
}
MotionEvent.ACTION_UP -> if (slideIsMoving) {
backZero()
slideIsMoving = false
}
else -> {
}
}
return super.onTouchEvent(event)
}
主要流程就是监听用户触摸事件,当用户点击在滑块上面时,开始处理用户滑动事件(ACTION_DOWN 返回true),然后计算用户滑动距离,修改slideLeft 值,并且调用invalidate() 刷新界面。在onDraw中根据slideLeft值,修改滑块位置。
2.新的需求是在viewpager中,此时我的滑动就出现了问题
查看日志如下:
2021-10-15 15:21:56.324 17940-17940/com.example.myapplication I/CustomSeekBar: ACTION_DOWN
2021-10-15 15:21:56.433 17940-17940/com.example.myapplication I/CustomSeekBar: ACTION_MOVE
2021-10-15 15:21:56.450 17940-17940/com.example.myapplication I/CustomSeekBar: ACTION_MOVE
2021-10-15 15:21:56.466 17940-17940/com.example.myapplication I/CustomSeekBar: ACTION_CANCEL
正常情况下应该是down- move-up
此时出现了down-move-cancel
为什么会这样呢?
3.咱们带着问题来分析一下Android的事件分发处理流程
咱们从activity接收到事件开始,底层的不用在意。
先来看看activity布局层级
基于Android SDK 30普通布局
activity布局如下:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/xml_root"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.viewpager.widget.CustomViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
图中可以看到activity,和window其实都不是view,DecorView才是咱们布局的顶层view,然后到咱们自己的布局上一共已经有5层view。从第6层起,刚好就是咱们自己布局文件的根view。
再来看看activity事件分化流程
以下是一次完整的事件分化处理过程:
请注意,viewPager onTouchEvent 默认返回true,以下log是我修改代码后的
2021-10-19 16:48:21.917 4857-4857/com.example.myapplication I/GUOHENG: SeekBarActivity dispatchTouchEvent
2021-10-19 16:48:21.925 4857-4857/com.example.myapplication I/GUOHENG: CustomViewPager dispatchTouchEvent
2021-10-19 16:48:21.926 4857-4857/com.example.myapplication I/GUOHENG: CustomViewPager onInterceptTouchEvent
2021-10-19 16:48:21.931 4857-4857/com.example.myapplication I/GUOHENG: CustomSeekBar dispatchTouchEvent
2021-10-19 16:48:21.931 4857-4857/com.example.myapplication I/GUOHENG: CustomSeekBar onTouchEvent
2021-10-19 16:48:25.761 4857-4857/com.example.myapplication I/GUOHENG: CustomViewPager onTouchEvent
2021-10-19 16:48:28.814 4857-4857/com.example.myapplication I/GUOHENG: SeekBarActivity onTouchEvent
点击seekbar时的事件分化及处理
点击时
2021-10-19 17:00:41.669 5464-5464/com.example.myapplication I/GUOHENG: SeekBarActivity dispatchTouchEvent
2021-10-19 17:00:41.669 5464-5464/com.example.myapplication I/GUOHENG: CustomViewPager dispatchTouchEvent
2021-10-19 17:00:41.669 5464-5464/com.example.myapplication I/GUOHENG: CustomViewPager onInterceptTouchEvent
2021-10-19 17:00:41.670 5464-5464/com.example.myapplication I/GUOHENG: CustomSeekBar dispatchTouchEvent
2021-10-19 17:00:41.670 5464-5464/com.example.myapplication I/GUOHENG: CustomSeekBar onTouchEventACTION_DOWN
2021-10-19 17:00:41.670 5464-5464/com.example.myapplication I/GUOHENG: CustomViewPager onTouchEvent
2021-10-19 17:00:41.780 5464-5464/com.example.myapplication I/GUOHENG: SeekBarActivity dispatchTouchEvent
2021-10-19 17:00:41.780 5464-5464/com.example.myapplication I/GUOHENG: CustomViewPager dispatchTouchEvent
2021-10-19 17:00:41.780 5464-5464/com.example.myapplication I/GUOHENG: CustomViewPager onTouchEvent
滑动seekbar时的事件分化及处理
滑动时
2021-10-19 17:02:19.707 5577-5577/com.example.myapplication I/GUOHENG: SeekBarActivity dispatchTouchEvent
2021-10-19 17:02:19.707 5577-5577/com.example.myapplication I/GUOHENG: CustomViewPager dispatchTouchEvent
2021-10-19 17:02:19.708 5577-5577/com.example.myapplication I/GUOHENG: CustomViewPager onInterceptTouchEvent
2021-10-19 17:02:19.708 5577-5577/com.example.myapplication I/GUOHENG: CustomSeekBar dispatchTouchEvent
2021-10-19 17:02:19.708 5577-5577/com.example.myapplication I/GUOHENG: CustomSeekBar onTouchEventACTION_DOWN
2021-10-19 17:02:19.778 5577-5577/com.example.myapplication I/GUOHENG: SeekBarActivity dispatchTouchEvent
2021-10-19 17:02:19.778 5577-5577/com.example.myapplication I/GUOHENG: CustomViewPager dispatchTouchEvent
2021-10-19 17:02:19.778 5577-5577/com.example.myapplication I/GUOHENG: CustomViewPager onInterceptTouchEvent
2021-10-19 17:02:19.779 5577-5577/com.example.myapplication I/GUOHENG: CustomSeekBar dispatchTouchEvent
2021-10-19 17:02:19.779 5577-5577/com.example.myapplication I/GUOHENG: CustomSeekBar onTouchEventACTION_MOVE
2021-10-19 17:02:19.779 5577-5577/com.example.myapplication I/GUOHENG: SeekBarActivity onTouchEvent
2021-10-19 17:02:19.794 5577-5577/com.example.myapplication I/GUOHENG: SeekBarActivity dispatchTouchEvent
2021-10-19 17:02:19.794 5577-5577/com.example.myapplication I/GUOHENG: CustomViewPager dispatchTouchEvent
2021-10-19 17:02:19.794 5577-5577/com.example.myapplication I/GUOHENG: CustomViewPager onInterceptTouchEvent
2021-10-19 17:02:19.795 5577-5577/com.example.myapplication I/GUOHENG: CustomSeekBar dispatchTouchEvent
2021-10-19 17:02:19.795 5577-5577/com.example.myapplication I/GUOHENG: CustomSeekBar onTouchEventACTION_MOVE
2021-10-19 17:02:19.795 5577-5577/com.example.myapplication I/GUOHENG: SeekBarActivity onTouchEvent
2021-10-19 17:02:19.812 5577-5577/com.example.myapplication I/GUOHENG: SeekBarActivity dispatchTouchEvent
2021-10-19 17:02:19.813 5577-5577/com.example.myapplication I/GUOHENG: CustomViewPager dispatchTouchEvent
2021-10-19 17:02:19.813 5577-5577/com.example.myapplication I/GUOHENG: CustomViewPager onInterceptTouchEvent
2021-10-19 17:02:19.814 5577-5577/com.example.myapplication I/GUOHENG: CustomSeekBar dispatchTouchEvent
2021-10-19 17:02:19.814 5577-5577/com.example.myapplication I/GUOHENG: CustomSeekBar onTouchEventACTION_CANCEL
2021-10-19 17:02:19.816 5577-5577/com.example.myapplication I/GUOHENG: SeekBarActivity onTouchEvent
2021-10-19 17:02:19.828 5577-5577/com.example.myapplication I/GUOHENG: SeekBarActivity dispatchTouchEvent
2021-10-19 17:02:19.828 5577-5577/com.example.myapplication I/GUOHENG: CustomViewPager dispatchTouchEvent
2021-10-19 17:02:19.828 5577-5577/com.example.myapplication I/GUOHENG: CustomViewPager onTouchEvent
出现滑动冲突原因就是父view将事件抢过去处理了,给我们发了cancel事件。导致后续move事件被父view处理,seekbar接收不到后续事件。
通过视频也能看到滑动一段距离后,viewpager出现了滑动不了的提示。关键代码如下:
viewpager.java onInterceptTouchEvent()
case MotionEvent.ACTION_MOVE: {
......
if (xDiff > mTouchSlop && xDiff * 0.5f > yDiff) {
Log.v(TAG, "Starting drag!" + mTouchSlop);
mIsBeingDragged = true;
requestParentDisallowInterceptTouchEvent(true);
setScrollState(SCROLL_STATE_DRAGGING);
mLastMotionX = dx > 0
? mInitialMotionX + mTouchSlop : mInitialMotionX - mTouchSlop;
mLastMotionY = y;
setScrollingCacheEnabled(true);
}
viewGroup.java
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
解决冲突:在viewgroup中判断是否拦截当前事件前,会有一个是否允许拦截的判断,我们可以在滑块滑动时不让父亲(viewpager)拦截
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;
}
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_MOVE -> {
Log.i("GUOHENG", "CustomSeekBar onTouchEvent"+ "ACTION_MOVE")
......
......
parent.requestDisallowInterceptTouchEvent(true)
}
此刻就能正常滑动了。