Отключить контекстное меню EditText

Я делаю вертикаль EditText для традиционного монгольского. Я успешно реализовал это, вставив слегка измененный EditText внутрь повернутого ViewGroup. Мне нужно создать полностью настраиваемое контекстное меню, потому что системное не поддерживает вертикальный текст, а также не поворачивается при повороте ViewGroup. Поэтому я хочу вообще отключить системное контекстное меню.

Обратите внимание, что это отличается от этих вопросов, которые просто пытаются отключить копирование / вставку и т. Д .:

Хотя в симуляторе не появляется контекстное меню, оно появляется в моем телефоне Xiaomi с Android 5.0.2.

Я пытался:

Я открыт для взлома, но мне нужно, чтобы он работал на всех устройствах. Марк Мерфи (сотрудник Commons) написал некоторое время назад в ответ другому пользователю, пытающемуся сделать что-то подобное:

Я подозреваю, что даже если вы дадите ответ, он не будет работать на всех устройствах. Производители устройств имели тенденцию создавать собственное «контекстное меню» для EditText, отбивая попытки разработчиков добавить элементы в это контекстное меню. Я предполагаю, что попытка заблокировать это контекстное меню будет иметь аналогичные результаты.

Мне не повезло?

Единственное, о чем я могу сейчас думать, - это полностью переписать TextView и EditText с нуля (ну, изменив исходный код Android). Я знаю кого-то еще, кто делал что-то подобное, но его код не является открытым исходным кодом. Прежде чем я сделаю этот важный шаг, я хочу попробовать попросить более простое решение здесь, в Stack Overflow.

Обновление. Я пытался изменить TextView исходный код в течение последних двух дней, и это похоже на шестимесячный проект. Это масса взаимосвязанных классов. Мне нужно другое решение, но у меня нет идей.

MVCE

Это самый простой способ воссоздать проблему. От моего кастома EditText нет ничего нужного. В макете есть единственный EditText, созданный путем замены TextView проекта Hello World по умолчанию. Я изменил min API на 11, чтобы избежать использования устаревших методов.

public class MainActivity extends AppCompatActivity {

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

        EditText editText = (EditText) findViewById(R.id.edit_text);
        editText.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
            @Override
            public boolean onCreateActionMode(ActionMode actionMode, Menu menu) { return false; }
            @Override
            public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) { return false; }
            @Override
            public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) { return false; }
            @Override
            public void onDestroyActionMode(ActionMode actionMode) { }
        });
    }
}

Контекстное меню в симуляторе (с запущенным API 24) все еще отображается, когда я нажимаю на дескриптор курсора (но не при долгом или двойном щелчке). Вот изображение:

введите описание изображения здесь

На моем телефоне Xiaomi MIUI под управлением Android 5.0 я получаю контекстное меню во всех ситуациях (щелчок маркера курсора, долгий щелчок, двойной щелчок).

Обновлять

Решение Аритры Роя работает в симуляторе, на некоторых других устройствах, которые он тестировал, и на моем устройстве. Я принял его ответ, потому что он решает мою первоначальную проблему. Единственный отрицательный побочный эффект - также отключено выделение текста.


person Suragch    schedule 16.01.2017    source источник
comment
Хотя, вероятно, нет необходимости отвечать на вопрос, мой собственный код EditText - здесь, на GitHub.   -  person Suragch    schedule 18.01.2017


Ответы (7)


Я сделал этот код для EditText, и он отлично справился с такой проблемой.

try {
    edtName.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            edtName.setSelection(0);
        }
    });
    edtName.setOnLongClickListener(new View.OnLongClickListener() {
        @Override
        public boolean onLongClick(View v) {
            return true;
        }
    });
    edtName.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
        @Override
        public boolean onCreateActionMode(ActionMode actionMode, Menu menu) { return false; }
        @Override
        public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) { return false; }
        @Override
        public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) { return false; }
        @Override
        public void onDestroyActionMode(ActionMode actionMode) { }
    });
} catch (Exception e) {
    e.printStackTrace();
}
person Rjz Satvara    schedule 23.01.2017
comment
Я протестировал ваше решение, заменив свой код вашим в моем MVCE выше. На моем устройстве Xiaomi MIUI Android 5.0 это отключает контекстное меню только при долгом нажатии. Меню по-прежнему появляется при двойном щелчке и нажатии на ручку курсора. Я не могу ожидать, что вы решите проблему для моего конкретного устройства. Я просто даю вам знать, что он там есть. Однако в эмуляторе Android Studio с API 24 я все еще получаю меню, которое отображается, когда я щелкаю дескриптор курсора. Я добавил изображение к моему ответу выше, которое показывает, что это происходит. - person Suragch; 23.01.2017

