最新消息:欢迎访问Android开发中文站!商务联系微信:loading_in

Android Touch事件传递机制详解 下

开发进阶 loading 15123浏览 0评论

尊重原创:http://blog.csdn.net/yuanzeyao/article/details/38025165

资源下载:http://download.csdn.net/detail/yuanzeyao2008/7660997

在前一篇文章中,我主要讲解了Android源码中的Touch事件的传递过程,现在我想使用一个demo以及一个实例来学习一下Andorid中的Touch事件处理过程。

在Android系统中,和Touch事件分发和处理紧密相关的三个函数如下:

(1) public boolean dispatchTouchEvent(MotionEvent ev)

(2) public boolean onInterceptTouchEvent(MotionEvent ev)

(3) public boolean onTouchEvent(MotionEvent event)

这三个方法我在前一篇文章中都对他们的源码进行了分析:方法1主要是对Touch事件进行分发,方法2主要是对Touch事件进行拦截,方法3是对Touch事件进行处理

这三个方法主要存在于ViewGroup,View,Activity中,具体情况如下图:

ViewGroup View Activity
dispatchTouchEvent
onInterceptTouchEvent
onTouchEvent

下面我们就使用一个demo来看看这些方法的执行流程:

自定义一个类:MyLayoutFirst.java

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;

public class MyLayoutFirst extends LinearLayout {
    private static final String TAG = "MyLayoutFirst";

    public MyLayoutFirst(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.w("yzy", "MyLayoutFirst->onInterceptTouchEvent->" + MyUtils.getActionName(ev));
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("yzy", "MyLayoutFirst->onTouchEvent->" + MyUtils.getActionName(event));
        return super.onTouchEvent(event);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.i("yzy", "MyLayoutFirst->dispatchTouchEvent->" + MyUtils.getActionName(ev));
        return super.dispatchTouchEvent(ev);
    }

}

自定义一个类;MyLayoutSecond.java

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;

public class MyLayoutSecond extends LinearLayout {
    private static final String TAG = "MyLayoutSecond";

    public MyLayoutSecond(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("yzy", "MyLayoutSecond->MyLayoutSecond->" + MyUtils.getActionName(event));
        return super.onTouchEvent(event);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.w("yzy", "MyLayoutSecond->onInterceptTouchEvent->" + MyUtils.getActionName(ev));
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.i("yzy", "MyLayoutSecond->dispatchTouchEvent->" + MyUtils.getActionName(ev));
        return super.dispatchTouchEvent(ev);
    }

}

加入到main_layout.xml中

<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">

    <com.event.demo.MyLayoutFirst
        android:id="@+id/layout_first"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#FF0000">

        <com.event.demo.MyLayoutSecond
            android:id="@+id/layout_second"
            android:layout_width="320dip"
            android:layout_height="120dip"
            android:layout_gravity="center"
            android:background="#0000FF">

        </com.event.demo.MyLayoutSecond>
    </com.event.demo.MyLayoutFirst>

</RelativeLayout>

MainActivity中加入onTouchEvent方法

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MotionEvent;

import cn.ipaynow.parking.R;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.i("yzy", "MainActivity->dispatchTouchEvent->" + MyUtils.getActionName(ev));
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("yzy", "MainActivity->onTouchEvent->" + MyUtils.getActionName(event));
        return super.onTouchEvent(event);
    }
}

最后就一个工具类,用来将事件id转换为字符串。

import android.view.MotionEvent;

public class MyUtils
{
    private static final String TAG = "MyUtils";
    public static String getActionName(MotionEvent event)
    {
        String name="";
        switch(event.getAction())
        {
            case MotionEvent.ACTION_DOWN:
                name="ACTION_DOWN";
                break;
            case MotionEvent.ACTION_MOVE:
                name="ACTION_MOVE";
                break;
            case MotionEvent.ACTION_UP:
                name="ACTION_UP";
                break;
        }
        return name;
    }
}

运行效果如图:

20140721223223828

其中蓝色部分是MyLayoutSecond.java ,红色部分是MyLayoutFirst.java

现在我点击一下蓝色部分:运行结果如图:

20140721223312078

