Andoid事件机制

1.理论Api事件:

 MontionEvent  触碰操作
    download  产生一次,按下    
    move  这里的move会调用多次  
    up     产生一次,松开
    
    getX():获取X轴坐标,原始发生事键按钮自己
    getRawX():  现对于屏幕左上角
    
    屏幕类型:
    电阻屏:按下压力感应,软屏
    电容屏:现对于低电流来感应屏幕,硬屏
    事件传播:
    屏幕-》Linxu 内核-》传递到当期应用-》当前Activity-》事件分发dispachTouchEvent(MontionEvent event)
    ->  按钮响应事件
    -》 如果按钮消费,那么Activity自己处理

 1.  案例1: 事件分发

main.xml 布局文件: 

<?xml version="1.0" encoding="utf-8"?>
<!-- Father -->
<com.qqyumidi.event.TouchEventFather xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="320dp"
    android:layout_height="480dp"
    android:background="#468AD7"
    android:gravity="center"
    android:orientation="vertical">
    <!-- Childs -->
    <com.qqyumidi.event.TouchEventChilds
        android:gravity="center"
        android:id="@+id/childs"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_gravity="center"
        android:background="#66ff0000">
      <!--MyView -->
        <com.qqyumidi.event.MyView
            android:id="@+id/myview"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:background="#000"/>


    </com.qqyumidi.event.TouchEventChilds>

</com.qqyumidi.event.TouchEventFather>

TouchEventFather 
package com.qqyumidi.event;

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

public class TouchEventFather extends LinearLayout {


	String Tag="denganzhi";


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

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

