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

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

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

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

Я пытался:

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

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

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

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

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

MVCE

Это самый простой способ воссоздать проблему. Там нет ничего необходимого из моего обычая EditText, Макет имеет единый EditText сделано путем замены проекта по умолчанию Hello World's TextView, Я изменил 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 я получаю контекстное меню во всех ситуациях (щелчок курсора, длительное нажатие, двойной щелчок).

Обновить

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

9 ответов

Решение

Есть три вещи, которые вам нужно сделать.

ШАГ 1

Вы можете отключить отображение контекстных меню, вернув false из этих методов,

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

ШАГ 2

Необходимо также отключить длинный щелчок в EditText.

mEditText.setLongClickable(false);

или делать это, android:longClickable="false" в XML.

ШАГ 3

Теперь вам нужно предотвратить появление меню при нажатии на ручки. Решение простое,

1) Расширить EditText учебный класс,

2) Переопределить isSuggestionsEnabled() и вернуться false,

3) Создать canPaste() метод и возврат false, Это метод сокрытия.

БЫСТРОЕ РЕШЕНИЕ

Если вы не хотите делать все это вручную. Вот пользовательский класс EditText, который вы можете использовать, чтобы сделать это быстро. Но я все же рекомендую вам пройти через шаги один раз, чтобы понять, как все работает.

public class MenuHidingEditText extends EditText {
    private final Context mContext;

    public MenuHidingEditText(Context context) {
        super(context);
        this.mContext = context;

        blockContextMenu();
    }

    public MenuHidingEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.mContext = context;

        blockContextMenu();
    }

    public MenuHidingEditText(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.mContext = context;

        blockContextMenu();
    }

    private void blockContextMenu() {
        this.setCustomSelectionActionModeCallback(new BlockedActionModeCallback());
        this.setLongClickable(false);
        this.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                MenuHidingEditText.this.clearFocus();
                return false;
            }
        });
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            // setInsertionDisabled when user touches the view
            this.setInsertionDisabled();
        }
        return super.onTouchEvent(event);
    }

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

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

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

        }
    });
   }
}

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

Я сделал этот код для 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();
}

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

Есть два контекстных меню

  1. customInsertionActionModeCallback — (вставить, выбрать все)
  2. customSelectionActionModeCallback — (вырезать, копировать, поделиться и т. д.)

Все решения направлены на удаление второго меню, но не того, которое запрашивает OP, которое является InsertionCallback, когда OP нажимает на большой палец управления курсором. Чтобы удалить оба обратных вызова, они оба должны быть переопределены.

Для SelectionCallback (вырезать, копировать, вставить)

      customSelectionActionModeCallback = object : ActionMode.Callback {
    override fun onCreateActionMode(ActionMode mode, Menu menu) {
        menu.clear()
        return true;
    }

   override fun onPrepareActionMode(ActionMode mode, Menu menu) {
        menu.clear();
        return false;
    }

   override fun onActionItemClicked(ActionMode mode, MenuItem item) {
        return false;
    }

    override fun onDestroyActionMode(ActionMode mode) {

    }

Аналогично для InsertionCallback (выбратьВсе, вставить)

          customInsertionActionModeCallback = object : ActionMode.Callback { 
    override fun onCreateActionMode(ActionMode mode, Menu menu) {
        menu.clear()
        return true;
    }

    override fun onPrepareActionMode(ActionMode mode, Menu menu) {
        menu.clear();
        return false;
    }

    override fun onActionItemClicked(ActionMode mode, MenuItem item) {
        return false;
    }

    override fun onDestroyActionMode(ActionMode mode) {

    }
mEditText.setLongClickable(false);

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

Таким образом вы блокируете отображение меню вставки копий любым способом, в любой форме или форме. Эта ошибка действительно свела меня с ума, и, как и любая другая ошибка 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);
      }
    }

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

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

а) setCustomSelectionActionModeCallback "решение" в первоначальном вопросе Но он держит показывая выберите ручки (красная капля под курсором) и всплывающее окно "Clipboard + Select All", при нажатии на красную каплю.

б) Создайте пустой образ для выбранных маркеров. Я создал файл под названием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

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

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

Попробуй это

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

Ссылка: /questions/44655709/edittext-otklyuchit-vsplyivayuschee-menyu-vstavit-zamenit-v-sobyitii-schelchka-obrabotchika-vyibora-teksta/44655734#44655734

Другие вопросы по тегам