34

我正在EditText为传统的蒙古语制作垂直。我已经通过EditText在一个旋转的ViewGroup. 我需要创建一个完全自定义的上下文菜单,因为系统不支持垂直文本,并且在旋转时也不ViewGroup旋转。所以我想完全禁用系统上下文菜单。

请注意,这与这些只是试图禁用复制/粘贴/等的问题不同:

虽然我没有在模拟器中看到上下文菜单,但我在我的 Android 5.0.2 小米手机中看到了它。

我努力了:

我对黑客持开放态度,但我需要它能够跨设备始终如一地工作。Mark Murphy(一个 Commons Guy)回复了另一个试图做类似事情的用户:

我怀疑即使你想出了一个答案,它也不会跨设备工作。设备制造商倾向于为 EditText 推出他们自己的“上下文菜单”,从而挫败了开发人员将项目添加到该上下文菜单中的尝试。我的猜测是,试图阻止该上下文菜单会产生类似的结果。

我运气不好?

我现在唯一能想到的就是完全重写TextViewEditText从头开始(嗯,通过修改Android源代码)。我认识其他人做过类似的事情,但他的代码不是开源的。在迈出这一重要步骤之前,我想尝试在 Stack Overflow 上寻求更简单的解决方案。

更新: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)仍然显示。这是一张图片:

在此处输入图像描述

在我运行 Android 5.0 的小米 MIUI 手机上,我在所有情况下(光标句柄单击、长按、双击)都能获得上下文菜单。

更新

Aritra Roy 的解决方案在模拟器、他测试过的其他一些设备以及我的设备上运行。我接受了他的回答,因为它解决了我原来的问题。唯一的负面影响是文本选择也被禁用​​。

4

7 回答 7

5

解决方案很简单

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

        }
    });
   }
}

在此处输入图像描述

于 2018-07-07T10:18:46.557 回答
4

我已经为 编写了这个代码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();
}
于 2017-01-23T05:35:14.873 回答
1

这就是阻止复制粘贴菜单以任何方式、形状或形式出现的方式。这个错误真的让我发疯了,就像任何三星错误一样,你知道它在他们的代码中,但你也知道他们不会很快修复它。无论如何,这里是奇迹墙......

  1. 检查 Android.Build.Model.toLowerCase().startsWith('sm-g930')。不要匹配整个字符串,最后一个字母是次要版本标识符。我将此布尔值存储在稍后出现的 shouldBlockCopyPaste 变量中。

  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);
      }
    }
于 2017-06-02T18:02:28.023 回答
1

这是一个难题。我花了好几个小时在我的Android Studio 3.4.2中研究和测试。

我建议3个步骤:

a)原始问题中的setCustomSelectionActionModeCallback “解决方案”但是当您单击红色下拉菜单时,它会一直显示选择句柄(光标下方的红色下拉菜单)和“剪贴板 + 全选”弹出窗口。

b)为选择句柄创建一个空图像。我创建了一个名为ic_empty.xmlunder的文件res/drawable

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

c)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
于 2019-08-05T19:52:30.620 回答
1
mEditText.setLongClickable(false);

这是禁用编辑文本的最简单方法。

于 2017-01-23T04:24:49.810 回答
0

我尝试了上面的所有答案,但没有得到完整的解决方案。如果你只想禁用 PASTE 选项,你可以试试这个:

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) {}
    }
}
 
于 2020-06-17T09:30:00.290 回答
-1

尝试这个

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

于 2017-01-23T07:56:34.733 回答