EditText: отключить всплывающее меню "Вставить / заменить" в событии щелчка обработчика выбора текста

Моя цель - иметь EditText это не имеет никаких причудливых функций, только обработчик выбора текста для более удобного перемещения курсора - так что нет контекстных меню или всплывающих окон.

Я отключил внешний вид панели действий функции редактирования текста (копирование / вставка и т. Д.), Используя событие обратного вызова ActionMode в соответствии с этим решением.

Средний Средний дескриптор выбора текста (см. Изображение ниже) по-прежнему отображается, когда в поле существует текст и в тексте происходит щелчок. Большой! Я хочу сохранить это поведение. Чего я не хочу, так это того, чтобы меню "PASTE" появлялось при щелчке по самому дескриптору выбора текста.

Ручка выделения текста с меню вставки

Я также отключил ввод длинным щелчком для EditText, установив android:longClickable="false" в стилях XML. Отключение длинного щелчка предотвращает появление меню "Вставить / Заменить" при нажатии и удерживании мыши (т. Е. При длительном касании), однако при щелчке мышью (одно касание) внутри текста появляется дескриптор выделения текста, и когда При щелчке по самому дескриптору выделения текста появляется пункт меню "вставить" (когда в буфере обмена есть текст). Это то, что я пытаюсь предотвратить.

Из того, что я могу видеть из источника, ActionPopupWindow это то, что всплывает с опциями PASTE/REPLACE. ActionPopupWindow - это защищенная переменная (mActionPopupWindow) в закрытом абстрактном классе HandleView внутри открытого класса android.widget.Editor...

Если не считать отключение службы буфера обмена или редактирование исходного кода Android, можно ли как-то предотвратить это? Я пытался определить новый стиль для android:textSelectHandleWindowStyleи установить android:visibility в gone, но это не сработало (приложение на какое-то время зависло, если бы оно не показывалось).

16 ответов

Решение

Решение: переопределить isSuggestionsEnabled а также canPaste в 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) {}
    }
} 

Я проверял это в Android v4.4.2 и v4.4.3.

Или просто использовать

yourEditText.setLongClickable(false);

ИЛИ в XML

android:longClickable="false"

Обновить

На самом деле пользователь хочет отключить дескриптор выделения текста сам

1. Создайте фигуру (handle.xml)

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

 <size
    android:height="0dp"
    android:width="0dp" />
 </shape>

2. В вашем EditText

 android:textSelectHandle="@drawable/handle"

Вот взломать, чтобы отключить "вставить" всплывающее окно. Вы должны переопределить EditText метод:

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

Это решение работает и на более новых версиях Android, в отличие от принятого ответа.

Я не нахожу способ скрыть всплывающее меню, но вы можете отключить вставку, если пользователь нажмет на меню

Создать кастом EditText и переопределить onTextContextMenuItem метод и вернуть ложь для android.R.id.paste а также android.R.id.pasteAsPlainText идентификаторы меню.

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

    }
    return super.onTextContextMenuItem(id);
}

Вы можете полностью удалить menuItem, выполнив следующие действия:

Ява:

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

            @Override
            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                return true;
            }

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

            @Override
            public void onDestroyActionMode(ActionMode mode) {

            }
        };

        mEditText.setCustomInsertionActionModeCallback(callback);

        mEditText.setCustomSelectionActionModeCallback(callback);

Котлин:

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

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

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

    override fun onDestroyActionMode(mode: ActionMode?) {}
}

затем для использования сайта в EditText:

fun preventPaste() {
    customInsertionActionModeCallback = callback
    customSelectionActionModeCallback = callback
}

Нашел другое решение, когда синий вид (контроллер вставки) вообще не появился. Я использовал отражение, чтобы установить целевое логическое поле класса 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
    }
}

Также, возможно, вы можете найти лучшее место, чем onTouch() для вызова целевого метода.

Проверено на Android 5.1

Он исправил все упомянутые ниже 3 изменения

fun TextView.disableCopyPaste() {
isLongClickable = false.  //  change 1 ,  disable Long click
setTextIsSelectable(false). //  change 2  ,  disable text selection click

//change 3 ,  return false from all actionmode 
 customSelectionActionModeCallback = object : ActionMode.Callback {
    override fun onCreateActionMode(mode: ActionMode?, menu: Menu): Boolean {
        return false
    }

    override fun onPrepareActionMode(mode: ActionMode?, menu: Menu): Boolean {
        return false
    }

    override fun onActionItemClicked(mode: ActionMode?, item: MenuItem): Boolean {
        return false
    }

    override fun onDestroyActionMode(mode: ActionMode?) {}
}

}

Use this in java file

if (android.os.Build.VERSION.SDK_INT < 11) {
    editText.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {

        @Override`enter code here`
        public void onCreateContextMenu(ContextMenu menu, View v,
                ContextMenuInfo menuInfo) {
            // TODO Auto-generated method stub
            menu.clear();
        }
    });
} else {
    editText.setCustomSelectionActionModeCallback(new ActionMode.Callback() {

        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            // TODO Auto-generated method stub
            return false;
        }

        public void onDestroyActionMode(ActionMode mode) {
            // TODO Auto-generated method stub

        }

        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            // TODO Auto-generated method stub
            return false;
        }

        public boolean onActionItemClicked(ActionMode mode,
                MenuItem item) {
            // TODO Auto-generated method stub
            return false;
        }`enter code here`
    });
}


