收不到MotionEvent.ACTION_UP的另一种可能情况

本文介绍了一个关于在自定义View内嵌于ScrollView时遇到的MotionEvent.ACTION_UP事件丢失的问题。通过去除ScrollView包裹,成功解决了事件丢失现象。

正在写一个手写板的东西时发现在onTouchEvent中有时候收不到MotionEvent.ACTION_UP的事件。依照网上所说返回true,但发现仍然不行。然后改了很久才发现原来是因为我的自定义View套在ScrollView中,所以才会出现这种情况。把外面的ScrollView去掉就可以了。我记得以前写AS3代码时也出现过类似的情况——那次是因为文本域把鼠标点击事件给挡住了。

public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mOldX = event.getX(); mOldY = event.getY(); startX = event.getX(); break; case MotionEvent.ACTION_UP: float endX = event.getX(); float deltaX = endX - startX; // 向左滑动(负值)且距离超过阈值 if (deltaX < -MIN_SWIPE_DISTANCE) { performJump(false); // 向后跳转 } // 向右滑动(正值)且距离超过阈值 else if (deltaX > MIN_SWIPE_DISTANCE) { performJump(true); // 向前跳转 } return true; case MotionEvent.ACTION_MOVE: float distanceX = event.getX() - mOldX; float distanceY = event.getY() - mOldY; int touchAction; if (Math.abs(distanceX) > touchSlop && Math.abs(distanceX) >= Math.abs(distanceY)) { touchAction = TOUCH_MOVE_HORIZONTAL; } else if (Math.abs(distanceY) > touchSlop) { if (mOldX > halfScreenWidth) { // 音量 touchAction = TOUCH_MOVE_VERTICAL_RIGHT; } else if (mOldX < halfScreenWidth) { // 亮度 touchAction = TOUCH_MOVE_VERTICAL_LEFT; } else { return false; } switch (touchAction) { case TOUCH_MOVE_VERTICAL_LEFT: // 亮度调节 float brightnessDelta = -distanceY / 100; // 可根据实际情况调整 WindowManager.LayoutParams layoutParams = window.getAttributes(); float brightness = layoutParams.screenBrightness + brightnessDelta; if (brightness > 1) { brightness = 1; } else if (brightness < 0) { brightness = 0; } layoutParams.screenBrightness = brightness; window.setAttributes(layoutParams); break; case TOUCH_MOVE_VERTICAL_RIGHT: // 音量调节 int volumeDelta = (int) (-distanceY / 10); // 可根据实际情况调整 int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC); int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); int newVolume = currentVolume + volumeDelta; if (newVolume > maxVolume) { newVolume = maxVolume; } else if (newVolume < 0) { newVolume = 0; } audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, newVolume, 0); break; } } break; } gestureDetector.onTouchEvent(event); return super.onTouchEvent(event); }使用onTouchEvent去实现双击
最新发布
10-18
package com.videogo.ui.login; // 新增导入 import com.videogo.openapi.EZPTZAction; import com.videogo.openapi.EZPTZCommand; import android.view.MotionEvent; public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback, Handler.Callback { // ... 已有代码保持不变 ... // 云台控制速度常量 (0-7) private static final int PTZ_SPEED = 4; private boolean mIsSupportPTZ = false; // 设备是否支持云台 @Override protected void onCreate(Bundle savedInstanceState) { // ... 已有代码保持不变 ... // 4. 初始化云台控制按钮 initPTZButtons(); } // 初始化云台控制按钮 private void initPTZButtons() { // 竖屏布局的按钮 setupPTZButton(R.id.ptz_left_btn, EZPTZCommand.EZPTZCommandLeft); setupPTZButton(R.id.ptz_right_btn, EZPTZCommand.EZPTZCommandRight); setupPTZButton(R.id.ptz_top_btn, EZPTZCommand.EZPTZCommandUp); setupPTZButton(R.id.ptz_bottom_btn, EZPTZCommand.EZPTZCommandDown); setupPTZButton(R.id.zoom_add, EZPTZCommand.EZPTZCommandZoomIn); // 物理放大 setupPTZButton(R.id.zoom_reduce, EZPTZCommand.EZPTZCommandZoomOut); // 物理缩小 // 横屏布局的按钮 setupPTZButton(R.id.ptz_left_btn2, EZPTZCommand.EZPTZCommandLeft); setupPTZButton(R.id.ptz_right_btn2, EZPTZCommand.EZPTZCommandRight); setupPTZButton(R.id.ptz_top_btn2, EZPTZCommand.EZPTZCommandUp); setupPTZButton(R.id.ptz_bottom_btn2, EZPTZCommand.EZPTZCommandDown); setupPTZButton(R.id.zoom_add2, EZPTZCommand.EZPTZCommandZoomIn); setupPTZButton(R.id.zoom_reduce2, EZPTZCommand.EZPTZCommandZoomOut); } private void setupPTZButton(int buttonId, final EZPTZCommand command) { ImageButton button = findViewById(buttonId); if (button != null) { button.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (!mIsSupportPTZ) { Toast.makeText(MainActivity.this, "设备不支持云台控制", Toast.LENGTH_SHORT).show(); return true; } switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // 按下时开始云台动作 controlPTZ(command, EZPTZAction.EZPTZActionStart); return true; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: // 松开时停止云台动作 controlPTZ(command, EZPTZAction.EZPTZActionStop); return true; } return false; } }); } } // 云台控制方法 private void controlPTZ(final EZPTZCommand command, final EZPTZAction action) { new Thread(new Runnable() { @Override public void run() { try { // 使用基线客户推荐的混合控制方法 boolean result = EZOpenSDK.getInstance().controlPTZMix( mDeviceSerial, mCameraNo, command, action, PTZ_SPEED ); Log.d(TAG, "PTZ control: " + command + " " + action + " | Result: " + result); } catch (BaseException e) { Log.e(TAG, "PTZ control failed: " + e.getErrorCode(), e); runOnUiThread(() -> Toast.makeText(MainActivity.this, "云台控制失败: " + e.getErrorCode(), Toast.LENGTH_SHORT).show()); } } }).start(); } // 在播放成功回调中检查设备是否支持云台 @Override public boolean handleMessage(@NonNull Message msg) { switch (msg.what) { // ... 已有代码保持不变 ... case MSG_REALPLAY_PLAY_SUCCESS: // ... 已有代码保持不变 ... // 检查设备是否支持云台 checkDevicePTZSupport(); break; // 处理云台角度信息 case EZRealPlayConstants.MSG_PTZ_GET_SUCCESS: handleDevicePtzAngleInfo(msg.obj); break; } return true; } // 检查设备是否支持云台 private void checkDevicePTZSupport() { new Thread(() -> { try { EZDeviceInfo deviceInfo = EZOpenSDK.getInstance().getDeviceInfo(mDeviceSerial); mIsSupportPTZ = deviceInfo.isSupportPTZ(); Log.d(TAG, "Device PTZ support: " + mIsSupportPTZ); if (!mIsSupportPTZ) { runOnUiThread(() -> Toast.makeText(MainActivity.this, "当前设备不支持云台控制", Toast.LENGTH_LONG).show()); } } catch (BaseException e) { Log.e(TAG, "Failed to get device info: " + e.getErrorCode(), e); } }).start(); } // 处理云台角度信息 private void handleDevicePtzAngleInfo(Object obj) { if (obj instanceof EZDevicePtzAngleInfo) { EZDevicePtzAngleInfo info = (EZDevicePtzAngleInfo) obj; Log.d(TAG, "PTZ Angles: Hor[" + info.horStartAng + "-" + info.horEndAng + "] Cur:" + info.horCurAng + " | Ver[" + info.verStartAng + "-" + info.verEndAng + "] Cur:" + info.verCurAng); // 这里可以更新UI显示云台角度信息 // 例如在界面上显示当前云台位置 } } // ... 已有代码保持不变 ... } 你给我的这个云台控制实现方案是从哪里确认需要新增导入为import com.videogo.openapi.EZPTZAction; import com.videogo.openapi.EZPTZCommand;添加到java文件中提示Cannot resolve symbol 'EZPTZAction',Cannot resolve symbol 'EZPTZCommand'
06-25
/** * 此视图内部只能包含一个RecyclerView */ class OutsideLinearLayout(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : LinearLayout(context, attrs, defStyleAttr) { //记录手指按下时的坐标 var mInitialTouchX: Float = -1f var mInitialTouchY: Float = -1f //判断是否需要在分发原MotionEvent事件之前,分发一个滑动距离为滑动阈值的事件 var splitFlag = false constructor(context: Context?, attrs: AttributeSet?) : this(context, attrs, -1) constructor(context: Context?) : this(context, null) override fun dispatchTouchEvent(ev: MotionEvent?): Boolean { ev?.let { if(it.action == MotionEvent.ACTION_DOWN){ mInitialTouchX = it.x mInitialTouchY = it.y splitFlag = true }else if(it.action == MotionEvent.ACTION_MOVE){ var recyclerView = searchRecyclerView(this) //获取ViewPager2的滑动阈值 var touchSlop = getRecyclerViewTouchSlop(recyclerView!!) var isScroll = Math.abs(it.x - mInitialTouchX)>touchSlop||Math.abs(it.y - mInitialTouchY)>touchSlop //检查是否需要插入一个滑动距离为滑动阈值的触摸事件 //需要满足的条件为:1、滑动距离里大于滑动阈值;2、自Down事件发生后,第一次大于滑动阈值 if(isScroll&&splitFlag){ var newMotionEvent = MotionEvent.obtain(it) var newX = it.x if(Math.abs(it.x - mInitialTouchX)>touchSlop){ //使新MotionEvent的x与手指按下时的x之间的差为滑动阈值 if(it.x-mInitialTouchX>0){ newX = mInitialTouchX+touchSlop }else newX = mInitialTouchX-touchSlop } var newY = it.y if(Math.abs(it.y - mInitialTouchY)>touchSlop){ if(it.y-mInitialTouchY>0){ newY = mInitialTouchY+touchSlop }else newY = mInitialTouchY-touchSlop } newMotionEvent.setLocation(newX, newY) //发生新的MotionEvent super.dispatchTouchEvent(newMotionEvent) splitFlag = false } } } return super.dispatchTouchEvent(ev) } /** * 获取RecyclerView的滑动阈值 * @param recyclerView RecyclerView * @return Int 滑动阈值 */ fun getRecyclerViewTouchSlop(recyclerView: RecyclerView): Int { var clazz = RecyclerView::class.java var field = clazz.getDeclaredField("mTouchSlop") field.isAccessible = true var touchSlop = field.get(recyclerView) as Int return touchSlop } /** * 获取ViewGroup中的RecyclerView * @param vg ViewGroup * @return RecyclerView? */ fun searchRecyclerView(vg: ViewGroup):RecyclerView?{ for(i in 0 until vg.childCount){ var view = vg.getChildAt(i) if(view is RecyclerView){ return view }else if(view is ViewGroup){ var recyclerView = searchRecyclerView(view) if(recyclerView!=null){ return recyclerView } } } return null } }
09-30
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值