自定义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 ...
随机推荐
- JS获取页面,元素,窗口和返回页面,元素,窗口的宽高以及滚动值
jquery获取页面,元素,窗口的宽高以及滚动值 //获取浏览器显示区域(可视区域)的高度 : $(window).height(); //获取浏览器显示区域(可视区域)的宽度 : $(window) ...
- ajax实例解析
function showHint(str) { var xmlhttp; if (str.length==0) { document.getElementById("txtHint&quo ...
- Springmvc使用阿里巴巴的fastjson传输到前台中文乱码的解决方案,他娘的大家都少制造垃圾,学习过程将会多么快乐
弄了大概七八个小时吧 都他妈比的抄来抄去,一分一秒的去试错 最终参考这个问题解决了乱码的情况https://bbs.csdn.net/topics/392169549 412 需要在springmvc ...
- 洛谷 P1217 [USACO1.5]回文质数 Prime Palindromes【取回文数/数论/字符串】
题目描述 因为151既是一个质数又是一个回文数(从左到右和从右到左是看一样的),所以 151 是回文质数. 写一个程序来找出范围[a,b](5 <= a < b <= 100,000 ...
- input输入框校验
1.只能输入数字,当输入不符字符删除,光标位置不变 //只能输入数字 function onlyNumTrue(obj){ var reg = /[^\d]/g; var pos = obj.sele ...
- Python实例 包机制
每一个.py文件称为一个module,module之间可以互相导入.请参看以下例子: # a.py def add_func(a,b): return a+b # b.py from a im ...
- cron服务
相对与at,cron的优点就是能够周期性的执行某个命令,at却只能执行一次,cron的后台进程名字是crond ,cron也是system V的服务,所以我们可以service crond start ...
- 运行Jmeter时,响应数据中文乱码问题解决办法
需要修改jmeter中的配置,在Jmeter安装目录/bin/jmeter.properties文件中进行修改: sampleresult.default.encoding默认为ISO-8859-1, ...
- python fullmatch函数
- golang中特殊的标识符
你会发现在 Go 代码中的几乎所有东西都有一个名称或标识符.另外,Go 语言也是区分大小写的,这与 C 家族中的其它语言相同.有效的标识符必须以字符(可以使用任何 UTF-8 编码的字符或 _)开头, ...