решение очень простое

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

        }
    });
   }
}

введите описание изображения здесь

person Muhammad Ali    schedule 07.07.2018
comment
Я еще не тестировал это, но похоже, что разница в вашем ответе в том, что вы звоните menu.clear(). Что насчет того, когда вы щелкаете маркеры курсора, долгое или двойное нажатие? Меню появляется? - person Suragch; 07.07.2018
comment
@Suragch протестируйте это и дайте мне знать. - person Muhammad Ali; 07.07.2018
comment
Работает. Спасибо! - person DmitryKanunnikoff; 08.11.2018

mEditText.setLongClickable(false);

Это самый простой способ отключить текст редактирования.

person udayatom    schedule 23.01.2017
comment
Это может отключить контекстное меню при долгом нажатии, но не отключит его при двойном касании или одиночном щелчке курсора. - person Suragch; 23.01.2017

Таким образом вы блокируете отображение меню копирования и вставки в любом виде или форме. Эта ошибка действительно свела меня с ума, и, как и в случае с любой другой ошибкой Samsung, вы знаете, что она есть в их коде, но вы также знаете, что они не исправят ее в ближайшее время. В любом случае, вот чудо-стена ...

  1. Убедитесь, что Android.Build.Model.toLowerCase (). Начинается с ('sm-g930'). Не совпадать со всей строкой, последняя буква - это дополнительный идентификатор версии. Я сохранил это логическое значение в переменной shouldBlockCopyPaste, которая появится позже.

  2. Если он совпадает, вы хотите заблокировать отображение меню копирования и вставки. Вот как вы НА САМОМ ДЕЛЕ ДЕЛАЕТЕ !!!

Переопределите эти 2 функции, вы заметите мое логическое значение shouldBlockCopyPaste, это значит, что другие устройства не блокируются.

   @Override
   public ActionMode StartActionMode (ActionMode.Callback callback){
      if (shouldBlockCopyPaste) {
        return null;
      } else {
        return super.StartActionMode(callback);
      }
    }

   @Override
   public ActionMode StartActionMode (ActionMode.Callback callback, int type){
      if (shouldBlockCopyPaste) {
        return null;
      } else {
        return super.StartActionMode(callback, type);
      }
    }
person self.name    schedule 02.06.2017
comment
это сделало это для меня! - person Alberto M; 19.07.2021

Это сложная проблема. Я провел часы и часы, исследуя и тестируя свою Android Studio 3.4.2.

Предлагаю 3 шага:

a) setCustomSelectionActionModeCallback "решение" в исходном вопросе. Но он продолжает показывать маркеры выбора ( красная капля под курсором) и всплывающее окно «Буфер обмена + Выбрать все» при нажатии на красную каплю.

б) Создайте пустое изображение для выбранных ручек. Я создал файл с именем ic_empty.xml в папке res/drawable.

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
</shape>

в) Я создал стиль в style.xml для всех EditTexts.

По основной теме

 <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        ...
        <item name="android:editTextStyle">@style/TStyle</item>
        ....
 </style>

Таким образом, вы можете определить свой стиль, связывая пустое изображение для левого, среднего и правого маркеров выбора:

   <style name="TStyle" parent="@android:style/Widget.EditText" >
        <item name="android:textSelectHandle">@drawable/ic_empty</item>
        <item name="android:textSelectHandleLeft">@drawable/ic_empty</item>
        <item name="android:textSelectHandleRight">@drawable/ic_empty</item>
    </style>

Если цель - от API 23, можно использовать setTextAppearance для присоединения стиля к тексту внутри EditText. Однако вышеуказанное решение работает всегда