从图中可以看出,事件最先被Activity捕获,然后分发给 MyLayoutFirst,MyLayoutFirst首先调用自身的onInterceptTouchEvent判断是否将该事件拦截,由于默认返回是false,所以没有拦截,从而事件分发给了MyLayoutSecond,MyLayoutSecond同样通过dispatchTouchEvent分发出去,分发出去之前同样检查是否被拦截,默认都是没有被拦截的,但是由于MyLayoutSecond是没有子视图的,所有最终事件有自己处理,调用自身的onTouchEvent方法,由于该方法默认返回的是false,所以认为此事件是没有被消费掉的,继续传递到了MyLayoutFirst中,同样也没有消费这个事件,最终传递到了Mainactivity,继续往后看发现后面的ACTION_MOVE和ACTION_UP并没有传入MyLayoutFirst和MyLayoutSecond,这是因为一旦某一个事件没有被处理,后面的事件是不会被分发的。所以ACTION_MOVE和ACTION_UP直接被MainActivity处理掉了。

下面再看第二种情况:

MainActivity MyLayoutFirst MyLayoutSecond
dispatchTouchEvent super.dispatchTouchEvent super.dispatchTouchEvent super.dispatchTouchEvent
onInterceptTouchEvent true super.onInterceptTouchEvent(ev)
onTouchEvent super.onTouchEvent super.onTouchEvent super.onTouchEvent

运行结果如下:

3

从图中可以看出,事件传递到了MyLayoutFirst后没有分发到MyLayoutSecond,直接调用自身的onTouchEvent,由于返回的是false,导致事件没有消费,最终传递给了MainActivity,而且后续事件也没有传递到MyLayoutFirst和MyLayoutSecond,直接被MainActivity处理

第三种情况:

MainActivity MyLayoutFirst MyLayoutSecond
dispatchTouchEvent super.dispatchTouchEvent super.dispatchTouchEvent super.dispatchTouchEvent
onInterceptTouchEvent true super.onInterceptTouchEvent(ev)
onTouchEvent super.onTouchEvent true super.onTouchEvent

运行结果:

5

和情况二不同的是MyLayoutFirst的onTouchEvent返回了true,也就是说MyLayoutFirst消费了此事件,所以ACTION_DOWN也没有再传给MainActivity,并且ACTION_MOVE和ACTION_UP均传给了MyLayoutFirst

第四中情况:

MainActivity MyLayoutFirst MyLayoutSecond
dispatchTouchEvent super.dispatchTouchEvent super.dispatchTouchEvent super.dispatchTouchEvent
onInterceptTouchEvent super.onInterceptTouchEvent(ev) super.onInterceptTouchEvent(ev)
onTouchEvent super.onTouchEvent super.onTouchEven true

运行结果:

4

发现所有的事件都是传递到了MyLayoutSecond后被消费了

其实还有很多其他组合方式,大家如果又兴趣可以自己尝试改变每个函数的返回值,查看打印结果,这里我就不一一列举了。。。。。

最后我会提供一个小demo演示如何解决滑动冲突,背景如下:

一个ViewPager里面包含两个Framgent,有一个Fragment里面有一个HorizontalListView ,如何滑动冲突?
我就贴出关键代码吧

horizontal=(HorizontalListView)view.findViewById(R.id.hscroll);
horizontal.setOnTouchListener(new OnTouchListener()
{

    @Override
    public boolean onTouch(View arg0, MotionEvent event)
    {
        if(event.getAction()==MotionEvent.ACTION_DOWN)
        {
            parent.requestDisallowInterceptTouchEvent(true);
        }else if(event.getAction()==MotionEvent.ACTION_UP)
        {
            parent.requestDisallowInterceptTouchEvent(false);
        }
        return false;
    }
});

加入这段代码就可以避免滑动冲突了,至于为什么大家可以参考我的前以前文章《Android Touch 事件传递机制详解 上》 这两个demo的例子我均会上传下载的

失败是什么?没有什么,只是更走近成功一步;成功是什么?就是走过了所有通向失败的路,只剩下一条路,那就是成功的路。

转载请注明:Android开发中文站 » Android Touch事件传递机制详解 下

您必须 登录 才能发表评论!