	public boolean dispatchTouchEvent(MotionEvent ev) {
		Log.e(Tag, "Father | dispatchTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
		return super.dispatchTouchEvent(ev);
//		return true;
	}

	public boolean onInterceptTouchEvent(MotionEvent ev) {
		Log.i(Tag, "Father | onInterceptTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
	//	return super.onInterceptTouchEvent(ev);
		return true;
		//return false;
	}

	public boolean onTouchEvent(MotionEvent ev) {
		Log.d(Tag, "Father | onTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
		return super.onTouchEvent(ev);
//		return true;
	}

}
TouchEventChilds:
package com.qqyumidi.event;

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

public class TouchEventChilds extends LinearLayout {
	private  View childView;
	private Scroller scroller;
	String Tag="denganzhi";
	@Override
	protected void onFinishInflate() {
		super.onFinishInflate();

//		childView = getChildAt(0);
	}

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

	public TouchEventChilds(Context context, AttributeSet attrs) {
		super(context, attrs);
//		scroller = new Scroller(context) ;
	}

	public boolean dispatchTouchEvent(MotionEvent ev) {
		Log.e(Tag, "Childs | dispatchTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
		return super.dispatchTouchEvent(ev);
//		return true;
	}

	public boolean onInterceptTouchEvent(MotionEvent ev) {
		Log.e(Tag, "Childs | onInterceptTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
		return super.onInterceptTouchEvent(ev);
//		return true;
	}

	public boolean onTouchEvent(MotionEvent ev) {
	Log.e(Tag, "Childs | onTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
		return super.onTouchEvent(ev);
//		return true;
	}
	
}
MyView:
package com.qqyumidi.event;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

/**
 * Created by denganzhi on 2020/3/26.
 */

public class MyView extends View {
    String Tag="denganzhi";

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


    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.e(Tag, "MyView | dispatchTouchEvent --> " + TouchEventUtil.getTouchAction(event.getAction()));
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        Log.e(Tag, "MyView | onTouchEvent --> " + TouchEventUtil.getTouchAction(event.getAction()));
     //   return super.onTouchEvent(event);
        return true;
    }



}
 
TouchEventActivity  代码: 
package com.qqyumidi.event;

import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;

public class TouchEventActivity extends Activity {

	String Tag="denganzhi";

	MyView myview;

	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		myview = (MyView)findViewById(R.id.myview);

		myview.setOnTouchListener(new View.OnTouchListener() {
			@Override
			public boolean onTouch(View v, MotionEvent event) {
				Log.e(Tag,"TouchEventActivity->myview.setOnTouchListener:"+TouchEventUtil.getTouchAction(event.getAction()));
				return true;
			}
		});
	}

	@Override
	public boolean dispatchTouchEvent(MotionEvent ev) {
		Log.e(Tag, "Activity | dispatchTouchEvent --> " + TouchEventUtil.getTouchAction(ev.getAction()));
		return super.dispatchTouchEvent(ev);
//		return true;
	}


	@Override
	public boolean onTouchEvent(MotionEvent event) {
		Log.e (Tag, "Activity | onTouchEvent --> " + TouchEventUtil.getTouchAction(event.getAction()));
		return super.onTouchEvent(event);
	}



}

事件分发 分析:

 1.Activity | dispatchTouchEvent --> ACTION_DOWN      Activity分发 
 Father | dispatchTouchEvent --> ACTION_DOWN        分发
 Father | onInterceptTouchEvent --> ACTION_DOWN     拦截   false,不拦截,往下分发,
 如果拦截,事件交给Activity | onTouchEvent 消费
 
 
2. Childs | dispatchTouchEvent --> ACTION_DOWN         分发到ViewGroup
    Childs | onInterceptTouchEvent --> ACTION_DOWN       拦截  false,不拦截,往下分发
 
 3. MyView | dispatchTouchEvent --> ACTION_DOWN          分发到View
 TouchEventActivity->myview.setOnTouchListener:ACTION_DOWN    触发setOnToucher
 返回true,消费
 返回false,不消费,往下调用
 MyView | onTouchEvent --> ACTION_DOWN                   触发Touch,不消费,返回false  
 

 4.Childs | onTouchEvent --> ACTION_DOWN                 View不消费往上传递,返回false,不消费,继续往上传递 
 Father | onTouchEvent --> ACTION_DOWN                 返回false,不消费,继续往上传递
 Activity | onTouchEvent --> ACTION_DOWN               返回false,不消费,继续往上传递到顶端,最终消费
    
    
ACTION_DOWN 事件被Acticity消费,那么ACTION_MOVE 事件不下发了,直接交给Activity的onTouchEvent 事件消费
    如果 ACTION_DOWN 被 Father| Childs 消费,事件逐层下发
 Activity | dispatchTouchEvent --> ACTION_MOVE          
  Activity | onTouchEvent --> ACTION_MOVE
  
  Activity | dispatchTouchEvent --> ACTION_UP
   Activity | onTouchEvent --> ACTION_UP
   后续.......

*****************************************************************     

   viewGroup事件传递:
    //事件分发, 返回true, 不继续分发,没有被消费
    // 返回false 继续往下分发,如果是ViewGroup 分发给 onInterceptHoverEvent 进行判断是否拦截该事件

onInterceptHoverEvent 只有ViewGroup才有, 负责事件拦截,
    返回true, 拦截事件, 不继续往下分发, 交给自身的 onTouchEvent进行处理 
    返回false, 继续往下传递

 

    ***最底层View首先调用,activity最后调用
    onTouchEvent方法进行时间处理,返回true消费,
    返回false, View 往上传递,  最终Acticity的 OnTouchEvent消费
    返回false, ViewGroup 往下传递

*****************************************************************     

案例2: 事件分发

activity_main.xml 布局文件: 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="lanya.denganzhi.com.montioneventtest.Main2Activity"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/moveImg"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@mipmap/ic_launcher"
        android:layout_marginLeft="30dp"
        android:layout_marginTop="50dp"/>

</LinearLayout>

MyImageView  代码: 

package lanya.denganzhi.com.montioneventtest;

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

/**
 * Created by Administrator on 20-3-8.
 */
public class MyImageView extends android.support.v7.widget.AppCompatImageView {
    public MyImageView(Context context) {
        super(context);
    }
    public MyImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
  // 分发
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {

        Log.e("denganzhi","ImageView--dispatchTouchEvent:"+event.getAction());
        return super.dispatchTouchEvent(event);
    }
    // 处理
    @Override
    public boolean onTouchEvent(MotionEvent event) {

        Log.e("denganzhi","ImageView--onTouchEvent:"+event.getAction());

        return super.onTouchEvent(event);
    }
}

 MainActivity  代码:

package lanya.denganzhi.com.montioneventtest;

import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
    MyImageView myImageView=null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myImageView = (MyImageView) findViewById(R.id.myImageView);
        myImageView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.e("denganzhi","myImageView--setOnTouchListener:"+event.getAction());
                return true;
            }
        });
    }
    //
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.e("denganzhi","MainActivity--dispatchTouchEvent:"+event.getAction());

        return super.dispatchTouchEvent(event);
    }
    //
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("denganzhi","MainActivity--onTouchEvent:"+event.getAction());
        //  MotionEvent.ACTION_DOWN   0
        // MotionEvent.ACTION_MOVE  // 1
        //  MotionEvent.ACTION_UP    //2

        return super.onTouchEvent(event);
    }
    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        Log.e("denganzhi1","key--dispatch:"+event.getKeyCode());
        return super.dispatchKeyEvent(event);
    }
}