Единственная оставшаяся проблема - я могу избавиться от эффекта двойного щелчка. Он выделяет слово в тексте на розовом фоне. Однако это относительно безвредно, но неудобно, поскольку не требует взаимодействия с пользователем.

Уловка, которую можно проделать, - это установить прозрачный цвет выделения.

EditT.setHighlightColor(Color.TRANSPARENT)  // EditT is a EditText
person Paulo Buchsbaum    schedule 05.08.2019

Я попробовал все приведенные выше ответы, но не получил полного решения. Если вы хотите отключить только опцию ВСТАВИТЬ, вы можете попробовать следующее:

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

Это просто взлом, но лучше не нашел.

если вы хотите полностью отключить меню и курсор, вы можете попробовать следующий класс вместо вашего EditText:

class MaskedCodeEditTextView : EditText {
    constructor(context: Context) : super(context) {
        init()
        blockContextMenu()
    }

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
        init()
        blockContextMenu()
    }

    constructor(context: Context, attrs: AttributeSet?, defStyle: Int) : super(
        context,
        attrs,
        defStyle
    ) {
        init()
        blockContextMenu()
    }

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

    private fun setInsertionDisabled() {
        try {
            val editorField = TextView::class.java.getDeclaredField("mEditor")
            editorField.isAccessible = true
            val editorObject = editorField[this]
            val editorClass = Class.forName("android.widget.Editor")
            val mInsertionControllerEnabledField =
                editorClass.getDeclaredField("mInsertionControllerEnabled")
            mInsertionControllerEnabledField.isAccessible = true
            mInsertionControllerEnabledField[editorObject] = false
        } catch (ignored: Exception) {
            // ignore exception here
        }
    }

    private fun blockContextMenu() {
        this.customSelectionActionModeCallback = ActionModeCallbackInterceptor()
        this.isLongClickable = false
        setOnClickListener { v: View? ->
            v?.let {
                if(!it.isFocused) {
                    requestFocus()
                } else {
                    clearFocus()
                    requestFocus()
                }
            }
        }
    }

    override fun isSuggestionsEnabled(): Boolean {
        return false
    }

    private fun init() {
        this.customSelectionActionModeCallback = ActionModeCallbackInterceptor()
        this.isLongClickable = false
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        if (event.action == MotionEvent.ACTION_DOWN) {
            setInsertionDisabled()
        }
        return super.onTouchEvent(event)
    }

    private inner class ActionModeCallbackInterceptor : 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) {}
    }
}
 
person Aleksey Kislyakov    schedule 17.06.2020

попробуй это

mEditText.setClickable(false);
mEditText.setEnabled(false);

ОБНОВЛЕНИЕ

Попробуйте это решение, расширив Edittext,

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

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

Ссылка: https://stackoverflow.com/a/28893714/5870896

person Rashid    schedule 23.01.2017
comment
Если EditText не активен и не активен, то для всех практических целей он перестает быть EditText. По сути, он просто становится TextView. Мне все еще нужно ввести и выделить текст. Я просто не хочу, чтобы отображалось системное контекстное меню. - person Suragch; 23.01.2017
comment
@ Сурагч вл. Я думал, что это edittext, потому что в вашем коде это edittext - person Rashid; 23.01.2017
comment
Это еще EditText. Я просто имею в виду, что ваше решение (которое фактически отключает контекстное меню) имеет нежелательные побочные эффекты, заключающиеся в том, что пользователю не разрешается вводить или выбирать текст. - person Suragch; 23.01.2017
comment
Ой, извините, в моей голове было текстовое представление, но я все еще набирал edittext - person Rashid; 23.01.2017
comment
Ваше обновление выглядит так же, как и код в моем вопросе. Есть разница? - person Suragch; 23.01.2017
comment
@Suragch проверь это еще раз - person Rashid; 23.01.2017
comment
Я проверил еще раз. Когда вы тестировали его в эмуляторе, у вас не появлялось всплывающее меню после того, как вы щелкнули ручкой курсора (как я показал на изображении в моем вопросе)? - person Suragch; 23.01.2017