Android滑屏与子控件点击事件处理

Android滑屏与子控件点击事件处理

     想象下面画面描述中的情况,程序如何区分当前用户是想打开程序还是滑动界面?


    在android中,触摸屏幕事件逻辑如下:

1.首先调用父容器的onInterceptTouchEvent方法。

2.如果该方法返回true,则说明该触屏事件被父容器拦截,触屏事件不会传递到子控件中去处理,直接调用容器的onTouchEvent方法。

3.如果该方法返回false,则将触屏事件传递到子控件中去处理,如果子控件在onTouchEvent方法返回true,则说明触屏事件已被消费,不会再传递给父容器;如果子控件的onTouchEvent方法返回false,则再调用父容器的onTouchEvent处理。

4.触屏事件一般有三个阶段ACTION_DOWN、ACTION_MOVE、ACTION_UP,在onInterceptTouchEvent方法和onTouchEvent方法中,都可以对着三个阶段进行处理。如果父容器在onInterceptTouchEvent某一阶段返回false,则系统会在后续阶段再次调用onInterceptTouchEvent。

    现在回到程序如何区分当前用户是想打开程序还是滑动界面的问题上,滑动界面和点击图标只有在ACTION_MOVE阶段中才能区分,子控件如果是可点击的,则子控件的OnTouchEvent一定会返回true,也就是说如果触屏事件将ACTION_DOWN和ACTION_UP事件都传递给子控件,当前动作就变成了点击图标,所以判断当前是滑动还是点击就必须在onInterceptTouchEvent方法中处理。

    首先在onInterceptTouchEvent的ACTION_DOWN阶段,无法判断是否滑动还是点击,则需要返回false,以便后续阶段中调用拦截事件。

    然后系统会进入onInterceptTouchEvent的ACTION_MOVE阶段,在当前阶段可以用触摸移动的距离来判断是点击还是滑动。

    判读移动距离是否大于ViewConfiguration.get(getContext()).getScaledTouchSlop(),就可以判断是点击还是滑动,如果是点击则直接返回false,交给子控件处理;如果是滑动则返回true,然后系统就会调用容器的onTouchEvent其中action是为ACTION_MOVE,只需要在这里处理滑动的具体代码即可。

    如果在onInterceptTouchEvent的ACTION_MOVE阶段返回true(滑动屏幕),则不会在进入onInterceptTouchEvent的ACTION_UP阶段了,系统会直接调用onTouchEvent的ACTION_UP阶段,所以处理滑屏的惯性效果就在这里处理。

    如果在onInterceptTouchEvent的ACTION_MOVE阶段返回false(点击图标),则系统会进入onInterceptTouchEvent的ACTION_UP阶段,此时需要传递false,以便子控件接收ACTION_UP,完成点击事件。

	/**
	 * 是否传递到子view
	 * true  不传递
	 * false 传递
	 * 一次触摸屏幕中,如果拦截过则不会再次进入onInterceptTouchEvent
	 * 触摸屏幕中,如果在ACTION_DOWN时返回true,则不会再有ACTION_MOVE和ACTION_UP拦截
	 * 如果ACTION_DOWN返回false,ACTION_MOVE中返回true,则不会有ACTION_UP拦截
	 */
	@Override
	public boolean onInterceptTouchEvent(MotionEvent event) {
		// TODO Auto-generated method stub
		switch(event.getAction()){
			case MotionEvent.ACTION_DOWN:
				//如果ACTION_DOWN未被拦截,则有可能被子view消费,一旦消费后,
				//则父容器中OnTouchEvent中就不会接受到ACTION_DOWN事件
				System.out.println("onInterceptTouchEvent ACTION_DOWN");
				touchDownX = event.getX();
				startX = this.getScrollX();
								
				mScrolling = false;
				
				velocityTracker = VelocityTracker.obtain();
				
				velocityTracker.addMovement(event);
				break;
			case MotionEvent.ACTION_MOVE:
				System.out.println("onInterceptTouchEvent ACTION_MOVE");
				
				if(Math.abs(touchDownX - event.getX())>=mTouchSlop){
					mScrolling = true;
				}
				else{
					mScrolling = false;
				}
				
				break;
			case MotionEvent.ACTION_UP:
				//如果ACTION_MOVE判断是点击就会进入ACTION_UP,是滚动则不会进入
				System.out.println("onInterceptTouchEvent ACTION_UP");	
				
				//返回false以便子控件接收ACTION_UP事件
				mScrolling = false; 
				
				break;
		}
		
		return mScrolling;
	}

	/**
	 * onTouchEvent() 用于处理事件,返回值决定当前控件是否消费(consume)了这个事件。
	 * 可能你要问是否消费了又区别吗,反正我已经针对事件编写了处理代码?
	 * 答案是有区别!比如ACTION_MOVE或者ACTION_UP发生的前提是一定曾经发生了ACTION_DOWN,
	 * 如果你没有消费ACTION_DOWN,那么系统会认为ACTION_DOWN没有发生过,
	 * 所以ACTION_MOVE或者ACTION_UP就不能被捕获。
	 * */
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		// TODO Auto-generated method stub
		switch(event.getAction()){
			case MotionEvent.ACTION_DOWN:
				System.out.println("ACTION_DOWN");				
				break;
			case MotionEvent.ACTION_MOVE:
				System.out.println("ACTION_MOVE");
				int offsetX = Float.valueOf(startX + touchDownX - event.getX() ).intValue();
				
				if (offsetX>=0 && offsetX<=this.getWidth()) {
					scrollTo(offsetX,0);
				}

				velocityTracker.addMovement(event);
				
				break;
			case MotionEvent.ACTION_UP:
				System.out.println("ACTION_UP");
				
				velocityTracker.computeCurrentVelocity(500);
				
				//从左往右,velocity>0,实际是想往回拔
				int speed = -Float.valueOf(velocityTracker.getXVelocity()).intValue();
				
				System.out.println("scrollX:" + this.getScrollX() + ",speed:" + speed);
				
				//开始滚动->起始点,偏移距离,持续事件
				mScroller.startScroll(this.getScrollX(), 0,speed, 0,2000);
				velocityTracker.recycle();
				break;
			default:
				break;
		}
		
		return true;
	}


发布了38 篇原创文章 · 获赞 4 · 访问量 19万+

猜你喜欢

转载自blog.csdn.net/tomatozq/article/details/44564889