android 中的事件分发和事件拦截

代码:事件分发和事件拦截


事件分发:
dispathTouchEvent
1. 功能:从当前容器向自己内部的子容器/控件,传递触摸事件;
2. 特点:在传递的时候,会检查,触摸事件是否命中了内部的子控件,如果找到命中的子控件,那么,会调用子控件的 dispatchTouchEvent 方法,让子控件继续传递触摸事件;
3. 当 dispatchTouchEvent 方法找不到命中的子控件时候,当前的控件或者容器自己就进入到 onTouchEvent


onTouchEvent
1. onTouchEvent 如果这个方法,返回true, 这个方法所属的控件,相当于把事件消费掉了;
2. 返回true, 这个控件的父容器以及更上层的父容器都不会再调用 onTouchEvent了;
3. 对于UI结构来说,  onTouchEvent 这个方法,是从最内部子控件开始执行,之后就是每一级的父容器在执行onTouchEvent() ,相当于从最内部调用到最外部。



事件拦截:
拦截就是在容器执行的时候,不再向子控件发送事件
当前的ViewGroup 在进行事件分发的时候,先进行检查,调用自身的 onInterceptTouchEvent, 当这个方法,返回 true的时候,那么ViewGroup 将把事件拦截下来,不再给 自己的子控件发送事件了


注意:拦截的功能,只有ViewGroup以及子类,容器可以实现拦击;因为ViewGroup的dispatchTouchEvent内部会向子容器和空间发送消息;

其实换个位置思考,如果是View的话,它也没有子控件,它就是最后的那个接受者,就是最后的士兵,它也没有必要拦截.

+++++++++++++++++++++++++++++++++++++++++++++++++++++++

打个比方:事件分发就像是领导分配任务:或者让下面的人交经费。

连长分配任务给排长,

------------ 排长分配任务给班长,

-------------------------班长分配任务给士兵,

-------------------------士兵写报告交给班长,

-----------班长将报告交给排长,

排长将报告交给给连长  



分配任务就是dispatchTouchEvent

提交报告就是onTouchEvent 当然有的时候班长收到报告后也可以不给排长,可能这里是什么经费的,自己先用了,哈哈,如果是true的就是自己用了。

如果排长体恤下面的班长,那么他可以不分配任务给下面的人,自己拦截了然后处理了,onInterceptTouchEvent.

如果士兵非要做那么他要提个请求: requestDisallowInterceptTouchEvent


++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++



之前的参考记录:



1:事件分发

Android UI的结构
Android UI内部的坐标
Android UI常见的事件
Android UI事件目标的定位
Android UI事件的传递方式
Android UI事件拦截与分发


2:Android UI结构


