Cómo deshabilitar copiar / pegar desde / a EditText

131

En mi aplicación, hay una pantalla de registro, donde no quiero que el usuario pueda copiar / pegar texto en el EditTextcampo. He configurado uno onLongClickListeneren cada uno EditTextpara que no aparezca el menú contextual que muestra el método copiar / pegar / entrada y otras opciones. Por lo tanto, el usuario no podrá copiar / pegar en los campos Editar.

 OnLongClickListener mOnLongClickListener = new OnLongClickListener() {

        @Override
        public boolean onLongClick(View v) {
            // prevent context menu from being popped up, so that user
            // cannot copy/paste from/into any EditText fields.
            return true;
        }
    };

Pero el problema surge si el usuario ha habilitado un teclado de terceros que no sea el predeterminado de Android, que puede tener un botón para copiar / pegar o que puede mostrar el mismo menú contextual. Entonces, ¿cómo deshabilito copiar / pegar en ese escenario?

Avíseme si hay otras formas de copiar / pegar también. (y posiblemente cómo deshabilitarlos)

Cualquier ayuda sería apreciada.

rDroid
fuente
Si la operación "pegar" proviene de un IME, entonces no tiene una forma estándar de distinguirlo de las pulsaciones de teclas normales. Una idea para intentar es medir el tiempo entre la llegada de cada personaje y si el tiempo es demasiado corto, entonces los personajes provienen de una operación de "pegar".
BitBank
parece ser una solución sucia! Vale la pena echarle un vistazo.
rDroid
1
use android: longClickable = "false"
Azay Gupta

Respuestas:

112

Si está utilizando API nivel 11 o superior, puede detener la copia, pegar, cortar y que aparezcan menús contextuales personalizados.

edittext.setCustomSelectionActionModeCallback(new ActionMode.Callback() {

            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                return false;
            }

            public void onDestroyActionMode(ActionMode mode) {                  
            }

            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                return false;
            }

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

Devolver falso de onCreateActionMode (ActionMode, Menu) evitará que se inicie el modo de acción (acciones Seleccionar todo, Cortar, Copiar y Pegar).

Zain Ali
fuente
1
¿Qué pasa con el nivel de API por debajo de 13?
Jonathan
1
No entiendo ninguno de los comentarios, esta muestra funciona api11 +, pre-api11 no había copiar y pegar IIRC
scottyab
28
No funciona para mí. Aparecerá el botón Pegar en caso de tocar el indicador de cursor azul.
nulo el
8
Tampoco funciona para mí. Al pulsar dos veces se muestra el menú de copiar y pegar.
Android Killer el
esto ya no funciona en Android 6.0, verifique esta respuesta stackoverflow.com/questions/27869983/…
has19
132

El mejor método es usar:

etUsername.setLongClickable(false);
Vicky Kapadia
fuente
58
O, simplemente en xml android:longClickable="false":)
lomza
19
El botón Pegar aparecerá en caso de tocar el indicador azul del cursor.
nulo
16
Esto sin duda evitará que se pueda hacer clic durante mucho tiempo en la vista, pero los controles de edición también se pueden solicitar haciendo doble clic en el texto, lo que significa que esta solución no está completa. Tenga esto en cuenta para sus propósitos.
Kevin Grant
1
Además, los atajos de teclado aún podrían funcionar (Ctrl + C) con teclados externos.
Oleg Vaskevich
Esto no funciona en Ice Cream Sandwich porque las opciones del portapapeles se pueden abrir tocando dos veces el texto, así como tocando por unos segundos.
Paul Wintz
44

Puede hacer esto deshabilitando la pulsación larga de EditText

Para implementarlo, simplemente agregue la siguiente línea en el xml:

android:longClickable="false"
Ameya Pandilwar
fuente
66
El problema era que el usuario de mi aplicación tiene un teclado de terceros que tiene un botón copiar y pegar.
rDroid
3
Otro problema es que puede seleccionar el texto tocando dos veces y muestra copiar / pegar nuevamente
Nikola
37

Puedo desactivar la funcionalidad de copiar y pegar con lo siguiente:

textField.setCustomSelectionActionModeCallback(new ActionMode.Callback() {

    public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
        return false;
    }

    public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
        return false;
    }

    public boolean onActionItemClicked(ActionMode actionMode, MenuItem item) {
        return false;
    }

    public void onDestroyActionMode(ActionMode actionMode) {
    }
});

textField.setLongClickable(false);
textField.setTextIsSelectable(false);

Espero que funcione para ti ;-)

