自定义View系列教程08--滑动冲突的产生及其处理
Android多分辨率适配框架(1)— 核心基础
Android多分辨率适配框架(2)— 原理剖析
Android多分辨率适配框架(3)— 使用指南
自定义View系列教程00–推翻自己和过往,重学自定义View
自定义View系列教程01–常用工具介绍
自定义View系列教程02–onMeasure源码详尽分析
自定义View系列教程03–onLayout源码详尽分析
自定义View系列教程04–Draw源码分析及其实践
自定义View系列教程05–示例分析
自定义View系列教程06–详解View的Touch事件处理
自定义View系列教程07–详解ViewGroup分发Touch事件
自定义View系列教程08–滑动冲突的产生及其处理
PS:如果觉得文章太长,那就直接看视频吧
在之前的几篇文章中,我们已经分析了View对于Touch的处理以及ViewGroup对于Touch事件的分发。
但在开发中时常遇到一个棘手的问题:Touch事件的滑动冲突。比如ListView嵌套ScrollView,ViewPager嵌套ScrollView,ListView嵌套ScrollView时常常发生。
这些滑动冲突的产生,一般而言都具有以下特点:
- 子View和父View都有滑动的需求
- 滑动事件不能准确地传递给合适的View
其实,Google官方不鼓励这种滑动嵌套的设计;但是在实际项目中却会碰到客户提出类似的要求。既然不太合理的东西已经存在了,开发人员再去抱怨设计或者客户都没有实际作用了,只有想办法完成对应的功能。
那么,有哪些方法可以解决滑动冲突呢?
子View禁止父View拦截Touch事件
在分析ViewGroup的dispatchTouchEvent()源码时,我们知道:Touch事件是由父View分发的。如果一个Touch事件是子View需要的,但是被其父View拦截了,子View就无法处理该Touch事件了。在此情形下,子View可以调用requestDisallowInterceptTouchEvent( )禁止父View对Touch的拦截在父View中准确地进行事件分发和拦截
我们可以重写父View中与Touch事件分发相关的方法,比如onInterceptTouchEvent( )。这些方法中摒弃系统默认的流程,结合自身的业务逻辑重写该部分代码,从而使父View放行子View需要的Touch。
在此,通过一个示例展示解决滑动冲突的常用方法。
示例效果,请看下图:
它的布局文件如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.stay4it.testtouch3.MainActivity">
<HorizontalScrollView
android:id="@+id/horizontalScrollView"
android:layout_height="450dp"
android:layout_width="match_parent"
android:scrollbars="horizontal"
android:fillViewport="true">
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</HorizontalScrollView>
</RelativeLayout>
在该示例中,我们在HorizontalScrollView中嵌入ViewPager浏览图片。由于HorizontalScrollView和ViewPager都可以响应水平滑动,当我们用手指切换图片时发现ViewPager无法正常的滚动。这是因为Touch事件被HorizontalScrollView处理,根本没有传递给ViewPager。
嗯哼,滑动冲突的原因已经清楚了,我们现在来解决这个问题。
第一种解决方法
在ViewPager中禁止HorizontalScrollView拦截Touch事件
package com.stay4it.testtouch3;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.view.MotionEvent;
import android.view.View;
/**
* 原创作者
* 谷哥的小弟
*
* 博客地址
* http://blog.csdn.net/lfdfhl
*/
public class MainActivity extends AppCompatActivity {
private ViewPager mViewPager;
private ViewPagerAdapter mViewPagerAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init(){
mViewPager = (ViewPager) findViewById(R.id.viewPager);
mViewPagerAdapter=new ViewPagerAdapter(this);
mViewPager.setAdapter(mViewPagerAdapter);
mViewPager.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
mViewPager.getParent().requestDisallowInterceptTouchEvent(true);
return false;
}
});
}
}
在此,请关注第32行代码
mViewPager.getParent().requestDisallowInterceptTouchEvent(true);
此处首先利用 mViewPager.getParent( )得到了ViewPager的父View即HorizontalScrollView;然后再执行requestDisallowInterceptTouchEvent( )方法从而禁止掉父View对于Touch事件的传递。
第二种解决方法
在自定义HorizontalScrollView中合理地处理Touch事件的拦截和分发
package com.stay4it.testtouch4;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.HorizontalScrollView;
/**
* 原创作者
* 谷哥的小弟
*
* 博客地址
* http://blog.csdn.net/lfdfhl
*/
public class HorizontalScrollViewSubclass extends HorizontalScrollView{
private float LastX;
private float LastY;
private float distanceX;
private float distanceY;
public HorizontalScrollViewSubclass(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
distanceX = 0f;
distanceY = 0f;
LastX = ev.getX();
LastY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
final float currentX = ev.getX();
final float currentY = ev.getY();
distanceX += Math.abs(currentX - LastX);
distanceY += Math.abs(currentY - LastY);
LastX = currentX;
LastY = currentY;
if(distanceX > distanceY){
return false;
}
break;
case MotionEvent.ACTION_UP:
break;
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
return super.onTouchEvent(ev);
}
}
从这段代码中,我们的主要操作是在自定义HorizontalScrollView中重写了onInterceptTouchEvent( )。
在该方法中如果判定Touch是水平的滑动:
if(distanceX > distanceY)
那么直接返回false不拦截该Touch。这样Touch事件就可以顺利传递到其子View即ViewPager中,从而正常地切换图片。
既然已经采用了自定义的HorizontalScrollView就不要忘记修改布局文件喔~
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.stay4it.testtouch4.MainActivity">
<com.stay4it.testtouch4.HorizontalScrollViewSubclass
android:id="@+id/horizontalScrollView"
android:layout_height="450dp"
android:layout_width="match_parent"
android:scrollbars="horizontal"
android:fillViewport="true"
>
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.stay4it.testtouch4.HorizontalScrollViewSubclass>
</RelativeLayout>
嗯哼,在此通过一个完整的示例展示了事件滑动冲突的常用解决方式。
在实际的项目开发中所遇到的滑动冲突可能比这要复杂,但是处理冲突的基本原理和方式是相同的。
至此,自定义View系列教程就全部结束了
多谢大家的指正和鼓励
Thank you very much
PS:如果觉得文章太长,那就直接看视频吧
自定义View系列教程08--滑动冲突的产生及其处理的更多相关文章
- 自定义View系列教程01--常用工具介绍
站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)- 核心基础 Android多分辨率适配框架(2)- 原理剖析 Android多分辨率适配框架(3)- 使用指南 自定 ...
- 自定义View系列教程07--详解ViewGroup分发Touch事件
深入探讨Android异步精髓Handler 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)- 核心基础 Android多分辨率适配框架(2)- 原理剖析 Andr ...
- 自定义View系列教程06--详解View的Touch事件处理
深入探讨Android异步精髓Handler 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)- 核心基础 Android多分辨率适配框架(2)- 原理剖析 Andr ...
- 自定义View系列教程05--示例分析
站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)- 核心基础 Android多分辨率适配框架(2)- 原理剖析 Android多分辨率适配框架(3)- 使用指南 自定 ...
- 自定义View系列教程04--Draw源码分析及其实践
深入探讨Android异步精髓Handler 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)- 核心基础 Android多分辨率适配框架(2)- 原理剖析 Andr ...
- 自定义View系列教程03--onLayout源码详尽分析
深入探讨Android异步精髓Handler 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)- 核心基础 Android多分辨率适配框架(2)- 原理剖析 Andr ...
- 自定义View系列教程02--onMeasure源码详尽分析
深入探讨Android异步精髓Handler 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)- 核心基础 Android多分辨率适配框架(2)- 原理剖析 Andr ...
- wing带你玩转自定义view系列(3)模仿微信下拉眼睛
发现了爱神的自定义view系列,我只想说一个字:凸(艹皿艹 ) !!相见恨晚啊,早看到就不会走这么多弯路了 另外相比之下我这完全是小儿科..所以不说了,这篇是本系列完结篇....我要从零开始跟随爱哥脚 ...
- wing带你玩转自定义view系列(2) 简单模仿qq未读消息去除效果
上一篇介绍了贝塞尔曲线的简单应用 仿360内存清理效果 这一篇带来一个 两条贝塞尔曲线的应用 : 仿qq未读消息去除效果. 转载请注明出处:http://blog.csdn.net/wingicho ...
随机推荐
- node 和npm环境安装
node 安装 1.下载node二进制文件 [root@baolin-images#>> ~]#wget https://nodejs.org/dist/v10.16.0/node-v10 ...
- A Simple Problem with Integers POJ - 3468 (线段树)
思路:线段树,区间更新,区间查找 #include<iostream> #include<vector> #include<string> #include< ...
- 数据库操作之Spring JDBCTemplate(postgresql)
本文总结了两种使用JDBCTemplate进行数据库CRUD操作的例子,我用的是pg,废话不说,直接开始吧. 先贴一张目录结果图吧: 上图中最主要的是配置文件和所需的各种jar包. 一.通过属性文件的 ...
- 关于Layui 响应式移动端轮播图的问题
用layui做轮播图,在手机上宽度异常, 可通过以下方法解决, 不喜欢layui的同学可以选择Swiper // 轮播图 layui.use('carousel', function () { var ...
- 手机端点击键盘无法获取keyCode值的部分时隐藏键盘并执行事件
用计时器监视window.innerHeight高度改变来判断.触发键盘其他地方也有事件反应 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 ...
- freemarker 取值(插值)(转)
Java数据模型 1)基本数据类型取值 八种基本的java类型:byte.short.int.long:float,double:char:boolean 对应的封装类型:Byte.Short.Int ...
- Vue动态加载异步组件
背景: 目前我们项目都是按组件划分的,然后各个组件之间封装成产品.目前都是采用iframe直接嵌套页面.项目中我们还是会碰到一些通用的组件跟业务之间有通信,这种情况下iframe并不是最好的选择,if ...
- DOM事件机制
前言 本文主要介绍DOM事件级别.DOM事件模型.事件流.事件代理和Event对象常见的应用,希望对你们有些帮助和启发! 本文首发地址为GitHub博客,写文章不易,请多多支持与关注! 一.DOM事件 ...
- oracle 处理表的一列
---删除一列和数据一起删除了. ALTER TABLE 表名 DROP COLUMN 列名; ---添加一列 alert table 表名 add column 列名; ---只删除一列的数据 没有 ...
- day38 18-Spring的XML和注解的结合使用
什么情况下使用XML,什么情况下使用注解?又有XML,又有注解,开发的时候使用哪种? XML:结构清晰,配置麻烦. 注解:简单, 它俩的结合点在属性注入上. 两种方式结合:一般使用XML注册Bean, ...