将常量文本放入EditText中,使其不能被编辑 - Android

88

我想让editText里的文本保持不变,类似于:

http://<here_user_can_write>

用户不应该能够从"http://"中删除任何字符,我搜索了相关信息并找到了这个:

用户不应该能够从"http://"中删除任何字符,我搜索了相关信息并找到了这个:

editText.setFilters(new InputFilter[] {
    new InputFilter() {
        public CharSequence filter(CharSequence src, int start,
            int end, Spanned dst, int dstart, int dend) {
            return src.length() < 1 ? dst.subSequence(dstart, dend) : "";
        }
    }
}); 
但我不知道它是否限制用户无法删除从开始到结束的任何字符。我也无法理解Spanned类的用途。 如果我们可以在EditText中放置一个TextView,那么一种方法是一个很好的选择,但我认为在Android中这是不可能的,因为两者都是Views,这是否可能?

请查看此答案:https://dev59.com/22Ij5IYBdhLWcg3w4Y2S#19789317 - Sunil Mishra
18个回答

170

你尝试过这种方法吗?

final EditText edt = (EditText) findViewById(R.id.editText1);

edt.setText("http://");
Selection.setSelection(edt.getText(), edt.getText().length());


edt.addTextChangedListener(new TextWatcher() {

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            // TODO Auto-generated method stub

        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count,
                int after) {
            // TODO Auto-generated method stub

        }

        @Override
        public void afterTextChanged(Editable s) {
            if(!s.toString().startsWith("http://")){
                edt.setText("http://");
                Selection.setSelection(edt.getText(), edt.getText().length());

            }

        }
    });

25
使用 startsWith 比使用 contains 更安全,可以避免用户将光标位置移动并在常量前键入任何内容。 - Ε Г И І И О
1
但是当我不移动手从删除按钮上删除时,文本就被删除了。我该怎么解决? - user1767260
我现在无法设置提示,请帮帮我。 - Aman Verma
2
此外,您可以扩展EditText并覆盖protected void onSelectionChanged(int selStart,int selEnd)方法,以此方式防止选择常量文本。 - demaksee
我有一个非常类似的需求,但我希望在用户输入文本时始终在后面添加文本“KG”。如何实现这一点,请帮忙。 - Rishikesh pathak

59

截至材料设计库的版本1.2.0-alpha01,支持为文本字段添加前缀和后缀:

<com.google.android.material.textfield.TextInputLayout
        app:prefixText="Price: "
        app:prefixTextAppearance="..."
        app:prefixTextColor="..."
        app:suffixText="Dollar"
        app:suffixTextColor="..."
        app:suffixTextAppearance="...">

    <com.google.android.material.textfield.TextInputEditText .../>

</com.google.android.material.textfield.TextInputLayout>
在我看来,唯一的缺点是后缀固定在文本字段的末尾,没有选项使其随输入文本流动。您可以在这个问题上投票以解决此问题。

这里的前缀不是固定在文本开头的。 - Konstantin Konopko
如果有人对前缀和编辑文本的对齐有问题,这个链接可能会有帮助:github链接 - SadeQ digitALLife
@KonstantinKonopko 在你的dimen.xml文件中添加以下代码:<dimen name="material_input_text_to_prefix_suffix_padding">0dp</dimen> - SadeQ digitALLife

16

以下是如何使用InputFilter来实现:

final String prefix = "http://"
editText.setText(prefix);

