深入探讨Android异步精髓Handler


站在源码的肩膀上全解Scroller工作机制


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时常常发生。

这些滑动冲突的产生,一般而言都具有以下特点:

  1. 子View和父View都有滑动的需求
  2. 滑动事件不能准确地传递给合适的View

其实,Google官方不鼓励这种滑动嵌套的设计;但是在实际项目中却会碰到客户提出类似的要求。既然不太合理的东西已经存在了,开发人员再去抱怨设计或者客户都没有实际作用了,只有想办法完成对应的功能。

那么,有哪些方法可以解决滑动冲突呢?

  1. 子View禁止父View拦截Touch事件

    在分析ViewGroup的dispatchTouchEvent()源码时,我们知道:Touch事件是由父View分发的。如果一个Touch事件是子View需要的,但是被其父View拦截了,子View就无法处理该Touch事件了。在此情形下,子View可以调用requestDisallowInterceptTouchEvent( )禁止父View对Touch的拦截

  2. 在父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--滑动冲突的产生及其处理的更多相关文章

  1. 自定义View系列教程01--常用工具介绍

    站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)- 核心基础 Android多分辨率适配框架(2)- 原理剖析 Android多分辨率适配框架(3)- 使用指南 自定 ...

  2. 自定义View系列教程07--详解ViewGroup分发Touch事件

    深入探讨Android异步精髓Handler 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)- 核心基础 Android多分辨率适配框架(2)- 原理剖析 Andr ...

  3. 自定义View系列教程06--详解View的Touch事件处理

    深入探讨Android异步精髓Handler 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)- 核心基础 Android多分辨率适配框架(2)- 原理剖析 Andr ...

  4. 自定义View系列教程05--示例分析

    站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)- 核心基础 Android多分辨率适配框架(2)- 原理剖析 Android多分辨率适配框架(3)- 使用指南 自定 ...

  5. 自定义View系列教程04--Draw源码分析及其实践

    深入探讨Android异步精髓Handler 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)- 核心基础 Android多分辨率适配框架(2)- 原理剖析 Andr ...

  6. 自定义View系列教程03--onLayout源码详尽分析

    深入探讨Android异步精髓Handler 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)- 核心基础 Android多分辨率适配框架(2)- 原理剖析 Andr ...

  7. 自定义View系列教程02--onMeasure源码详尽分析

    深入探讨Android异步精髓Handler 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)- 核心基础 Android多分辨率适配框架(2)- 原理剖析 Andr ...

  8. wing带你玩转自定义view系列(3)模仿微信下拉眼睛

    发现了爱神的自定义view系列,我只想说一个字:凸(艹皿艹 ) !!相见恨晚啊,早看到就不会走这么多弯路了 另外相比之下我这完全是小儿科..所以不说了,这篇是本系列完结篇....我要从零开始跟随爱哥脚 ...

  9. wing带你玩转自定义view系列(2) 简单模仿qq未读消息去除效果

    上一篇介绍了贝塞尔曲线的简单应用 仿360内存清理效果 这一篇带来一个  两条贝塞尔曲线的应用 : 仿qq未读消息去除效果. 转载请注明出处:http://blog.csdn.net/wingicho ...

随机推荐

  1. JS文字翻滚效果

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN""http://www.w3.org/TR/xhtm ...

  2. maven和java版本的对应关系

      发布日期 版 必需的Java版本 链接 2018年6月21日 3.5.4 Java 7 宣布,发布说明,参考文档 2018年3月8日 3.5.3 宣布,发布说明,参考文档 2017年10月24日 ...

  3. cvc-elt.1: 找不到元素 'beans' 的声明。springmvc netbeans maven

    搭建最基本的框架,出现问题,提示cvc-elt.1: 找不到元素 'beans' 的声明. HTTP Status 500 - Servlet.init() for servlet spring th ...

  4. Elasticsearch连接类(带密码)

    /** * 获取ES连接类 * * @author 陈康 * @description * @create 2019/08/15 **/ @Component("ElasticsearchR ...

  5. C#如何检测网络端口连接的状态

    原文:C#如何检测网络端口连接的状态 C#如何检测/监控远程连接网络端口的情况(例如:3389端口是否处于监听状态,是否建立了连接等). using System; using System.Coll ...

  6. Vue. 之 Element table 高度自适应

    Vue. 之 Element table 高度自适应 使用vue创建table后,其高度自适应浏览器高度. 在创建的 el-table 中添加:height属性,其值为一个变量(tableHeight ...

  7. 【模板】KMP [2017年5月计划 清北学堂51精英班Day2]

    Day2就搞一个KMP把  马拉车.AC自动机等准备省选的时候再说.. 模板题: 1204 寻找子串位置 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 青铜 Bronze     ...

  8. CommonJS、requirejs、ES6的对比

    文件路径 首先先搞清楚文件路径的写法,这里我总是记不住,有点晕,正好这次整理一下. 以 / 为起始,表示从根目录开始解析: 以 ./ 为起始,表示从当前目录开始解析: 以 ../ 为起始,表示从上级目 ...

  9. Vue--vue中的组件、组件绑定事件和数据、私有组件、子组件,父子组件参数互传

    一.公有组件以及组件的使用和特点 <!DOCTYPE html> <html lang="en"> <head> <meta charse ...

  10. 使用新版本5+SDK创建最简Android原生工程(Android studio)http://ask.dcloud.net.cn/article/13232

    1 使用Android Studio创建一个工程 2 删除原生工程中Java目录下系统默认创建的源代码 3 复制SDK->libs->lib.5plus.base-release.aar文 ...