EditText:在文本选择处理程序单击事件上禁用粘贴/替换菜单弹出窗口

CJB*_*JBS 18 android contextmenu android-widget android-edittext

我的目标是拥有一个EditText没有花哨功能,只有文本选择处理程序更容易移动光标 - 所以没有上下文菜单或弹出窗口.

根据此解决方案,我已通过使用ActionMode Callback事件禁用了文本编辑功能操作栏(复制/粘贴等)的外观.

当文本存在于字段中并且文本中出现单击时,仍然会出现中间中间文本选择句柄(请参见下图).大!我想保持这种行为.我不想要的是单击文本选择句柄本身时出现的"粘贴"菜单.

带粘贴菜单的文本选择手柄

我还通过设置android:longClickable="false"样式XML 禁用了EditText的长按输入.禁用长按可防止单击鼠标(即长按)时出现"粘贴/替换"菜单,但是当在文本中单击鼠标(单触)时,会出现文本选择手柄,并且单击文本选择手柄本身,然后出现"粘贴"菜单选项(当剪贴板中有文本时).这就是我想要阻止的.

从我从源头看到的内容,ActionPopupWindow就是PASTE/REPLACE选项弹出的内容.ActionPopupWindow是公共类android.widget.Editor中私有抽象类HandleView中的受保护变量(mActionPopupWindow)...

如果没有禁用剪贴板服务或编辑Android源代码,有没有办法阻止它显示?我试图定义一个新的样式android:textSelectHandleWindowStyle,并设置android:visibilitygone,但它不起作用(应用程序冻结了一段时间,否则它会显示).

CJB*_*JBS 20

解决方案:覆盖isSuggestionsEnabledcanPaste进入EditText.

对于快速解决方案,请复制下面的类 - 此类将覆盖EditText该类,并相应地阻止所有事件.

对于细节,请继续阅读.

解决方案在于防止PASTE/REPLACE菜单出现在show()(未记录的)android.widget.Editor类的方法中.在菜单出现之前,进行检查if (!canPaste && !canSuggest) return;.用作设置这些变量的基础的两个方法都在EditText类中:

因此,将这些更新合并到也具有setCustomSelectionActionModeCallback的类中,并禁用长按,这里是完整的类,以防止所有编辑(但仍显示文本选择处理程序)来控制游标:

package com.cjbs.widgets;

import android.content.Context;
import android.util.AttributeSet;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.EditText;


/**
 *  This is a thin veneer over EditText, with copy/paste/spell-check removed.
 */
public class NoMenuEditText extends EditText
{
    private final Context context;

    /** This is a replacement method for the base TextView class' method of the same name. This 
     * method is used in hidden class android.widget.Editor to determine whether the PASTE/REPLACE popup
     * appears when triggered from the text insertion handle. Returning false forces this window
     * to never appear.
     * @return false
     */
    boolean canPaste()
    {
       return false;
    }

    /** This is a replacement method for the base TextView class' method of the same name. This method
     * is used in hidden class android.widget.Editor to determine whether the PASTE/REPLACE popup
     * appears when triggered from the text insertion handle. Returning false forces this window
     * to never appear.
     * @return false
     */
    @Override
    public boolean isSuggestionsEnabled()
    {
        return false;
    }

    public NoMenuEditText(Context context)
    {
        super(context);
        this.context = context;
        init();
    }

    public NoMenuEditText(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        this.context = context;
        init();
    }

    public NoMenuEditText(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
        this.context = context;
        init();
    }

    private void init()
    {
        this.setCustomSelectionActionModeCallback(new ActionModeCallbackInterceptor());
        this.setLongClickable(false);
    }


    /**
     * Prevents the action bar (top horizontal bar with cut, copy, paste, etc.) from appearing
     * by intercepting the callback that would cause it to be created, and returning false.
     */
    private class ActionModeCallbackInterceptor implements ActionMode.Callback
    {
        private final String TAG = NoMenuEditText.class.getSimpleName();

        public boolean onCreateActionMode(ActionMode mode, Menu menu) { return false; }
        public boolean onPrepareActionMode(ActionMode mode, Menu menu) { return false; }
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) { return false; }
        public void onDestroyActionMode(ActionMode mode) {}
    }
} 
Run Code Online (Sandbox Code Playgroud)

我在Android v4.4.2和v4.4.3中测试了这个.

  • 为什么地狱android没有给我们禁用复制粘贴的选项,一切都需要成为android中的黑客 (20认同)
  • 此解决方案在Redmi 4设备中不起作用. (2认同)

Mur*_*ain 14

或者只是使用

yourEditText.setLongClickable(false);
Run Code Online (Sandbox Code Playgroud)

或者在XML中