editText.setFilters(new InputFilter[] {
    new InputFilter() {
      @Override
      public CharSequence filter(final CharSequence source, final int start,
          final int end, final Spanned dest, final int dstart, final int dend) {
        final int newStart = Math.max(prefix.length(), dstart);
        final int newEnd = Math.max(prefix.length(), dend);
        if (newStart != dstart || newEnd != dend) {
          final SpannableStringBuilder builder = new SpannableStringBuilder(dest);
          builder.replace(newStart, newEnd, source);
          if (source instanceof Spanned) {
            TextUtils.copySpansFrom(
                (Spanned) source, 0, source.length(), null, builder, newStart);
          }
          Selection.setSelection(builder, newStart + source.length());
          return builder;
        } else {
          return null;
        }
      }
    }
});
如果您还希望前缀不能被选择,您可以添加以下代码。
final SpanWatcher watcher = new SpanWatcher() {
  @Override
  public void onSpanAdded(final Spannable text, final Object what,
      final int start, final int end) {
    // Nothing here.
  }

  @Override
  public void onSpanRemoved(final Spannable text, final Object what, 
      final int start, final int end) {
    // Nothing here.
  }

  @Override
  public void onSpanChanged(final Spannable text, final Object what, 
      final int ostart, final int oend, final int nstart, final int nend) {
    if (what == Selection.SELECTION_START) {
      if (nstart < prefix.length()) {
        final int end = Math.max(prefix.length(), Selection.getSelectionEnd(text));
        Selection.setSelection(text, prefix.length(), end);
      }
    } else if (what == Selection.SELECTION_END) {
      final int start = Math.max(prefix.length(), Selection.getSelectionEnd(text));
      final int end = Math.max(start, nstart);
      if (end != nstart) {
        Selection.setSelection(text, start, end);
      }
    }
  }
};

editText.getText().setSpan(watcher, 0, 0, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);

10

@Rajitha Siriwardena的答案存在一点小问题。它假设在后缀被删除之前,整个字符串除了后缀都已经被删除,这意味着如果您有字符串

http://stackoverflow.com/
如果您尝试删除任何http:// 的一部分,您将删除 stackoverflow.com/,只剩下http://。我还添加了一个检查以防用户在前缀之前输入。
 @Override
 public void afterTextChanged(Editable s) {
     String prefix = "http://";
     if (!s.toString().startsWith(prefix)) {
         String cleanString;
         String deletedPrefix = prefix.substring(0, prefix.length() - 1);
         if (s.toString().startsWith(deletedPrefix)) {
             cleanString = s.toString().replaceAll(deletedPrefix, "");
         } else {
             cleanString = s.toString().replaceAll(prefix, "");
         }
         editText.setText(prefix + cleanString);
         editText.setSelection(prefix.length());
    }
}

注意:这不处理用户尝试仅在前缀本身之前和之后编辑的情况。


7

这段内容摘自Ali Muzaffar的博客,欲了解更多细节请参考原始文章

使用自定义的EditText视图来绘制前缀文本,并根据前缀文本大小添加填充:

public class PrefixEditText extends EditText {

private String mPrefix = "$"; // add your prefix here for example $
private Rect mPrefixRect = new Rect(); // actual prefix size

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

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    getPaint().getTextBounds(mPrefix, 0, mPrefix.length(), mPrefixRect);
    mPrefixRect.right += getPaint().measureText(" "); // add some offset

    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawText(mPrefix, super.getCompoundPaddingLeft(), getBaseline(), getPaint());
}

@Override
public int getCompoundPaddingLeft() {
    return super.getCompoundPaddingLeft() + mPrefixRect.width();
}
}

3
嗨,Vishal,下次你从他人博客(或互联网的其他地方)复制代码时,请记得链接到来源。 - Marcel Bro

6

你几乎做对了,试试这样做

private final String PREFIX="http://";

editText.setFilters(new InputFilter[]{new InputFilter() {
            @Override
            public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int
                    dend) {
                return dstart<PREFIX.length()?dest.subSequence(dstart,dend):null;
            }
        }});

EditText字段停止接收任何输入,无法在其中键入任何内容。我认为此解决方案不再起作用了。 - Ali Obeid

5

给EditText添加自定义前缀(前缀不可编辑)的代码

来自Ali Muzaffar在Medium上的文章

public class PrefixEditText extends AppCompatEditText {
    float originalLeftPadding = -1;

    public PrefixEditText(Context context) {
        super(context);
    }

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

    public PrefixEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        calculatePrefix();
    }

    private void calculatePrefix() {
        if (originalLeftPadding == -1) {
            String prefix = (String) getTag();
            float[] widths = new float[prefix.length()];
            getPaint().getTextWidths(prefix, widths);
            float textWidth = 0;
            for (float w : widths) {
                textWidth += w;
            }
            originalLeftPadding = getCompoundPaddingLeft();
            setPadding((int) (textWidth + originalLeftPadding),
                getPaddingRight(), getPaddingTop(),
                getPaddingBottom());
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        String prefix = (String) getTag();
        canvas.drawText(prefix, originalLeftPadding, getLineBounds(0, null), getPaint());
    }
}

