Tips_Android点击事件(Down、Move、Up)的分发_重写Layout响应拖动事件

首先是点击事件在不同的布局层次中传递的。

理解Down事件再哪个层次被消费(拦截),后续的Move、Up的点击事件如何传递。

其中ViewGroup中onInterceptTouchEvent方法用来对事件作预处理的,对于Down事件返回true表示要消费这个事件,不再向子View传递。

Android中dispatchTouchEvent, onInterceptTouchEvent, onTouchEvent的理解

onInterceptTouchEvent用于改变事件的传递方向。决定传递方向的是返回值,返回为false时事件会传递给子控件,返回值为true时事件会传递给当前控件的onTouchEvent(),这就是所谓的Intercept(拦截)。

[tisa ps:正确的使用方法是,在此方法内仅判断事件是否需要拦截,然后返回。即便需要拦截也应该直接返回true,然后由onTouchEvent方法进行处理。]

    onTouchEvent用于处理事件,返回值决定当前控件是否消费(consume)了这个事件。尤其对于ACTION_DOWN事件,返回true,表示我想要处理后续事件;返回false,表示不关心此事件,并返回由父类进行处理。

    可能你要问是否消费了又区别吗,反正我已经针对事件编写了处理代码?答案是有区别!比如ACTION_MOVE或者ACTION_UP发生的前提是一定曾经发生了ACTION_DOWN,如果你没有消费ACTION_DOWN,那么系统会认为ACTION_DOWN没有发生过,所以ACTION_MOVE或者ACTION_UP就不能被捕获。

在没有重写onInterceptTouchEvent()和onTouchEvent()的情况下(他们的返回值都是false), 对上面这个布局,MotionEvent事件的传递顺序如下:

当某个控件的onInterceptTouchEvent()返回值为true时,就会发生截断,事件被传到当前控件的onTouchEvent()。如我们将LayoutView2的onInterceptTouchEvent()返回值为true,则传递流程变成:

 如果我们同时将LayoutView2的onInterceptTouchEvent()和onTouchEvent()设置成true,那么LayoutView2将消费被传递的事件,同时后续事件(如跟着ACTION_DOWN的ACTION_MOVE或者ACTION_UP)会直接传给LayoutView2的onTouchEvent(),不传给其他任何控件的任何函数。同时传递给子空间一个ACTION_CANCEL事件。传递流程变成(图中没有画出ACTION_CANCEL事件):

         


[tisa ps:总体来看, onInterceptTouchEvent是自rootview向下传递, onTouchEvent正好相反。]