Joseph Johnson
fuente
Esta es exactamente la misma solución que terminé en base a las otras respuestas anteriores. Esto debería marcarse como la solución correcta, ya que maneja los casos
extremos que
2
Esta opción bloquea la copia, pero aún puede pegar haciendo clic en el cursor.
Mehul Kanzariya
12

Aquí hay una mejor manera de deshabilitar cortar, copiar y pegar del trabajo de edición de texto en todas las versiones

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;
            }
        });
    }
Hardik
fuente
Esto funcionó para mí, solo tenía que agregar @TargetApi (Build.VERSION_CODES.HONEYCOMB)
Sheepdogsheep
11

Además de setCustomSelectionActionModeCallback y las soluciones de clic largo deshabilitadas , es necesario evitar que aparezcan los menús PEGAR / REEMPLAZAR cuando se hace clic en el controlador de selección de texto, como se muestra en la imagen a continuación:

Manija de selección de texto con menú pegar

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

Una respuesta más completa está disponible aquí .

CJBS
fuente
Esta es la solución CORRECTA y COMPLETA
FireZenk
En algunos dispositivos, en lugar de la opción Pegar portapapeles, está visible, actúa solo como pegar. Comprobé los enlaces pero puedo evitar pegar pero no el portapapeles. alguna idea ?
Richa
10

Solución Kotlin:

fun TextView.disableCopyPaste() {
    isLongClickable = false
    setTextIsSelectable(false)
    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?) {}
    }
}

Entonces puede simplemente llamar a este método en su TextView:

override fun onCreate() {
    priceEditText.disableCopyPaste()
}
Alexandr
fuente
1
Hola, estoy usando este enfoque, pero recibo un Type mismatcherror con esta descripción Required:ActionMode.Callback! Found: en esta parte object: ActionMode.Callback. ¿Alguna idea de por qué podría no funcionar?
Abdul Mateen
8

Usando otras soluciones, API 26 (Oreo) todavía mostraba el control del cursor con un solo toque en el texto ingresado, y luego se puede mostrar el menú. Solo la combinación de soluciones puede resolver mi problema.

public class CustomEditText extends EditText {

    public CustomEditText(Context context) {
        super(context);
        init();
    }

    public CustomEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public CustomEditText(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

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

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            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
        }
    }

    @Override
    public boolean isSuggestionsEnabled() {
        return false;
    }

    private class BlockedActionModeCallback implements ActionMode.Callback {
        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) {
        }
    }
}
Agua
fuente
5

Si no desea deshabilitar el clic largo porque necesita realizar alguna funcionalidad con un clic largo, entonces devolver verdadero es una mejor opción para hacerlo.

Su clic largo de edición de texto será así.

edittext.setOnLongClickListener(new View.OnLongClickListener() {
      @Override
      public boolean onLongClick(View v) {
            //  Do Something or Don't
            return true;
      }
});

Según la documentación, "True" indicará que se han manejado los clics largos, por lo que no es necesario realizar operaciones predeterminadas.

Probé esto en API nivel 16, 22 y 25. Funciona bien para mí. Espero que esto ayude.

muak
fuente
1
Bueno Alternativamente, solo configure android:longClickable="false"en XML
Alex Semeniuk
3

Aquí hay un truco para deshabilitar la ventana emergente "pegar". Tienes que anular el EditTextmétodo:

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

Similar puede hacerse para las otras acciones.

Anton Tananaev
fuente
puedes decir para el portapapeles deshabilitar
Richa
3

He probado esta solución y funciona

    mSubdomainEditText.setLongClickable(false);
    mSubdomainEditText.setCustomSelectionActionModeCallback(new ActionMode.Callback() {

      public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
        return false;
      }

      public void onDestroyActionMode(ActionMode mode) {
      }

      public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        return false;
      }

      public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
        return false;
      }
    });
Aakash Anuj
fuente
1

Lea el Portapapeles, verifique la entrada y el tiempo en que la entrada está "escrita". Si el Portapapeles tiene el mismo texto y es demasiado rápido, elimine la entrada pegada.

kadir
fuente
1

@Zain Ali, su respuesta funciona en la API 11. Solo quería sugerir una forma de hacerlo en la API 10 también. Como tenía que mantener mi API de proyecto en esa versión, estaba jugando constantemente con las funciones disponibles en 2.3.3 y tuve la posibilidad de hacerlo. He compartido el fragmento a continuación. Probé el código y estaba funcionando para mí. Hice este fragmento con urgencia. Siéntase libre de mejorar el código si hay algún cambio que pueda hacerse.

// A custom TouchListener is being implemented which will clear out the focus 
// and gain the focus for the EditText, in few milliseconds so the selection 
// will be cleared and hence the copy paste option wil not pop up.
// the respective EditText should be set with this listener 
// tmpEditText.setOnTouchListener(new MyTouchListener(tmpEditText, tmpImm));

