5.2 点击、长按、触摸与键盘处理


5.2 点击、长按、触摸与键盘处理

本节摘要:输入事件按粒度分三层——单击与长按是成品手势,触摸是原始轨迹,键盘是文本通道。本节给猜数字 App 接齐三类输入:长按标题显示答案彩蛋、触摸滑动清空输入、软键盘"完成"直接提交,并处理返回键拦截这一高频需求。

成品手势:click 与 longClick

单击上一节讲透了,长按只是多一个监听器,但它的交互语义值得单独说:长按适合低频、有破坏性或"藏起来"的操作。猜数字 App 把答案范围做成彩蛋——长按标题两秒,弹出真实提示:

binding.tvTitle.setOnLongClickListener { Toast.makeText(this, "答案是 1 到 100 里的某个数", Toast.LENGTH_SHORT).show() true // 消费长按,避免再触发单击 }

返回值沿用上一节的消费语义:返回 true 告诉系统长按已被处理,同一按次不会再派发单击。返回 false 的后果是长按松手后又补一次 onClick,用户会看到彩蛋和提交动作接连触发——两个监听器打架时,先查返回值。

View 还提供了便捷的 isClickableperformClick():前者控制控件可否响应点击(默认 Button 可点、TextView 不可点),后者在代码里模拟一次点击。上一节 onTouch 例子里调 performClick 正是为了保住无障碍语义——读屏辅助服务依赖 click 事件播报"已点击",绕过它自己发动画是常见的无障碍事故。

原始轨迹:onTouch 与坐标

onTouch 拿到的是未加工的 MotionEvent 流:ACTION_DOWN 一枚、ACTION_MOVE 若干、ACTION_UP 收尾,event.x / event.y 给出指尖相对控件左上角的坐标。做一个"甩掉输入"的交互——在输入框上快速左滑即清空:

binding.etGuess.setOnTouchListener { v, event -> when (event.actionMasked) { MotionEvent.ACTION_DOWN -> { startX = event.x false // 不消费,点击照常 } MotionEvent.ACTION_UP -> { val dx = event.x - startX if (dx < -120) { // 左滑超过 120px 视为甩动 binding.etGuess.text.clear() Toast.makeText(this, "已清空", Toast.LENGTH_SHORT).show() v.performClick() true } else false } else -> false } }

两处设计说明:DOWN 返回 false 让单击与焦点行为不受影响,只有确认是甩动后才消费——消费点尽量晚,正常手势尽量不惊扰;阈值用像素只是演示,正式代码应换算成 dp 以适配不同密度屏幕(2.2 节的换算工具在这等着)。

键盘通道:动作键与返回键

软键盘不只会输入字符,右下角那颗动作键是可以接管的。1.1 节已经给 EditText 配了 imeOptions="actionDone",这里补全代码与另一种常用形态——"下一项":

binding.etGuess.setOnEditorActionListener { _, actionId, _ -> if (actionId == EditorInfo.IME_ACTION_DONE) { binding.btnGuess.performClick() // 复用提交逻辑,不复制粘贴 true } else false }

注意 performClick() 的用法:提交逻辑只写在按钮的 onClick 里,键盘路径通过模拟点击汇入同一段代码——一个动作一条逻辑,两个入口,复制粘贴两份逻辑必然在日后改动时漏改一半。

返回键是另一条必经之路。猜数字进行到一半按返回键,直接退出太粗暴,拦下来问一句"确定放弃本局吗":

override fun onBackPressed() { if (game.inProgress) { showGiveUpDialog() // 确认后 finish() } else { super.onBackPressed() } }

图:输入事件的三层粒度

图:输入事件的三层粒度

本节要点回顾

  • 长按返回 true 消费:否则长按松手又补一次单击,两个监听器打架。
  • performClick 保无障碍:绕过 click 体系做交互时,记得替读屏服务补报。
  • onTouch 消费点尽量晚:DOWN 不拦截,确认手势成立才返回 true。
  • 一个动作一条逻辑:键盘提交用 performClick 汇入按钮逻辑,拒绝复制粘贴。
  • 返回键拦截要有出口:条件不满足时务必调 super,别把用户锁死在界面里。

两个补充追问

多点触控要不要现在学? 单指场景用 actionMasked 加索引已经够用;双指缩放这类需求优先找现成手势工具类(如系统的 ScaleGestureDetector),别自己解析原始多点事件——那是另一个深度的坑,等真实需求来了再跳不迟。

所有控件都该能点吗? 恰恰相反,可点区域要克制。界面上每个可点元素都在向用户承诺"点了有事发生",承诺过多而兑现不了,信任就贬了值。不可点的装饰元素保持默认不可点状态,读屏与点击意图都干净,这既是交互设计也是无障碍礼貌。

最后提醒一个测试习惯:任何新接的输入路径都要做"暴力测试"——快速连点、长按后滑动、输入框里粘贴超长文本、转屏瞬间按键。输入事件的 bug 几乎都藏在非常规操作序列里,发布前五分钟的暴力测试,抵得上用户群里一晚上的吐槽。

键盘处理还有一个易漏角落:返回焦点管理。多个输入框顺序填写时,imeOptions 配 actionNext 可以在键盘上一路"下一项"走到提交,这比每格都点一下省一半操作;而输入完成后主动收起键盘,是许多 App 缺失的最后一厘米礼仪。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U