With this code also add android:textSelectHandle="@drawable/handle"
<shape xmlns:android="http://schemas.android.com/apk/res/android"
 android:shape="rectangle" >

 <size
    android:height="0dp"
    android:width="0dp" />
 </shape>


By Using these two combinations my problem is solved.

Если вам нужно удалить предложение PASTE, очистите буфер обмена перед долгим щелчком.

//class 
ClipboardManager clipboard;

//oncreate 
clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("","");
clipboard.setPrimaryClip(clip);

С помощью этого простого кода вы можете отключить панель инструментов выбора текста:

      // MainActivity.java
@Override
public void onActionModeStarted(android.view.ActionMode mode) {
    mode.getMenu().clear();
    super.onActionModeStarted(mode);
}

Ни одно из вышеперечисленных решений не помогло мне. Мне удалось сделать свое решение (объяснение после), которое отключило вставку чего-либо в EditText при сохранении всех остальных операций в силе.
Главным образом, вы должны переопределить этот метод в вашей реализации EditText:

@Override
public boolean onTextContextMenuItem (int id) {
    if (id == android.R.id.paste) return false;

    return super.onTextContextMenuItem(id);
}

Так исследуя код EditText, после всех проверок вставьте (и все ContextMenu действия над EditText) происходят в методе, называемом onTextContextMenuItem:

public boolean onTextContextMenuItem(int id) {
    int min = 0;
    int max = mText.length();

    if (isFocused()) {
        final int selStart = getSelectionStart();
        final int selEnd = getSelectionEnd();

        min = Math.max(0, Math.min(selStart, selEnd));
        max = Math.max(0, Math.max(selStart, selEnd));
    }

    switch (id) {
        case ID_SELECT_ALL:
            // This does not enter text selection mode. Text is highlighted, so that it can be
            // bulk edited, like selectAllOnFocus does. Returns true even if text is empty.
            selectAllText();
            return true;

        case ID_PASTE:
            paste(min, max);
            return true;

        case ID_CUT:
            setPrimaryClip(ClipData.newPlainText(null, getTransformedText(min, max)));
            deleteText_internal(min, max);
            stopSelectionActionMode();
            return true;

        case ID_COPY:
            setPrimaryClip(ClipData.newPlainText(null, getTransformedText(min, max)));
            stopSelectionActionMode();
            return true;
    }
    return false;
}

Если вы заметили, вставка будет происходить только тогда, когда id == ID_PASTEИтак, еще раз, глядя на код EditText:

static final int ID_PASTE = android.R.id.paste;

Я нашел одно простое решение. Надеюсь, это кому-то поможет: Расширить класс Edittetxt и методы override ниже. Таким же образом, если вы хотите отключить другие параметры, сравнивmenu.getItem(i).getTitle() ты можешь это сделать.

private class ActionModeCallbackInterceptor implements ActionMode.Callback
    {
        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            return true;
        }

        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            for(int i =0;i<menu.size();i++){
                if(menu.getItem(i).getTitle().toString().equals("Clipboard") 
               || menu.getItem(i).getTitle().toString().equals("Paste")) {
                    menu.getItem(i).setVisible(false);
                }
            }
              return false;
        }

        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
            return false;
        }
        public void onDestroyActionMode(ActionMode mode) {}
    }

Я нашел простой, но надежный способ. Идея состоит в том, чтобы уничтожить событие касания, чтобы предотвратить достижение события касания, подчеркивающего код по умолчанию.

  1. Чтобы отключить копирование / вставку всплывающих окон.
  2. Отключить обработчик выделения текста.
  3. По-прежнему показывает курсор в конце текста.
  4. Все еще показываю клавиатуру.

maskedEditText.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        focusAndShowKeyboard(view.getContext(), maskedEditText);
        // Consume the event.
        return true;
    }
});

private static void focusAndShowKeyboard(Context context, EditText editText) {
    editText.requestFocus();
    InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY);
}

Обратите внимание, мигающий курсор все еще отображается в конце текста. Просто скриншот не смог его запечатлеть.

Вы можете использовать этот код:

if (android.os.Build.VERSION.SDK_INT < 11) {
    editText.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {

        @Override
        public void onCreateContextMenu(ContextMenu menu, View v,
                ContextMenuInfo menuInfo) {
            // TODO Auto-generated method stub
            menu.clear();
        }
    });
} else {
    editText.setCustomSelectionActionModeCallback(new ActionMode.Callback() {

        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            // TODO Auto-generated method stub
            return false;
        }

        public void onDestroyActionMode(ActionMode mode) {
            // TODO Auto-generated method stub

        }

        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            // TODO Auto-generated method stub
            return false;
        }

        public boolean onActionItemClicked(ActionMode mode,
                MenuItem item) {
            // TODO Auto-generated method stub
            return false;
        }
    });
}

Возвращение ложного из onCreateActionMode отключит опции вырезания, копирования, вставки на уровне API больше 11.

Я просто использовал некоторые из вышеперечисленных решений в Android 11, и они работают нормально, вы можете использовать приведенную ниже суть. https://gist.github.com/harshmittal2810/26429eb426dd1b31750cb33b47f449a6

Просто переопределите один метод:

@Override
protected MovementMethod getDefaultMovementMethod() {
    // we don't need arrow key, return null will also disable the copy/paste/cut pop-up menu.
    return null;
}
Другие вопросы по тегам