setOnTouchListener onTouchEvent setOnClickListener

本文详细解析了Android中TextView触摸事件的触发机制,包括setOnTouchListener、onTouchEvent和OnClickListener之间的交互逻辑,以及它们如何响应触摸事件。

出处:http://lipeng88213.iteye.com/blog/1161450

package com.ex.touch;

import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.TextView;

public class MainActivity extends Activity {
	private TextView tx ;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		tx = (TextView)super.findViewById(R.id.textView);
		tx.setBackgroundColor(android.graphics.Color.RED);
		tx.setOnTouchListener(new OnTouchListener() {		
			@Override
			public boolean onTouch(View v, MotionEvent event) {
				System.out.println("setOnTouchListener:X="+event.getRawX()+"Y="+event.getRawY());
				return false;
			}
		});
		
		tx.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
			System.out.println("OnClick");	
			}
		});
		
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		// TODO Auto-generated method stub
		System.out.println("onTouchEvent:X="+event.getRawX()+"Y="+event.getRawY());
		return super.onTouchEvent(event);
	}
}


结论:
1、当在TextViw上触摸时 先触发setOnTouchLisener
   如果setOnTouchLisener返回false 会触发onTouchEvent 并且后续的move up都只会触发onTouchEvent
   如果setOnTouchLisener返回true 后续的move up 都只会触发setOnTouchLisener
2、当在其他位置触摸时 只会触发onTouchEvent
3、如果加上onClickListener时,
   如果setOnTouchLisener返回false 后续的move up都只会触发setOnTouchLisener;并且只有当up事件是在发生在textview上时 才会触发onclicklistener
   如果setOnTouchLisener返回true 后续的move up 也只会触发setOnTouchLisener 并且onclicklistner也不会执行


对了
当move触发setOnTouchLisener时 这个move不止是在TextView上 而是可以在整个activity上面

其实 这个很好理解 和http://lipeng88213.iteye.com/blog/1102048
是一样的

1、如果TextView的setonTouchListener返回为false 也就是说TextView不出来这个事件,这时就会向上传递给onTouchEvent 并且后续的也会直接传递给onTouchEvent
反之,就表明setonTouchListener处理这个事件
2、只要加上了onClickListener 就表明TextView会处理这个事件,但是事件会首先传递给setonTouchListener 如果它返回false 就会在up的时候出发click;如果它返回true,click就不会被执行了