修改public class MainActivity extends AppCompatActivity {
到public class MainActivity extends Activity {




点击Monitor
然后点击DDMS左边的有家好的图片,
选择Hierarchy View


MVC
PhoneWindow内部类DecorView
所有的Activity


actionbar 就是ViewStub
setContentView 就是FrameLayout 




现在已经五层了,所以listView 里面需要减少findViewById的次数




2.1 setContentView
public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }


我们可以自己查找一个PhoneWindow
其实也就是getWindow()返回的




PhoneWindow$DecorView 是一个帧布局FrameLayout+++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++
onDestory 就是删除
多了一个activity 就是在这个FrameLayout 上面又加一层
+++++++++++++++++++++++++++++++++++++++++++++++++++++
activity 的回退栈就是在这个FrameLayout 上面添加删除。
+++++++++++++++++++++++++++++++++++++++++++++++++++++




点击的位置,是由硬件传过来的。






event里面的getX 和getY是view 内部的,而不是全部屏幕的。
获取硬件的卓表,需要使用event.getRawX(),event.getRawY();








如果哦人能够起中有一个Button


点击的时候也是一层一级的。
其实大概是是先从DoctView-》contentView-》某个子容器-》button的容器


Android UI基础事件:一共分为四种:
1:MotionEvent:手在屏幕上的操作,也可以认为是是TouchEvent。因为TouchEvent的参数就是MotionEvnet
最重要的就是MotionEvent
2:KeyEvent:键盘事件,DPad(方向键),NumPad(数字键盘),
3:FocusEvent:焦点时间,主要影响了一个空间是否能够接受其他时间
例如键盘事件和触摸事件。谁有焦点,谁就可以接收时间
电视上面用到的比较多。


4:轨迹球的事件
几乎没有了。
----------------------------------------------------------
最重要的就是MotionEvent最重要的就是MotionEvent最重要的就是MotionEvent最重要的就是MotionEvent最重要的就是MotionEvent最重要的就是MotionEvent最重要的就是MotionEvent最重要的就是MotionEvent最重要的就是MotionEvent最重要的就是MotionEvent最重要的就是MotionEvent最重要的就是MotionEvent最重要的就是MotionEvent最重要的就是MotionEvent最重要的就是MotionEvent最重要的就是MotionEvent最重要的就是MotionEvent
--------------------------------------------------------------


onTouchEvent 的ontTouch Down返回true 就是后续的事件我自己处理了,
如果返回false,那么后续的事件交给父容器处理




===============================================================================




Android UI的事件传递啊




<LinearLayout 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" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">




    <TextView android:text="事件传递" android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">


        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="200dp">
            <TextView
                android:id="@+id/txt_click_me"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="点我一下"
                android:textSize="35dp"/>
        </FrameLayout>
    </LinearLayout>
</LinearLayout>


------------------------------------------------------------


public class MainActivity extends Activity implements View.OnTouchListener {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView textView = (TextView)findViewById(R.id.txt_click_me);
        //接口形式的触摸事件监听
        textView.setOnTouchListener(this);
    }


    /**
     *
     * @param v
     * @param event
     * @return
     */
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        //查看一下,onTouch 调用顺序,强制弄一个异常,你怎么这个时候调用关系就出来了e
        int a = 0,b=3;
        try {
            int c=b/3;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }
}




-------------------------------------------------------------------------
android 中所有的事件都是调用的loop来执行的。


创建一个自定义View LinearLiayout
public class RootLinearLayout extends LinearLayout {
    public RootLinearLayout(Context context) {
        super(context);
    }


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


-----------------------------------------------
public class RootLinearLayout extends LinearLayout {
    public RootLinearLayout(Context context) {
        super(context);
    }


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


    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                Log.d("151225MY", "dispatchTouchEvent 按下");
        }
        return super.dispatchTouchEvent(ev);
    }
}


直接修改main 里面的首行:




<com.kodulf.uistructdemo.widget.RootLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    
------------------------------------------------
在创建一个自定义View




public class SubLinearLayout extends LinearLayout {
    public SubLinearLayout(Context context) {
        super(context);
    }


    public SubLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                Log.d("151225MY", "SubLinearLayout dispatchTouchEvent 按下");
        }
        return super.dispatchTouchEvent(ev);
    }
}




---------------------------------------------------
然后把布局里面的第二个LinearLayout也修改为我们的自定义的View
<com.kodulf.uistructdemo.widget.SubLinearLayout




---------------------------------------------------
public class MyTextView extends TextView{


    public MyTextView(Context context) {
        super(context);
    }


    public MyTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                Log.d("151225MY", "MyTextView dispatchTouchEvent 按下");
        }
        return super.dispatchTouchEvent(ev);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
       int action = event.getAction();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                Log.d("151225MY", "MyTextView onTouchEvent 按下");
        }
        return super.onTouchEvent(event);
    }
}


--------------------------------------------------------
修改TextView 为<com.kodulf.uistructdemo.widget.MyTextView




最终的xml为:
<com.kodulf.uistructdemo.widget.RootLinearLayout 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" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">




    <TextView android:text="事件传递" android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <com.kodulf.uistructdemo.widget.SubLinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
            <com.kodulf.uistructdemo.widget.MyTextView
                android:id="@+id/txt_click_me"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="点我一下"
                android:textSize="35dp"/>


    </com.kodulf.uistructdemo.widget.SubLinearLayout>
</com.kodulf.uistructdemo.widget.RootLinearLayout>
----------------------------------------------------------------


<com.kodulf.uistructdemo.widget.RootLinearLayout 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" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"
    android:background="#F00">




    <TextView android:text="事件传递" android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <com.kodulf.uistructdemo.widget.SubLinearLayout
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:layout_marginTop="100dp"
        android:orientation="vertical"
        android:background="#0F0">
            <com.kodulf.uistructdemo.widget.MyTextView
                android:id="@+id/txt_click_me"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="点我一下"
                android:layout_margin="50dp"
                android:textSize="35dp"
                android:background="#00f"/>


    </com.kodulf.uistructdemo.widget.SubLinearLayout>