public class MyTouchListener implements View.OnTouchListener {

    long click = 0;
    EditText mEtView;
    InputMethodManager imm;

    public MyTouchListener(EditText etView, InputMethodManager im) {
        mEtView = etView;
        imm = im;
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {

        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            long curr = System.currentTimeMillis();
            if (click !=0 && ( curr - click) < 30) {

                mEtView.setSelected(false);
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        mEtView.setSelected(true);
                        mEtView.requestFocusFromTouch();
                        imm.showSoftInput(mEtView, InputMethodManager.RESULT_SHOWN);
                    }
                },25);

            return true;
            }
            else {
                if (click == 0)
                    click = curr;
                else
                    click = 0;
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        mEtView.requestFocusFromTouch();
                        mEtView.requestFocusFromTouch();
                        imm.showSoftInput(mEtView, InputMethodManager.RESULT_SHOWN);
                    }
                },25);
            return true;
            }

        } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
            mEtView.setSelected(false);
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    mEtView.setSelected(true);
                    mEtView.requestFocusFromTouch();
                    mEtView.requestFocusFromTouch();
                    imm.showSoftInput(mEtView, InputMethodManager.RESULT_SHOWN);
                }
            },25);
            return true;
        }
        return false;
    }
Aravind
fuente
1

la solución es muy simple

public class MainActivity extends AppCompatActivity {

EditText et_0;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    et_0 = findViewById(R.id.et_0);

    et_0.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
        @Override
        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            //to keep the text selection capability available ( selection cursor)
            return true;
        }

        @Override
        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            //to prevent the menu from appearing
            menu.clear();
            return false;
        }

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

        @Override
        public void onDestroyActionMode(ActionMode mode) {

        }
    });
   }
}

--------> vista previa <---------

Muhammad Ali
fuente
1

Intente seguir la clase personalizada para copiar y pegar predominantemente Edittext

