zoukankan      html  css  js  c++  java
  • 自定义View系列教程08--滑动冲突的产生及其处理


    深入探讨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:如果觉得文章太长,那就直接看视频

  • 相关阅读:
    General Problem Solving Techniques [Beginner-1]~H
    做HDU1010 带出来一个小问题
    HDU1009
    hdu1007
    hdu 1008
    HDU1006
    hdu 1005
    hdu 1004
    UVA 10970 第一次比赛 D题 (后面才补的)
    LightOJ 1317 第八次比赛 A 题
  • 原文地址:https://www.cnblogs.com/hehe520/p/6147694.html
Copyright © 2011-2022 走看看