使用代码调出 TextView/EditText 的编辑菜单

0x0

大家都知道在输入框长按文字,会出现编辑菜单。最近遇到一个需求:代码直接调出 EditText(TextView 需要设置 setTextIsSelectable(true)) 的编辑菜单,这里我叫它 EditorActionMenu
在这里插入图片描述
既然通过长按可以调出,为何不直接 EditText.performLongClick()View.showContextMenu() 方法。事实证明,此代码无法调出 EditorActionMenu,下面进行分析如何弹出编辑菜单。

过程分析

  • 编辑菜单弹出过程: 按下 -> 等待 -> 松开 -> 弹出菜单

对应 ViewTouch 事件:
ACTION_DOWN => performLongClick => ACTION_UP

由于 TextView 拦截了 onTouchEventonTouchEventperformLongClick 源码结合长按过程和代码调试可以分析出真正显示菜单的代码执行过程:
mEditor.onTouchEvent(event) => mEditor.performLongClick(handled); => mEditor.startInsertionActionMode(); =>

TextView onTouchEventperformLongClick 源码(省略部分代码):

 	@Override
    public boolean performLongClick() {
   		//.....
        if (mEditor != null) {
        	//长按事件
            handled |= mEditor.performLongClick(handled);
            mEditor.mIsBeingLongClicked = false;
        }
        //....
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        final int action = event.getActionMasked();
        if (mEditor != null) {
            mEditor.onTouchEvent(event);
            if (mEditor.mSelectionModifierCursorController != null
                    && mEditor.mSelectionModifierCursorController.isDragAcceleratorActive()) {
                return true;
            }
        }
        final boolean superResult = super.onTouchEvent(event);
		// ACTION_UP 后执行
        if (mEditor != null && mEditor.mDiscardNextActionUp && action == MotionEvent.ACTION_UP) {
            mEditor.mDiscardNextActionUp = false;

            if (mEditor.mIsInsertionActionModeStartPending) {
                mEditor.startInsertionActionMode();
                mEditor.mIsInsertionActionModeStartPending = false;
            }
            return superResult;
        }
        //.......
        //.......
        return superResult;
    }

实现

反射实现

根据上面分析,菜单的弹出由 Editor 类控制,但这个类不对外开放 (被@hide标注) 在开发中无法接触到这个类。利用反射可以实现,但考虑反射可能带来出乎意料的情况,并且 Android P 已禁止利用反射进行这种操作,这里就不考虑了。

模拟实现

利用代码模拟 TouchEvent 来模拟手指动作。

这里使用 KotlinTextView 扩展一个 showEditorActionMenu 方法:

fun TextView.showEditorActionMenu() {
	//获取焦点
    requestFocus()
	//按下坐标
	val x: Float = (width / 2).toFloat()
    val y: Float = (height / 2).toFloat()
    // 按下事件
	onTouchEvent(newMotionEvent(MotionEvent.ACTION_DOWN, x, y))
    //延时发送松开事件
    postDelayed({
       onTouchEvent(newMotionEvent(MotionEvent.ACTION_UP, x, y))
    }, ViewConfiguration.getLongPressTimeout().toLong())
}
private fun newMotionEvent(action: Int, x: Float, y: Float): MotionEvent {
    //无需考虑 按下时间和事件时间
    return MotionEvent.obtain(0, 0, action, x, y, 0)
}

不过上面有个缺点, postDelayed 造成等待 LongPressTimeout 时间后才显示菜单。
在这里插入图片描述

优化模拟

不使用延时,直接调用 ACTION_DOWN => performLongClick => ACTION_UP

fun TextView.showEditorActionMenu() {
	//获取焦点
    requestFocus()
	//按下坐标
	val x: Float = (width / 2).toFloat()
    val y: Float = (height / 2).toFloat()
    // 按下事件
    onTouchEvent(newMotionEvent(MotionEvent.ACTION_DOWN, x, y))
    //长按事件
    performLongClick()
    //发送松开事件
    onTouchEvent(newMotionEvent(MotionEvent.ACTION_UP, x, y))
}
private fun newMotionEvent(action: Int, x: Float, y: Float): MotionEvent {
    //无需考虑 按下时间和事件时间
    return MotionEvent.obtain(0, 0, action, x, y, 0)
}

速度相比优化前稍微快些
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在 Android 中,TextViewEditText 都是常用的用户界面元素,用于显示文本和接收用户输入。layout_constraintEnd_toEndOf 属性是用于约束布局的属性,用于指定视图的右侧边缘与另一个视图的右侧边缘对齐。 下面是对 TextViewEditText 的 layout_constraintEnd_toEndOf 属性的使用方法的说明: 1. 布局文件中添加 TextViewEditText 元素: ```xml <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="This is a TextView" /> <EditText android:id="@+id/editText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:hint="Enter text here" /> ``` 2. 使用 layout_constraintEnd_toEndOf 属性约束 TextViewEditText: ```xml <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="This is a TextView" app:layout_constraintEnd_toEndOf="@id/editText" /> <EditText android:id="@+id/editText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:hint="Enter text here" /> ``` 在上述代码中,TextView 的 layout_constraintEnd_toEndOf 属性被设置为 @id/editText,表示 TextView 的右侧边缘与 EditText 的右侧边缘对齐。 这样,当布局被渲染时,TextView 的右侧边缘将与 EditText 的右侧边缘对齐,从而实现了视图之间的约束关系。 希望这个回答对你有帮助!如果还有其他问题,请随时提问。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值