android:longClickable="false"
Run Code Online (Sandbox Code Playgroud)

更新

实际上用户想要禁用文本选择句柄本身

1.创建一个形状(handle.xml)

 <shape xmlns:android="http://schemas.android.com/apk/res/android"
 android:shape="rectangle" >

 <size
    android:height="0dp"
    android:width="0dp" />
 </shape>
Run Code Online (Sandbox Code Playgroud)

2.在EditText中

 android:textSelectHandle="@drawable/handle"
Run Code Online (Sandbox Code Playgroud)

  • 这并不能解决我的问题。(我已经实现了这个,但我可能应该在问题中提到)。禁用长按可防止单击并按住鼠标(即长按)时出现“粘贴/替换”菜单。我描述的场景是在文本中单击鼠标(单击)。发生这种情况时,会出现文本选择手柄,当单击文本选择手柄本身时,会出现“粘贴”菜单选项。这就是我试图阻止的。 (2认同)

Ant*_*aev 7

这是一个禁用"粘贴"弹出窗口的黑客.你必须覆盖EditText方法:

@Override
public int getSelectionStart() {
    for (StackTraceElement element : Thread.currentThread().getStackTrace()) {
        if (element.getMethodName().equals("canPaste")) {
            return -1;
        }
    }
    return super.getSelectionStart();
}
Run Code Online (Sandbox Code Playgroud)

与已接受的答案不同,此解决方案也适用于较新版本的Android.


Lib*_*bin 6

我找不到隐藏菜单弹出窗口的方法,但如果用户点击菜单,您可以禁用粘贴

创建一个自定义EditText并覆盖该onTextContextMenuItem方法并返回false for android.R.id.pasteandroid.R.id.pasteAsPlainTextmenu id.

@Override
public boolean onTextContextMenuItem(int id) {
    switch (id){
        case android.R.id.paste:
        case android.R.id.pasteAsPlainText:
            return false;

    }
    return super.onTextContextMenuItem(id);
}
Run Code Online (Sandbox Code Playgroud)


Ben*_*ais 6

您可以通过执行以下操作完全删除 menuItem:

爪哇:

ActionMode.Callback callback = new ActionMode.Callback() {
            @Override
            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                return true;
            }

            @Override
            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                if (menu != null) {
                    menu.removeItem(android.R.id.paste);
                }
                return true;
            }

            @Override
            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
                return false;
            }

            @Override
            public void onDestroyActionMode(ActionMode mode) {

            }
        };

        mEditText.setCustomInsertionActionModeCallback(callback);

        mEditText.setCustomSelectionActionModeCallback(callback);
Run Code Online (Sandbox Code Playgroud)

科特林:

val callback = object : ActionMode.Callback {
    override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {
        return false
    }

    override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
        return true
    }

    override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
        menu?.removeItem(android.R.id.paste)
        return true
    }

    override fun onDestroyActionMode(mode: ActionMode?) {}
}
Run Code Online (Sandbox Code Playgroud)

然后在 EditText 中使用站点:

fun preventPaste() {
    customInsertionActionModeCallback = callback
    customSelectionActionModeCallback = callback
}
Run Code Online (Sandbox Code Playgroud)


Vla*_*kiy 5

当蓝色视图(插入控制器)根本没有出现时找到另一个解决方案.我使用反射来设置Editor类的目标布尔字段.查看android.widget.Editor和android.widget.TextView以获取更多详细信息.

将以下代码添加到自定义EditText中(包含本主题中的所有先前代码):

@Override
public boolean onTouchEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        // setInsertionDisabled when user touches the view
        this.setInsertionDisabled();
    }
    return super.onTouchEvent(event);
}

/**
 * This method sets TextView#Editor#mInsertionControllerEnabled field to false
 * to return false from the Editor#hasInsertionController() method to PREVENT showing
 * of the insertionController from EditText
 * The Editor#hasInsertionController() method is called in  Editor#onTouchUpEvent(MotionEvent event) method.
 */

private void setInsertionDisabled() {
    try {
        Field editorField = TextView.class.getDeclaredField("mEditor");
        editorField.setAccessible(true);
        Object editorObject = editorField.get(this);

        Class editorClass = Class.forName("android.widget.Editor");
        Field mInsertionControllerEnabledField = editorClass.getDeclaredField("mInsertionControllerEnabled");
        mInsertionControllerEnabledField.setAccessible(true);
        mInsertionControllerEnabledField.set(editorObject, false);
    }
    catch (Exception ignored) {
        // ignore exception here
    }
}
Run Code Online (Sandbox Code Playgroud)

此外,也许你可以找到比onTouch()更好的地方来调用目标方法.

在Android 5.1上测试过