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
учебный класс:
isSuggestionsEnabled()
является общедоступным и, таким образом, может быть переопределено.canPaste()
не является, и, следовательно, должен быть скрыт, введя функцию с тем же именем в производном классе.
Таким образом, включение этих обновлений в класс, который также имеет 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) {}
}
Я нашел простой, но надежный способ. Идея состоит в том, чтобы уничтожить событие касания, чтобы предотвратить достижение события касания, подчеркивающего код по умолчанию.
- Чтобы отключить копирование / вставку всплывающих окон.
- Отключить обработчик выделения текста.
- По-прежнему показывает курсор в конце текста.
- Все еще показываю клавиатуру.
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;
}