如何通过onTouch事件控制触摸移动的图片不超出屏幕且无抖动
问题原因分析
- 逻辑顺序错误:现有代码先判断View当前坐标是否越界并直接复位,再计算新坐标,边界处会出现「复位→下一次事件判定未越界又设置成超界坐标」的循环,直接导致抖动
- 未记录触点偏移:每次拖动直接将View中点对齐手指,一方面触摸体验不符合直觉,另一方面也会放大边界处的坐标跳变
- 缺少Y轴边界限制:现有代码仅处理了水平方向限制,垂直方向依然会移出屏幕
修复方案
首先在类中声明两个成员变量,用于记录按下时手指相对于View左上角的偏移量:
// 记录按下时触点相对View左上角的偏移 private float downOffsetX; private float downOffsetY;
然后修改onTouch逻辑:先计算无限制的目标坐标,再做边界裁剪,最后统一设置给View:
@Override public boolean onTouch(View v, MotionEvent event) { if (v.getId() == R.id.layout_foreground_image_view_533) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // 按下时记录偏移量,保证拖动时手指始终落在点击的位置 downOffsetX = event.getRawX() - v.getX(); downOffsetY = event.getRawY() - v.getY(); return true; case MotionEvent.ACTION_MOVE: // 先计算无限制的目标坐标 float targetX = event.getRawX() - downOffsetX; float targetY = event.getRawY() - downOffsetY; // 对X坐标做边界裁剪 float maxX = displayMetrics.widthPixels - v.getWidth(); targetX = Math.max(0, Math.min(targetX, maxX)); // 对Y坐标做边界裁剪,不需要Y轴限制可以删掉这部分 float maxY = displayMetrics.heightPixels - v.getHeight(); // 如果页面有状态栏/导航栏,需要把maxY减去对应高度避免被遮挡 // maxY -= statusBarHeight; targetY = Math.max(0, Math.min(targetY, maxY)); // 统一设置坐标 v.setX(targetX); v.setY(targetY); return true; } } return false; }
优化说明
- 新逻辑始终先计算目标坐标再做裁剪,不会出现边界处坐标来回跳的问题,彻底解决抖动
- 记录触点偏移后,拖动时手指点在View的哪个位置,就会一直保持这个相对位置,触摸体验更自然
- 如果不需要限制垂直方向拖动,直接删除Y轴相关的裁剪和设置代码即可
内容的提问来源于stack exchange,提问作者Taha Sami
相关产品推荐
相关产品推荐

