EditText: deshabilita la ventana emergente del menú Pegar / Reemplazar en el evento de clic del controlador de selección de texto

Mi objective es tener un EditText que no tenga características sofisticadas, solo el EditText selección de texto para mover el cursor más fácilmente, por lo que no hay menús contextuales ni ventanas emergentes.

Desactivé la apariencia de la barra de acciones de la función de edición de texto (copiar / pegar, etc.) consumiendo el evento de callback del modo de acción, según esta solución .

La manija central de selección de texto intermedia (ver imagen a continuación) aún aparece cuando existe texto en el campo y se produce un clic dentro del texto. ¡Estupendo! Quiero mantener este comportamiento. Lo que NO deseo es que aparezca el menú “PEGAR” cuando se hace clic en el Mango de selección de texto.

Manija de selección de texto con menú de pegar

También he desactivado la entrada de clic largo para EditText al configurar android:longClickable="false" en el XML de estilos. Desactivar el clic prolongado evita que el menú “Pegar / Reemplazar” aparezca cuando se presiona y mantiene presionado el mouse (es decir, toque prolongado); sin embargo, cuando se presiona el mouse (un toque) dentro del texto, aparece el identificador de selección de texto y se hace clic en el identificador de selección de texto, luego aparece la opción de menú “pegar” (cuando hay texto en el portapapeles). Esto es lo que estoy tratando de prevenir.

Por lo que puedo ver en la fuente, ActionPopupWindow es lo que aparece con las opciones PEGAR / REEMPLAZAR. ActionPopupWindow es una variable protegida (mActionPopupWindow) en la clase abstracta privada HandleView dentro de la clase pública android.widget.Editor …

A menos que deshabilite el servicio del portapapeles o edite el código fuente de Android, ¿hay alguna manera de evitar que esto se muestre? Traté de definir un nuevo estilo para android:textSelectHandleWindowStyle , y configuré android:visibility gone , pero no funcionó (la aplicación se congeló por un tiempo cuando de otro modo se habría mostrado).

Solución: Anular isSuggestionsEnabled y canPaste en EditText .

Para la solución rápida, copie la clase siguiente: esta clase anula la clase EditText y bloquea todos los eventos en consecuencia.

Para los detalles arenosos, sigue leyendo.

La solución consiste en evitar que el menú PEGAR / REPRODUCIR aparezca en el método show() de la clase (no documentada) android.widget.Editor . Antes de que aparezca el menú, se realiza una comprobación de if (!canPaste && !canSuggest) return; . Los dos métodos que se utilizan como base para establecer estas variables están ambos en la clase EditText :

  • isSuggestionsEnabled() es público y, por lo tanto, puede ser anulado.
  • canPaste() no es, y por lo tanto debe ocultarse mediante la introducción de una función del mismo nombre en la clase derivada.

Así que incorporando estas actualizaciones en una clase que también tiene el setCustomSelectionActionModeCallback , y la desactivada haga clic con el botón largo , aquí está la clase completa para evitar toda la edición (pero aún mostrar el controlador de selección de texto ) para controlar el cursor:

 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) {} } } 

He probado esto en Android v4.4.2 y v4.4.3.

o simplemente usa

 yourEditText.setLongClickable(false); 

O en XML

 android:longClickable="false" 

Actualizar

En realidad, el usuario quiere desactivar el manejador de selección de texto

1. Crea una forma (handle.xml)

     

2. En tu EditText

  android:textSelectHandle="@drawable/handle" 

Encontré otra solución cuando la vista azul (controlador de inserción) no aparece en absoluto. Usé la reflexión para establecer el campo booleano objective de la clase Editor. Mire android.widget.Editor y android.widget.TextView para más detalles.

Agregue el siguiente código en su EditText personalizado (con todos los códigos previos en este tema):

 @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 } } 

Además, tal vez pueda encontrar el mejor lugar que onTouch () para llamar al método de destino.

Probado en Android 5.1

No encuentro una manera de ocultar el menú emergente, pero puedes inhabilitar el pegado si el usuario toca el menú

Cree un EditText personalizado y anule el método onTextContextMenuItem y devuelva falso para android.R.id.paste y android.R.id.pasteAsPlainText id del menú.

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

Aquí hay un truco para deshabilitar la ventana emergente “pegar”. Tienes que anular el método EditText :

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

Esta solución también funciona en las versiones más nuevas de Android, a diferencia de la respuesta aceptada.

 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"    By Using these two combinations my problem is solved. 

Si necesita eliminar la sugerencia PASTE, borre el portapapeles antes del clic prolongado.

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

Ninguna de las soluciones anteriores funcionó para mí. Logré hacer mi solución (explicación posterior), que desactivó pegar algo en EditText mientras mantenía todas las otras operaciones válidas.
Principalmente, debes anular este método en tu implementación de EditText:

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

Así que investigar el código EditText, después de todas las comprobaciones, pegar (y todas las acciones de ContextMenu en EditText) ocurre en un método llamado 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; } 

Si lo nota, pegar solo ocurrirá cuando id == ID_PASTE , entonces, de nuevo, mirando el código EditText:

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

Encontré una manera simple pero confiable. La idea es consumir el evento táctil, para evitar que el evento toque scope el subrayado del código predeterminado.

  1. Para deshabilitar copiar / pegar emergente.
  2. Para deshabilitar el controlador de selección de texto.
  3. Todavía muestra el cursor al final del texto.
  4. Todavía mostrando el teclado.

 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); } 

enter image description here

Tenga en cuenta que el cursor parpadeante aún se muestra al final del texto. Solo que la captura de pantalla no puede capturarlo.

Simplemente anule un método:

 @Override protected MovementMethod getDefaultMovementMethod() { // we don't need arrow key, return null will also disable the copy/paste/cut pop-up menu. return null; } 

Puedes usar este código:

 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; } }); } 

La devolución de false de onCreateActionMode deshabilitará las opciones de cortar, copiar y pegar en un nivel de API superior a 11.