*****************************************************************     
    View Api:
    boolean onTouchEvnet(MontionEvent evnet) : 调用了方法叫处理了,返回true才表示消费该事件
    
down事件:
     MainActivity--dispatchTouchEvent:0        down事件,activity分发
     ImageView--dispatchTouchEvent:0        activity分发给imageview
     myImageView--setOnTouchListener:0                imageview的setOnTouch事件调用,不消费,往上传递

     ImageView--onTouchEvent:0            imageview 的重写 onTouch方法调用
     MainActivity--onTouchEvent:0         activity的重写 onTouch方法调用,最终消费时间
move事件: 
   MainActivity--dispatchTouchEvent:2
    MainActivity--onTouchEvent:2      // move事件都不调用ImageViw的dispatch分发了,直接activity消费 

up事件:
   MainActivity--dispatchTouchEvent:1  // up事件都不调用ImageViw的dispatch分发了,直接activity消费 
   MainActivity--onTouchEvent:1

 总结:要是第一次down事件不消费,后面的move,up事件不会分发了
*****************************************************************   
down事件: 
    myImageView.setOnTouchListener 返回true
     MainActivity--dispatchTouchEvent:0        down事件,activity分发
     ImageView--dispatchTouchEvent:0        activity分发给imageview
     myImageView--setOnTouchListener:0      返回true,那么时间已经被消费了,时间停止
    
  move事件: 
   MainActivity--dispatchTouchEvent:2
    ImageView--dispatchTouchEvent:2     
   myImageView--setOnTouchListener:2   消费
   
    up事件:
   MainActivity--dispatchTouchEvent:1
   ImageView--dispatchTouchEvent:1
 myImageView--setOnTouchListener:1    消费
 
 *****************************************************************************************

 2.  实现图片拖拽功能:

 View 视图Api:
   getLeft()、getTop():  左上角顶点坐标现对于原点坐标
   getRight()、getBottom()  右下角相对于原点坐标
   layout(int left,top,right,bottom): 对视图进行重新定位

 *****************************************************************
 实现图片拖动伪代码:
  实现图片拖动原理:
  lastX
  lastY
  eventX
  eventY 
 MontionEvent.down
  // 1.获取按下点坐标
   lastX=  event.getRawX();
   lastY= evnet.getRawY();
 MontionEvent.move
     // 2.获取move相对于down移动距离
    eventX =event.getRawX();
    eventY =  event.getRawY();
    dx =  eventX - lastX;
    dy =  eventY- lastY; 
    // 3.重新定位
    left= imageview.getLeft()+ dx;
    top = imageview.getTop() + dy;
    right = imageview.getRight() + dx;
    bottom = imageview.getBottom + dy:
    imageview.layout(left,top,right,bottom)
    //4. 下次移动做准备
    lastX= eventX
    lastY= eventY
*****************************************************************

源码: 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="lanya.denganzhi.com.montioneventtest.Main2Activity"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/moveImg"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@mipmap/ic_launcher"
        android:layout_marginLeft="30dp"
        android:layout_marginTop="50dp"/>