<think>我们讨论的是在Android开发中,当同时使用`setOnTouchListener`和`setOnClickListener`时,在滑动过程中可能会误触发点击事件的问题。根据引用[1]中的描述,用户通过图片的透明部分处理点击事件,同时使用了触摸和点击监听器。我们的目标是确保在滑动操作时不触发点击事件。 解决方案的核心思路:在触摸事件中判断用户的操作是点击还是滑动,如果是滑动则屏蔽点击事件。 通常的做法: 1. 在`OnTouchListener`中,根据不同的触摸事件(ACTION_DOWN, ACTION_MOVE, ACTION_UP等)来记录状态。 2. 当检测到移动(ACTION_MOVE)超过一定阈值(视为滑动)时,则设置一个标志,表示当前是滑动操作。 3. 在ACTION_UP事件中,如果判断为滑动,则阻止点击事件的触发。 具体步骤: - 在ACTION_DOWN时,记录初始触摸位置,并将点击事件标志置为true(即默认认为是点击)。 - 在ACTION_MOVE时,计算移动距离,如果超过某个阈值(例如10像素),则认为发生了滑动,将点击事件标志置为false。 - 在ACTION_UP时,如果点击事件标志为true,则执行点击操作;否则,不执行。 此外,还可以通过重写控件或使用GestureDetector来区分手势。 下面是一个示例代码,展示如何在一个View上同时使用触摸监听和点击监听,并避免滑动时触发点击: ```java // 设置触摸监听 view.setOnTouchListener(new View.OnTouchListener() { private float downX, downY; private boolean isClick; // 用于判断是否为点击事件 @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: downX = event.getX(); downY = event.getY(); isClick = true; // 初始为点击 break; case MotionEvent.ACTION_MOVE: // 计算移动距离 float moveX = Math.abs(event.getX() - downX); float moveY = Math.abs(event.getY() - downY); // 如果移动距离超过阈值,则认为是滑动 if (moveX > 10 || moveY > 10) { isClick = false; } break; case MotionEvent.ACTION_UP: // 如果是点击,则调用点击事件 if (isClick) { v.performClick(); // 手动触发点击事件 } break; } // 返回false表示不消耗事件,还会继续传递;返回true表示消耗事件,不再传递。 // 由于我们可能还需要处理点击事件(通过performClick),所以这里返回false,让系统继续处理点击事件。 // 但注意,如果我们在ACTION_UP中调用了performClick,那么点击事件就会被触发,而不会触发onClick(因为onClick是在UP事件中系统调用的,我们手动触发了)。 // 实际上,这里返回false,在ACTION_UP时系统仍然会触发onClick。为了避免触发两次,我们可以返回true,并自己处理点击事件(即通过performClick)。 // 因此,我们选择返回true,并在需要点击时手动触发。 return true; } }); // 设置点击监听 view.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 点击事件处理 } }); ``` 注意:在上面的代码中,我们在`onTouch`方法中返回了`true`,表示消耗了事件。这样,原本的`onClick`事件就不会被系统自动触发了。因此,我们在`ACTION_UP`中通过`performClick`手动触发点击事件。 但是,如果我们没有在`onTouch`中返回`true`,那么事件会被传递,系统在`ACTION_UP`时也会触发`onClick`,这样就会导致在滑动时(我们设置`isClick=false`)不会触发点击,而在点击时系统会触发一次点击,而我们手动又触发了一次(因为`isClick=true`时我们调用了`performClick`),导致两次点击。 因此,为了避免两次触发,我们选择在`onTouch`中消耗事件(返回true),并只在需要时手动触发点击事件。 另一种方案:使用`GestureDetector`,它可以更方便地识别各种手势。例如: ```java final GestureDetector gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onSingleTapUp(MotionEvent e) { // 点击事件 return true; } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { // 滑动事件 return true; } }); view.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { return gestureDetector.onTouchEvent(event); } }); ``` 在`GestureDetector`中,点击和滑动是分开的,因此不会互相干扰。注意,这里我们不需要再设置`setOnClickListener`,因为点击事件在`onSingleTapUp`中处理。 根据实际需求选择合适的方法。 §§相关问题§§ 1. 在Android中,如何区分点击和长按事件? 2. 使用GestureDetector时,如何处理双击事件? 3. 在ViewPager中,如何处理滑动冲突? </think> ### 解决滑动时点击事件误触发问题 在 Android 开发中,当同时使用 `setOnTouchListener` 和 `setOnClickListener` 时,滑动操作可能误触发点击事件。核心解决方案是通过**手势判断逻辑**区分点击和滑动操作。以下是具体实现方法: #### 方法 1:自定义触摸逻辑(推荐) ```java view.setOnTouchListener((v, event) -> { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // 记录触摸起始位置 downX = event.getX(); downY = event.getY(); return false; // 允许事件继续传递 case MotionEvent.ACTION_MOVE: // 计算移动距离 float deltaX = Math.abs(event.getX() - downX); float deltaY = Math.abs(event.getY() - downY); // 判断滑动阈值(通常 10-20px) if (deltaX > TOUCH_SLOP || deltaY > TOUCH_SLOP) { v.setPressed(false); // 取消点击状态 return true; // 标记为滑动,拦截后续点击事件 } break; case MotionEvent.ACTION_UP: // 如果未达到滑动阈值,允许触发点击 if (Math.abs(event.getX() - downX) < TOUCH_SLOP && Math.abs(event.getY() - downY) < TOUCH_SLOP) { v.performClick(); // 手动触发点击 } break; } return false; }); ``` #### 方法 2:使用 `GestureDetector`(更简洁) ```java GestureDetector gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onSingleTapUp(MotionEvent e) { // 点击事件处理 return true; } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { // 滑动事件处理 return true; } } ); view.setOnTouchListener((v, event) -> gestureDetector.onTouchEvent(event) ); ``` #### 关键配置 1. **阈值设置**:`TOUCH_SLOP` 建议取值 `ViewConfiguration.get(context).getScaledTouchSlop()`(系统最小滑动距离) 2. **事件传递**: - 返回 `true` 表示消耗事件(阻止点击) - 返回 `false` 允许事件继续传递 3. **性能优化**:避免在 `onTouch` 中执行耗时操作 > **注意**:在 `ViewPager` 等滑动容器中,需确保子 View 的触摸事件不会与父容器冲突。可通过 `requestDisallowInterceptTouchEvent(true)` 在滑动时禁止父容器拦截事件[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值