还有XML

<com.yourClassPath.PrefixEditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="bottom"
    android:textSize="14sp"
    android:tag="€ " />

请确保在您的xml文件中使用正确的类路径。 com.alimuzaffar.customwidgets.PrefixEditText - 如果您使用此类,您的应用程序将崩溃。 - Johnny Five
这在Nougat中无法工作,在其他操作系统中可以正常工作,您能告诉我如何在Nougat中解决这个问题吗? - Abdul Rizwan
你前面的答案指向了同一篇Medium文章。 - Sebastián Palma
@SebastianPalma 在我回答的时候,博客上还没有链接。这个回答已经被编辑过了。 - Jakub S.
你如何在这个EditText上设置文本?.setText()似乎无法工作。 - Sadda Hussain
如何更改标签(前缀值)的颜色? - Wini

4
一个易于使用的Kotlin扩展函数来实现此目的。
fun EditText.stickPrefix(prefix: String) {
    this.addTextChangedListener(afterTextChanged = {
        if (!it.toString().startsWith(prefix) && it?.isNotEmpty() == true) {
            this.setText(prefix + this.text)
            this.setSelection(this.length())

        }
    })
}
//someEditText.stickPrefix("+")

3
我知道我在回复一篇老帖子,但我想与社区分享这个话题,最近我一直在处理这个问题,我发现在EditText上放置一个TextView不仅完全可行(回答第二部分的问题),而且更可取,特别是当需要始终显示的文本时。此外,光标甚至不会移动到“可变”文本之前,这是一种优雅的效果。相比于添加监听器等其他解决方案,我更喜欢这种解决方案,因为它不会增加我的应用程序的工作量和复杂性。

以下是我的解决方案的示例代码:

<RelativeLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_marginStart="3dp"
        android:text="http://" />

    <EditText
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:inputType="textUri"
        android:paddingStart="choose an appropriate padding" />
</RelativeLayout>

通过将视图包含在RelativeLayout中,它们将重叠。 这里的诀窍是玩弄EditTextandroid:paddingStart属性,以使文本紧随TextView之后开始,同时使用android:layout_centerVertical="true"android:layout_marginStart="3dp"属性确保其文本与输入的文本以及EditText的底部线的开头正确对齐(或者至少在使用Material主题时会发生这种情况)。

3

我为添加前缀到 EditText 编写了 Kotlin 扩展函数。

fun EditText.addPrefix(prefix: String) {
        var text = ""
        var isPrefixModified = false
        val formattedPrefix = "$prefix "
        var lastCharSequence: CharSequence? = null

        val setEditText: () -> Unit = {
            setText(text)
            Selection.setSelection(editableText, text.length)
        }

        this.addTextChangedListener(object : TextWatcher {
            override fun afterTextChanged(editable: Editable?) {
                val newText = editable.toString()

                when {
                    isPrefixModified -> {
                        isPrefixModified = false
                        setEditText()
                    }
                    isTryingToDeletePrefix(newText) -> {
                        setEditText()
                    }
                    isNewInput(newText) -> {
                        text = "$formattedPrefix$newText"
                        setEditText()
                    }
                    else -> {
                        text = newText
                    }
                }
            }

            override fun beforeTextChanged(charSequence: CharSequence?, start: Int,
                                           count: Int, after: Int) {
                charSequence?.let {
                    if (it != lastCharSequence && it.isNotEmpty() && start <= prefix.length) {
                        isPrefixModified = true
                    }
                    lastCharSequence = charSequence
                }
            }

            override fun onTextChanged(charSequence: CharSequence?, start: Int,
                                       before: Int, count: Int) {
                // Ignore
            }

            private fun isTryingToDeletePrefix(newText: String) =
                    text.isNotEmpty() && newText.length < text.length && isNewInput(newText)

            private fun isNewInput(newText: String) = !newText.contains(formattedPrefix)
        })
    }

有没有任何方法可以更新前缀?我想在用户执行某些任务后进行更改。 - PriyankVadariya
我认为你可以尝试删除旧的和新的,只需对代码进行一些调整。 - Antonis Radz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接