项目中有时会需要用到一个不可编辑的 EditText,用于展示,很奇怪的需求对吧,明明就应该使用 TextView 来做展示,使用 EditText 来做输入,但现实就是可能为了 UI 统一,又或者是其他乱七八糟的原因,就得这么实现。

比如在之前的『PassBox』和『HacppleStore』,我就是这么干的。

那么在 Android 中应如何将 EditText 设置为不可编辑状态呢?

我总结了几种方法:

android:editable

<EditText
    ...
    android:editable="false" />

效果如下:

Editable

可以看到,默认情况下,EditText 并没有获取到焦点,但点击 EditText 后,尽管焦点马上被激活,但输入法并没有弹出。

需要提醒的是,该属性已经被标记为 @Deprecated

<!-- If set, specifies that this TextView has an input method.
     It will be a textual one unless it has otherwise been specified.
     For TextView, this is false by default.  
     For EditText, it is true by default.
     {@deprecated Use inputType instead.} -->
<attr name="editable" format="boolean" />

虽然上面提到使用 android:inputType 来代替,但我暂时没找到该属性的替代方法。

setKeyListener()

在逻辑中使用:

editText.setKeyListener(false);

效果与 android:editable 相同,默认情况下没有获取到焦点,点击后焦点被激活,输入法没有弹出。

setKeyListener() 实际上是继承自 TextView 的一个方法,传入 null 即为禁止用户输入。

android:focusable / setFocusable()

在布局文件中:

<EditText
    ...
    android:focusable="false" />

或使用逻辑控制:

editText.setFocusable(false)                // boolean
editText.setFocusable(View.NOT_FOCUSABLE);  // int

效果如下:

Focusable

这其实是继承自 View 的一个属性,用于控制对应的 View 能否获取焦点。

可以看到,默认情况下,EditText 并没有获取到焦点,触摸时 EditText 获取到焦点,并在触摸结束后失去焦点,而通过长按可以将剪贴板的内容粘贴至 EditText 内。

需要注意的是,setFocusable() 做了方法重载,使用 int 类型参数则需要到 API 26 才支持。

android:focusableInTouchMode / setFocusableInTouchMode()

在布局文件中:

<EditText
    ...
    android:focusableInTouchMode="false" />

或使用逻辑控制:

editText.setFocusableInTouchMode(false);

该方法效果和 android:focusable 也一样,同样是继承自 View 的一个属性,用于控制对应的 View 在触摸时能否获取焦点。

android:enabled / setEnabled()

在布局文件中:

<EditText
    ...
    android:enabled="false" />

或使用逻辑控制:

editText.setEnabled(false);

效果如下:

Enabled

编辑框完全失去焦点,即无法通过粘贴的方法修改输入内容,但是整个编辑框变成了暗色状态,不易于阅读。

android:longClickable / setLongClickable()

为了可以清晰显示,并且不可以通过粘贴的方式编辑,我们可以考虑引入一些其他的解决方法。

通过粘贴方式编辑,实际操作过程是通过长按 EditText 以弹出对应的操作浮窗,才得以完成粘贴操作,那么我们就可以禁用长按操作,来避免用户进行粘贴。

在布局中:

<EditText
    ...
    android:focusable="false"
    android:longClickable="false" />

或使用逻辑控制:

editText.setFocusable(false);
editText.setLongClickable(false);

效果如下:

禁用长按

可以看到,文字能够清晰显示,长按 EditText 时也无法呼出操作浮窗,使用 android:focusablesetFocusable() 来搭配使用,因此同时也具备了相同的缺点,就是触摸时 EditText 仍然会获取焦点,但因为已经禁用了长按,所以不会产生功能上的问题。

如果还是不想存在焦点效果,添加个背景即可。

自定义 EditText

没有什么效果是自定义 View 解决不了的。

public class NonEditableEditText extends AppCompatEditText {

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

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return true;
    }
}

其实不需要多复杂,只需要重写 onTouchEvent() 方法即可,这里涉及到 View 的事件分发机制,简单来说,返回 true 代表当前 View 已经完整地处理了该事件,且不希望上层 ViewGroup 其他回调方法再次处理。

我们只需要在布局中使用该自定义控件:

<com.custom.widget.NonEditableEditText
    ... />

效果如下:

自定义 EditText 不响应触摸事件

可以看到,无论是点击还是长按,该自定义的 EditText 都不会获取焦点,因为它并不会对我们的触摸事件做任何处理。