</com.kodulf.uistructdemo.widget.RootLinearLayout>




-------------------------------------------------------------------


RootLinearyLayout


里面添加:


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                Log.d("151225MY", "RootLineary onTouchEvent 按下");
        }
        return super.onTouchEvent(event);
    }


修改里面的返回值,true 和false 看看区别


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                Log.d("151225MY", "MyTextView onTouchEvent 按下");
        }
        return super.onTouchEvent(event);
    }


-------------------------------------------------------------------


再在SubLinearLayout




然后在修改它的onTouchEvent






dispatchTouchEvent -U型- onTouch




----------------------------------------------------------------------------------------------------------------------
标准时间传递的流程:




事件从最外层传递给内部
最外层 子容器onTouchEvent 返回false,执行最外层的onTouchEvent
   --》子容器 空间返回false,执行子容器onTouchEvent
---》控件 控件onTouchEvent,如果返回true,就是自己处理,如果返回fals

dispatchTouchEvnet 会调用空间内部的onTouchEvent


----------------------------------------------------------------------------------------------------------------------


dispatch 就是分发的意思
dispatch 就是分发的意思
dispatch 就是分发的意思
dispatch 就是分发的意思
dispatch 就是分发的意思
dispatch 就是分发的意思
dispatch 就是分发的意思
dispatch 就是分发的意思
dispatch 就是分发的意思
dispatch 就是分发的意思


--------------------------------------------------------------------------------------
拦截的功能,只有ViewGroup以及子类,容器可以实现拦击;
因为ViewGroup的dispatchTouchEvent内部会向子容器和空间发送消息;


拦截就是在容器执行的时候,不再像子空间发送事件


在SubLinearLayout
    /**
     * 事件拦截操作:返回true,那么所有传给当前容器的事件
     * 都不会再传递给子控件了
     * @param ev
     * @return
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        //return super.onInterceptTouchEvent(ev);
        return true;
    }


000000000000000000000000000000000000000000000000000000000000
应用场景:用于解决一些时间的冲突情况的,例如功能的限制;
比如:功能需要登陆才可以使用,如果没有登陆,那么功能全部禁用
000000000000000000000000000000000000000000000000000000000000


如何开启容器的拦截和取消
1:容器内部包含了一个叫做requestDisallowInterceptTouchEvent(true);
2:这个方法,可以让控件的父容器以及更上层的容器都不再拦截事件。


ScrollView 里面套一个ListView


--------------------------------------------------------------






新建


/**
 * Created by Administrator on 15-12-25.
 */
public class MyListView extends ListView {


    public MyListView(Context context) {
        super(context);
    }


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


    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action){
            case MotionEvent.ACTION_MOVE:
                Log.d("151225MY", "MYListView dispatchTouchEvent MOVE");
        }
        return super.dispatchTouchEvent(ev);
    }


    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action){
            case MotionEvent.ACTION_MOVE:
                Log.d("151225MY", "MYListView onInterceptTouchEvent MOVE");
        }
        return super.onInterceptTouchEvent(ev);
    }
}


新建




/**
 * Created by Administrator on 15-12-25.
 */
public class MyScrollView extends ScrollView {
    public MyScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }


    public MyScrollView(Context context) {
        super(context);
    }


    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action){
            case MotionEvent.ACTION_MOVE:
                Log.d("151225MY", "MYScrolloView dispatchTouchEvent MOVE");
        }
        return super.dispatchTouchEvent(ev);
    }


    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action){
            case MotionEvent.ACTION_MOVE:
                Log.d("151225MY", "MYScrolloView onInterceptTouchEvent MOVE");
        }
        return super.onInterceptTouchEvent(ev);
    }
}


litView中dispatchTouchEvent,
添加requestDisallowIntegceptTouchEvent(true);


但是这个还是有点问题的,


再优化:
listView的高度默认为0,把listView 的设置为wrap_content 就好了。
在listView 里面添加:


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int hMode = MeasureSpec.getMode(heightMeasureSpec);
        int hSize = MeasureSpec.getSize(heightMeasureSpec);


        switch(hMode){
            case MeasureSpec.UNSPECIFIED:
                Log.d("151225MY","hMode UNSPECIFIED");
                break;
            case MeasureSpec.AT_MOST:
                break;
            case MeasureSpec.EXACTLY:
                break;
        }
        Log.d("151225MY","hSize="+hSize);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }




进一步修改;
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int hMode = MeasureSpec.getMode(heightMeasureSpec);
        int hSize = MeasureSpec.getSize(heightMeasureSpec);


        switch(hMode){
            case MeasureSpec.UNSPECIFIED:
                Log.d("151225MY","hMode UNSPECIFIED");
                break;
            case MeasureSpec.AT_MOST:
                break;
            case MeasureSpec.EXACTLY:
                break;
        }
        Log.d("151225MY","hSize="+hSize);


        // ScrollView+ListView 时候,高度模式强制设置为UNSPECIFIED
        //ListView 只会计算一条的高度;
        //将height的模式,强制设置为AT_MOST就会进行计算实际的高度了
        //考虑到ListView条目数量不确定,高度也不确定,height size应该是一个最大值
        heightMeasureSpec=MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE>>2,MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }




-------------------------------------------------------------------------------
新建
public class MyArrayAdapter extends ArrayAdapter<String> {
    public MyArrayAdapter(Context context, int resource, List<String> objects) {
        super(context, resource, objects);
    }


    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Log.d("151223MY","postion="+position);
        return super.getView(position, convertView, parent);
    }
}


总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。
总体来说ScrollView 套ListView 是不推荐的。


ViewPager+ViewPager




关于拦截:
1:dispatchTouchEvent 会自动调用拦截方法返回true,拦截事件;
2:如果容器内部的requestDisallowInterceptTouchEvent设置为true,容器不会拦截事件。
3:通常在拦截事件中,进行数值的或者是控件位置的检查,来判断是否拦截事件。之后才是onTouchEvent执行;




新建一个NavigationDrawerActivity


作业:看ViewPager的源码关于onInterceptTouchEvent


++++++++++++++++++++++++++++++++++++++++++++++++
ViewPager+图片放大。。+++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++++




取消readme
选择ssh,
复制
打开git bash
git clone git.oschina.net:.....git
open .









作业里的:

public class MySliding extends SlidingPaneLayout {
    public MySliding(Context context) {
        super(context);
    }

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

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {

        boolean ret=false;
        float x = ev.getX();
        int width = getWidth();
        int action = ev.getAction();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                if(x>width/10&&x<width*9/10){
                    ret=false;
                    Log.d("151228MY", "false");
                }else{
                    ret=true;
                    Log.d("151228MY", "true");
                }
                Log.d("151228MY", "x=" + x + " width=" + width + " width/10=" + width / 10+" width*9/10="+width*9/10);
                break;
        }
        return ret;
//        return super.onInterceptTouchEvent(ev);
    }
}




















事件分发:
dispathTouchEvent
1. 功能:从当前容器向自己内部的子容器/控件,传递触摸事件;
2. 特点:在传递的时候,会检查,触摸事件是否命中了内部的子控件,如果找到命中的子控件,那么,会调用子控件的 dispatchTouchEvent 方法,让子控件继续传递触摸事件;
3. 当 dispatchTouchEvent 方法找不到命中的子控件时候,当前的控件或者容器自己就进入到 onTouchEvent


onTouchEvent
1. onTouchEvent 如果这个方法,返回true, 这个方法所属的控件,相当于把事件消费掉了;
2. 返回true, 这个控件的父容器以及更上层的父容器都不会再调用 onTouchEvent了;
3. 对于UI结构来说,  onTouchEvent 这个方法,是从最内部子控件开始执行,之后就是每一级的父容器在执行onTouchEvent() ,相当于从最内部调用到最外部。



事件拦截:
拦截就是在容器执行的时候,不再向子控件发送事件
当前的ViewGroup 在进行事件分发的时候,先进行检查,调用自身的 onInterceptTouchEvent, 当这个方法,返回 true的时候,那么ViewGroup 将把事件拦截下来,不再给 自己的子控件发送事件了


注意:拦截的功能,只有ViewGroup以及子类,容器可以实现拦击;因为ViewGroup的dispatchTouchEvent内部会向子容器和空间发送消息;


你可能感兴趣的:(千雅爸爸)