</LinearLayout>
Main2Activity  代码:
package lanya.denganzhi.com.montioneventtest;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;

public class Main2Activity extends AppCompatActivity {

    ImageView moveImg;
    float lastX ,lastY  ;  //上次位移
    float  eventX , eventY; // 当前坐标
    private LinearLayout parent;
    float parentBottom,parentRight;

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

        moveImg = (ImageView)findViewById(R.id.moveImg);

        parent = (LinearLayout) moveImg.getParent();  //parentBottom、parentRight 马上去取为0
        moveImg.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {

                switch (event.getAction()){

                    case MotionEvent.ACTION_DOWN:

                        // 得到父视图的 right/bottom
                        if(parentRight==0){
                            parentRight= parent.getRight();
                            parentBottom= parent.getBottom();
                        }
                        lastX= event.getRawX();
                        lastY =event.getRawY();

                        break;

                    case MotionEvent.ACTION_MOVE:
                        eventX = event.getRawX();
                        eventY = event.getRawY();


                        float dx= eventX -lastX;
                        float dy= eventY -lastY;
                        float left = moveImg.getLeft() + dx;
                        float top =moveImg.getTop() + dy;
                        float right = moveImg.getRight() + dx;
                        float bottom = moveImg.getBottom() + dy;

                        // 不能移动到外面去
                        if(left<0){
                            right = right + (-left);
                            left=0; // 那么已经移除了left,right也要复原,加上移除的left

                        }
                       // 不能移出到左边
                        if(top < 0){
                            bottom = bottom + (-top);
                            top = 0;
                        }
                        // 不能移出到右边
                        if(right > parentRight){
                            left= left - (right - parentRight);
                            right= parentRight;
                        }
                        if(bottom > parentBottom){
                            top = top - (bottom - parentBottom);
                            bottom = parentBottom;
                        }
                        moveImg.layout((int)left,(int)top,(int)right,(int)bottom);
                        lastX= eventX;
                        lastY=eventY;
                        break;

                    case MotionEvent.ACTION_UP:

                        break;
                }
                return true;
            }
        });
    }
}

上图:

3.     KeyEvent: 事件

基本类型:down:  手指按下, 会被调用多次
          up : 手指移动
   实现双击back键盘退出

    //
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.e("denganzhi","MainActivity--dispatchTouchEvent:"+event.getAction());
        return super.dispatchTouchEvent(event);
    }
    //
    @Override
    public boolean onTouchEvent(MotionEvent event) {

        Log.e("denganzhi","MainActivity--onTouchEvent:"+event.getAction());

        //  MotionEvent.ACTION_DOWN   0
        // MotionEvent.ACTION_MOVE  // 1
        //  MotionEvent.ACTION_UP    //2

        return super.onTouchEvent(event);
    }
    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        Log.e("denganzhi1","key--dispatch:"+event.getKeyCode());
        return super.dispatchKeyEvent(event);
    }

    // 会被 调用多次
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        // 如果要监听back,home键,必须设置下面这2个Api
        // startTracking 和  return true;
        event.startTracking();
        // event.getKeyCode()  //获取按键
        Log.e("denganzhi1","key--down");
        return true;
    }
    boolean isExit=false;
    Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if(msg.what==100){
                isExit=false;
            }

        }
    };
    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        Log.e("denganzhi1","key--up");
        if(event.getKeyCode() == KeyEvent.KEYCODE_BACK){
            if(!isExit){
                isExit=true;
                handler.sendEmptyMessageDelayed(100,4000);
                Toast.makeText(MainActivity.this,"退出",Toast.LENGTH_SHORT).show();
                return true;
            }
            // 否则退出 执行super,4s 以后 有需要双击
        }
        return super.onKeyUp(keyCode, event);
    }

    @Override
    protected void onDestroy() {
        handler.removeMessages(100);
        super.onDestroy();
    }
    // 长按监听
    @Override
    public boolean onKeyLongPress(int keyCode, KeyEvent event) {
        Log.e("denganzhi1","key--long");
        return super.onKeyLongPress(keyCode, event);
    }

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值