基于以上点击事件的传递,可以重写一些ViewGroup,响应其拖动的事件,比如LinearLayout,重写其onInterceptTouchEvent()和onTouchEvent()两个方法可以达到效果(具体看实际布局中子view对事件的消费情况而定)

    //true是拦截,false是不拦截。这里只是预处理判断点击的位置,不拦截。
    @Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
    	
		if (ev.getAction() == MotionEvent.ACTION_DOWN) {
			int x = (int) ev.getX();
			int y = (int) ev.getY();

			dragSrcPointY = y;

			FTLog.d(_TAG, "x=" + x + ",y=" + y);
		}
		
		return super.onInterceptTouchEvent(ev);
	}

    @Override
	public boolean onTouchEvent(MotionEvent ev) {
			//只有判断是点击触发拖动的区域时,才进行处理。否则不处理,交予FTBounceListView旧逻辑处理。
			int action = ev.getAction();
			switch (action) {
			case MotionEvent.ACTION_DOWN:
//				FTLog.e("TMS===down==", "tms");
//				bConsumeDown = true;
				return true;
//			case MotionEvent.ACTION_MOVE:
//				FTLog.e("TMS===move==", "tms");
//				if (bConsumeDown == false)
//				{
//					super.onTouchEvent(ev);
//				}
//				break;
			case MotionEvent.ACTION_UP:
				
				FTLog.d(_TAG, "x="+ev.getX()+",y="+ev.getY());
				if (ev.getY() < dragSrcPointY - 20)
				{//向上拖动距离超过20dip,才finish,动画效果。
					try {
						((Activity) context).finish();
						((Activity) context).overridePendingTransition(
								R.anim.move_bottom_in, R.anim.move_top_out);
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
				return true;
			default:
				break;
			}
		
		return super.onTouchEvent(ev);
	}

其他视具体情况而定,比如listview的最后一条,某中间区域响应拖动的事件:

 //true是拦截,false是不拦截。这里只是预处理判断点击的位置,不拦截。
    @Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
    	
		if (ev.getAction() == MotionEvent.ACTION_DOWN) {
			bConsumeDown = false;
			int x = (int) ev.getX();
			int y = (int) ev.getY();

			dragSrcPosition = pointToPosition(x, y);
			dragSrcPointY = y;
			if (dragSrcPosition == AdapterView.INVALID_POSITION) {
				return super.onInterceptTouchEvent(ev);
			}

			FTLog.d(_TAG, "x=" + x + ",y=" + y);
			FTLog.d(_TAG, "dragSrcPosition==" + dragSrcPosition
					+ "getAdapter().getCount()==" + getAdapter().getCount());
			if (dragSrcPosition == getAdapter().getCount() - 1
					|| dragSrcPosition == getAdapter().getCount() - 2) 
			{// 最后一条是footer,倒数第二条是数据
				ViewGroup itemView = (ViewGroup) getChildAt(dragSrcPosition
						- getFirstVisiblePosition());
				int itemLeft = itemView.getLeft();
				int itemRight = itemView.getRight();
				int itemMid = (itemRight + itemLeft) / 2;

				FTLog.d(_TAG, "itemLeft==" + itemLeft + ", itemRight="
						+ itemRight + ",itemMid=" + itemMid);

				if (x > itemMid - 60 && x < itemMid + 60) {
					bConsumeDown = true;//预处理,判断是点击触发拖动的区域,并不拦截事件
					return false;
				}
			}
		}
		
		return super.onInterceptTouchEvent(ev);
	}

    /**
     * 触摸事件
     */
    @Override
	public boolean onTouchEvent(MotionEvent ev) {

		if (bConsumeDown == true && dragSrcPosition != INVALID_POSITION) {
			//只有判断是点击触发拖动的区域时,才进行处理。否则不处理,交予FTBounceListView旧逻辑处理。
			int action = ev.getAction();
			switch (action) {
			case MotionEvent.ACTION_DOWN:
//				FTLog.e("TMS===down==", "tms");
//				bConsumeDown = true;
				break;
//			case MotionEvent.ACTION_MOVE:
//				FTLog.e("TMS===move==", "tms");
//				if (bConsumeDown == false)
//				{
//					super.onTouchEvent(ev);
//				}
//				break;
			case MotionEvent.ACTION_UP:
				
				FTLog.d(_TAG, "x="+ev.getX()+",y="+ev.getY());
				if (ev.getY() < dragSrcPointY - 20)
				{//向上拖动距离超过20dip,才finish,动画效果。
					try {
						((Activity) context).finish();
						((Activity) context).overridePendingTransition(
								R.anim.move_bottom_in, R.anim.move_top_out);
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
				break;
			default:
				break;
			}
			return true;
		}
		
		return super.onTouchEvent(ev);
	}

以上重写ViewGroup的两个方法,对于具体的View来讲,可以setOnTouchLinstener方法,再其OnTouch方法中,用mGestureDetectorOpen.onTouchEvent(event);来响应。

_imageview_room_op_item_open.setOnTouchListener(new OnTouchListener() {
			@Override
			public boolean onTouch(View v, MotionEvent event) {
				mGestureDetectorOpen.onTouchEvent(event);
				return true;
			}
		});
		
		mGestureDetectorOpen = new GestureDetector(this, new OnGestureListener()
		{
			@Override
			public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
					float velocityY) {//判断在_imageview_room_op_item_open控件上移动的距离,do something}
		}


转载于:https://my.oschina.net/mstian/blog/284268

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值