自定义ScanInputView的OnEditorActionListener无法触发问题求助
大家好,我现在遇到一个头疼的问题——我给自定义的ScanInputView设置了OnEditorActionListener,但无论按回车键还是点击软键盘的完成按钮,这个监听器都触发不了。我已经试过改imeOptions,比如从actionDone改成actionGo,但完全没用,希望各位能帮我看看问题出在哪!
先给大家贴一下相关代码:
使用ScanInputView的Fragment代码
class WMScanEmpfaengerFragment : TransactionNavigationFragment<WMScanEmpfaengerViewModel>() { private lateinit var binding: FragmentScanEmpfaengerBinding override fun getViewModelClass() = WMScanEmpfaengerViewModel::class override fun getContentLayoutResId() = R.layout.fragment_scan_empfaenger override fun isTransactionRoot() = true override var barcodeScannerEnabled = true override fun onViewCreated(view: View, savedInstanceState: Bundle?) { binding = FragmentScanEmpfaengerBinding.inflate(layoutInflater) binding.scaninputView.setOnEditorActionListener( OnEditorActionListener { v, actionId, event -> if (event != null && event.keyCode == KeyEvent.KEYCODE_ENTER || actionId == EditorInfo.IME_ACTION_DONE && v.text.isNotBlank()) { setEmpfaenger(v.text.toString().trim()) true } false }) } }
自定义View ScanInputView代码
class ScanInputView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : ConstraintLayout(context, attrs, defStyleAttr) { private val binding: ViewScaninputBinding = ViewScaninputBinding.inflate(LayoutInflater.from(context), this) init { val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ScanInputView) val hint = typedArray.getString(R.styleable.ScanInputView_hint) val imeNext = typedArray.getBoolean(R.styleable.ScanInputView_imeNext, false) typedArray.recycle() binding.inputId.showSoftInputOnFocus = false binding.inputId.addTextChangedListener(AppUtils.onTextChanged { binding.inputLayoutId.error = null }) setHint(hint) if (imeNext) binding.inputId.imeOptions = EditorInfo.IME_ACTION_NEXT binding.btnKeyboard.setOnClickListener { val keyboard = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager if (!binding.inputId.hasFocus()) { binding.inputId.requestFocus() moveCursorToEnd() } keyboard.showSoftInput(binding.inputId, 0) } } fun setOnEditorActionListener(editorActionListener: TextView.OnEditorActionListener) { binding.inputId.setOnEditorActionListener(editorActionListener) } // 省略其他方法... }
ScanInputView对应的布局XML
<?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" tools:layout_height="wrap_content" tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout" android:id="@+id/viewScanInput"> <com.google.android.material.textfield.TextInputLayout android:id="@+id/input_layout_id" android:layout_width="0dp" android:layout_height="wrap_content" android:orientation="horizontal" app:layout_constraintEnd_toStartOf="@id/btn_keyboard" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/img_topbanner"> <com.google.android.material.textfield.TextInputEditText android:id="@+id/input_id" android:layout_width="match_parent" android:layout_height="wrap_content" android:saveEnabled="false" android:imeOptions="actionDone" android:inputType="text" android:maxLines="1" android:singleLine="true" /> </com.google.android.material.textfield.TextInputLayout> <ImageButton android:id="@+id/btn_keyboard" android:layout_width="40dp" android:layout_height="40dp" android:layout_marginTop="14sp" android:background="?attr/selectableItemBackgroundBorderless" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@id/input_layout_id" app:layout_constraintTop_toTopOf="@id/input_layout_id" app:srcCompat="@drawable/ic_icon_keyboard" app:tint="@color/colorPrimary" /> </merge>
问题排查与解决方案
我仔细梳理了你的代码,发现几个可能导致监听器不触发的关键点,咱们逐个解决:
1. 监听器返回值逻辑错误(最可能的原因)
你当前的监听器写法有逻辑漏洞:if{... true} false 这种结构会导致不管条件是否满足,最终都会返回false,因为if块里的true并没有作为整个lambda的返回值。正确的写法需要明确返回结果:
binding.scaninputView.setOnEditorActionListener { v, actionId, event -> // 只处理按键抬起事件,避免重复触发 val isEnterUp = event != null && event.action == KeyEvent.ACTION_UP && event.keyCode == KeyEvent.KEYCODE_ENTER val isDoneAction = actionId == EditorInfo.IME_ACTION_DONE && v.text.isNotBlank() if (isEnterUp || isDoneAction) { setEmpfaenger(v.text.toString().trim()) return@OnEditorActionListener true // 明确标记事件已被消费 } false }
2. ImeOptions可能被覆盖
在ScanInputView的初始化代码中,如果imeNext属性为true,会将输入框的imeOptions设为IME_ACTION_NEXT,但你在Fragment里判断的是IME_ACTION_DONE,这会导致条件不匹配。
如果你的场景需要使用IME_ACTION_DONE,请确保:
- 在布局中给
ScanInputView设置app:imeNext="false" - 或者在
ScanInputView的init代码中补全默认逻辑:
if (imeNext) { binding.inputId.imeOptions = EditorInfo.IME_ACTION_NEXT } else { binding.inputId.imeOptions = EditorInfo.IME_ACTION_DONE // 明确默认值 }
3. 确保事件能传递到输入框
因为ScanInputView是继承自ConstraintLayout,有可能父布局拦截了按键事件。可以在ScanInputView中重写dispatchKeyEvent,确保事件优先传递给内部输入框:
override fun dispatchKeyEvent(event: KeyEvent?): Boolean { // 先让内部输入框处理事件,处理成功就直接返回 return binding.inputId.dispatchKeyEvent(event) || super.dispatchKeyEvent(event) }
4. 焦点与软键盘设置验证
你设置了showSoftInputOnFocus = false,这会阻止自动弹出软键盘,但要确保输入框能正常获取焦点——点击键盘按钮时已经做了requestFocus(),手动点击输入框时也应该能触发焦点获取。
按这个顺序调整代码后,再测试按回车键或软键盘完成按钮,应该就能正常触发setEmpfaenger方法了。如果还有问题,可以补充一下测试时的焦点状态、软键盘类型等细节~
内容来源于stack exchange