public class SegoeUiEditText extends AppCompatEditText {
private final Context context;


@Override
public boolean isSuggestionsEnabled() {
    return false;
}
public SegoeUiEditText(Context context) {
    super(context);
    this.context = context;
    init();
}

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

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


private void setFonts(Context context) {
    this.setTypeface(Typeface.createFromAsset(context.getAssets(), "Fonts/Helvetica-Normal.ttf"));
}

private void init() {

        setTextIsSelectable(false);
        this.setCustomSelectionActionModeCallback(new ActionModeCallbackInterceptor());
        this.setLongClickable(false);

}
@Override
public int getSelectionStart() {

    for (StackTraceElement element : Thread.currentThread().getStackTrace()) {
        if (element.getMethodName().equals("canPaste")) {
            return -1;
        }
    }
    return super.getSelectionStart();
}
/**
 * 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, android.view.ActionMode.Callback {
    private final String TAG = SegoeUiEditText.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) {}

    @Override
    public boolean onCreateActionMode(android.view.ActionMode mode, Menu menu) {
        return false;
    }

    @Override
    public boolean onPrepareActionMode(android.view.ActionMode mode, Menu menu) {
        menu.clear();
        return false;
    }

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

    @Override
    public void onDestroyActionMode(android.view.ActionMode mode) {

    }
}

}

Sagar Jethva
fuente
1

Para teléfonos inteligentes con portapapeles, es posible prevenir de esta manera.

editText.setFilters(new InputFilter[]{new InputFilter() {
        @Override
        public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
            if (source.length() > 1) {
                return "";
            }  return null;
        }
    }});
José Torres
fuente
0

Descubrí que cuando crea un filtro de entrada para evitar la entrada de caracteres no deseados, pegar dichos caracteres en el texto de edición no tiene ningún efecto. Así que esto también resuelve mi problema.

rDroid
fuente
0

Puede probar android: focusableInTouchMode = "false".

stdout
fuente
Nunca funcionará
Ragavendra M
0

La solución que funcionó para mí fue crear Edittext personalizado y anular el siguiente método:

public class MyEditText extends EditText {

private int mPreviousCursorPosition;

@Override
protected void onSelectionChanged(int selStart, int selEnd) {
    CharSequence text = getText();
    if (text != null) {
        if (selStart != selEnd) {
            setSelection(mPreviousCursorPosition, mPreviousCursorPosition);
            return;
        }
    }
    mPreviousCursorPosition = selStart;
    super.onSelectionChanged(selStart, selEnd);
}

}

Palejandro
fuente
0

Tratar de usar.

myEditext.setCursorVisible(false);

       myEditext.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;
        }
    });
Thambidurai
fuente
0

Quien está buscando una solución en Kotlin usa la siguiente clase como un widget personalizado y la usa en el xml.

clase SecureEditText: TextInputEditText {

/** 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 fun isSuggestionsEnabled(): Boolean {
    return false
}

override fun getSelectionStart(): Int {
    for (element in Thread.currentThread().stackTrace) {
        if (element.methodName == "canPaste") {
            return -1
        }
    }
    return super.getSelectionStart()
}

public override fun onSelectionChanged(start: Int, end: Int) {

    val text = text
    if (text != null) {
        if (start != text.length || end != text.length) {
            setSelection(text.length, text.length)
            return
        }
    }

    super.onSelectionChanged(start, end)
}

companion object {
    private val EDITTEXT_ATTRIBUTE_COPY_AND_PASTE = "isCopyPasteDisabled"
    private val PACKAGE_NAME = "http://schemas.android.com/apk/res-auto"
}

constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
    disableCopyAndPaste(context, attrs)
}

/**
 * Disable Copy and Paste functionality on EditText
 *
 * @param context Context object
 * @param attrs   AttributeSet Object
 */
private fun disableCopyAndPaste(context: Context, attrs: AttributeSet) {
    val isDisableCopyAndPaste = attrs.getAttributeBooleanValue(
        PACKAGE_NAME,
        EDITTEXT_ATTRIBUTE_COPY_AND_PASTE, true
    )
    if (isDisableCopyAndPaste && !isInEditMode()) {
        val inputMethodManager =
            context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        this.setLongClickable(false)
        this.setOnTouchListener(BlockContextMenuTouchListener(inputMethodManager))
    }
}

/**
 * Perform Focus Enabling Task to the widget with the help of handler object
 * with some delay
 * @param inputMethodManager is used to show the key board
 */
private fun performHandlerAction(inputMethodManager: InputMethodManager) {
    val postDelayedIntervalTime: Long = 25
    Handler().postDelayed(Runnable {
        this@SecureEditText.setSelected(true)
        this@SecureEditText.requestFocusFromTouch()
        inputMethodManager.showSoftInput(
            this@SecureEditText,
            InputMethodManager.RESULT_SHOWN
        )
    }, postDelayedIntervalTime)
}

/**
 * Class to Block Context Menu on double Tap
 * A custom TouchListener is being implemented which will clear out the focus
 * and gain the focus for the EditText, in few milliseconds so the selection
 * will be cleared and hence the copy paste option wil not pop up.
 * the respective EditText should be set with this listener
 *
 * @param inputMethodManager is used to show the key board
 */
private inner class BlockContextMenuTouchListener internal constructor(private val inputMethodManager: InputMethodManager) :
    View.OnTouchListener {
    private var lastTapTime: Long = 0
    val TIME_INTERVAL_BETWEEN_DOUBLE_TAP = 30
    override fun onTouch(v: View, event: MotionEvent): Boolean {
        if (event.getAction() === MotionEvent.ACTION_DOWN) {
            val currentTapTime = System.currentTimeMillis()
            if (lastTapTime != 0L && currentTapTime - lastTapTime < TIME_INTERVAL_BETWEEN_DOUBLE_TAP) {
                this@SecureEditText.setSelected(false)
                performHandlerAction(inputMethodManager)
                return true
            } else {
                if (lastTapTime == 0L) {
                    lastTapTime = currentTapTime
                } else {
                    lastTapTime = 0
                }
                performHandlerAction(inputMethodManager)
                return true
            }
        } else if (event.getAction() === MotionEvent.ACTION_MOVE) {
            this@SecureEditText.setSelected(false)
            performHandlerAction(inputMethodManager)
        }
        return false
    }
}

}

Dev
fuente
0

agregué la función de extensión en el idioma Kotlin :

fun EditText.disableTextSelection() {
    this.setCustomSelectionActionModeCallback(object : android.view.ActionMode.Callback {
        override fun onActionItemClicked(mode: android.view.ActionMode?, item: MenuItem?): Boolean {
            return false
        }
        override fun onCreateActionMode(mode: android.view.ActionMode?, menu: Menu?): Boolean {
            return false
        }
        override fun onPrepareActionMode(mode: android.view.ActionMode?, menu: Menu?): Boolean {
            return false
        }
        override fun onDestroyActionMode(mode: android.view.ActionMode?) {
        }
    })
}

puedes usarlo así:

edit_text.disableTextSelection()

también agregado debajo de la línea en su xml:

                android:longClickable="false"
                android:textIsSelectable="false"
Adnan Abdollah Zaki
fuente