将常量文本放入不应编辑的EditText内-Android


70

我想在editText中包含常量文本,例如:

http://<here_user_can_write>

用户应该无法删除“ 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类的用法。

如果我们可以放一个TextView内部,一种方法将是一个不错的选择,EditText但是我认为这在Android中是不可能的,因为两者都是Views,是否可以?


Answers:


155

您尝试过这种方法吗?

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,比使用更加安全,以避免用户移动光标位置并在常量之前输入任何内容。
ΕГИІИО

但是如果在不移动删除按钮的情况下删除它,则文本将被删除。如何解决?
user1767260

我现在无法设置提示。.帮助我
阿曼·维尔玛

2
您也可以扩展EditText并覆盖受保护的void onSelectionChanged(int selStart,int selEnd)方法,这样就可以防止选择常量文本
demaksee

我有非常相似的要求,但我总是希望在用户输入文本时返回文本“ KG”。如何实现此功能。
瑞诗凯诗pathak '17

20

随着版本的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>

我认为唯一的缺点是后缀固定在文本字段的末尾,并且不随输入文本一起流动。您可以为此投票。


14

这就是您实际上可以使用的方式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);

9

@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());
    }
}

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


6

取自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();
}
}

1
嗨,Vishal,下次您从某人的博客(或互联网上的任何地方)复制代码时,请记住链接至源。
马塞尔兄弟

好吧
维沙尔·

4

您几乎正确,尝试

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字段停止接收任何输入。不能在其中输入任何内容。我相信此解决方案不再起作用。
阿里·奥贝德

4

将自定义前缀添加到您的编辑文本中的代码(不可编辑前缀)

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-如果使用此选项,则应用程序将崩溃。
约翰尼五世

在Nougat中不能正常工作,在其他操作系统中也可以正常工作,请让我知道,我们如何在Nougat中解决此问题?
阿卜杜勒·里兹万

您之前写的答案指向同一中号帖子。
塞巴斯蒂安·帕尔玛

在我写我的答案时,@ SebastianPalma没有指向博客的链接。答案已被编辑。
PsychedelicSubstance19年

您将如何在此edittext上设置文本?.setText()似乎不起作用。
Sadda Hussain

2

我知道我正在恢复一个旧帖子,但是我想与这些天在这个主题上苦苦挣扎的社区分享一下,我发现放置一个 TextView放在上方EditText不仅是完全可行的(以回答问题的第二部分) ,在这种情况下,如果在起始位置需要使用常量文本,则更多,但最好也是如此。此外,光标甚至根本不会移动到“可变”文本之前,这是一种优雅的效果。我更喜欢这种解决方案,因为它不会给使用侦听器等的应用程序增加工作量和复杂性。

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

<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它们将被重叠。这里的窍门是使用的android:paddingStart属性EditText,以使文本紧接在TextView,之后,android:layout_centerVertical="true"并确保android:layout_marginStart="3dp"属性TextView以确保其文本与输入的文本以及的基础行的开头正确对齐EditText(或在至少在使用以材料为主题的主题时会发生这种情况)。


1

这基本上是在电话号码的编辑文本字段中添加前缀“ +91”。

1.将此代码添加到活动的oncreate()

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

   // Write other things......//

   etPhoneNumber.setFilters(new InputFilter[]{getPhoneFilter(),newInputFilter.LengthFilter(13)});

    etPhoneNumber.setOnFocusChangeListener(new View.OnFocusChangeListener() {
        @Override
        public void onFocusChange(View v, boolean hasFocus) {
            if (hasFocus) {
                if (etPhoneNumber.getText().toString().isEmpty()) {
                    etPhoneNumber.setText("+91");
                    Selection.setSelection(etPhoneNumber.getText(), etPhoneNumber.getText().length());                    }
            } else {
                if (etPhoneNumber.getText().toString().equalsIgnoreCase("+91")) {
                    etPhoneNumber.setFilters(new InputFilter[]{});
                    etPhoneNumber.setText("");
                    etPhoneNumber.setFilters(new InputFilter[]{getPhoneFilter(),new InputFilter.LengthFilter(13)});

                }
            }
        }
    });
}

2.声明一个名为getPhoneFilter()的方法

    private InputFilter getPhoneFilter() {

    Selection.setSelection(etPhoneNumber.getText(), etPhoneNumber.getText().length());

    etPhoneNumber.addTextChangedListener(new TextWatcher() {

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count,
                                      int after) {
        }

        @Override
        public void afterTextChanged(Editable s) {
            if(!s.toString().startsWith("+91")){
                if (etPhoneNumber.getFilters() != null && etPhoneNumber.getFilters().length > 0) {
                    etPhoneNumber.setText("+91");
                    Selection.setSelection(etPhoneNumber.getText(), etPhoneNumber.getText().length());
                }
            }
        }
    });

     // Input filter to restrict user to enter only digits..
    InputFilter filter = new InputFilter() {

        public CharSequence filter(CharSequence source, int start, int end,
                                   Spanned dest, int dstart, int dend) {

            for (int i = start; i < end; i++) {

                if (!String.valueOf(getString(R.string.digits_number)).contains(String.valueOf(source.charAt(i)))) {
                    return "";
                }
            }
            return null;
        }
    };
    return filter;
}

3.在值文件中声明“ digits_number”

    <string name="digits_number">1234567890+</string>

1

基于@demaksee注释。我扩展了EditText并覆盖了onSelectionChanged函数。因此,用户甚至无法编辑前缀。非常简单实用。科特林:

private var prefix : String? = ""

override fun onSelectionChanged(selStart: Int, selEnd: Int) {
    if (prefix != null && prefix!!.isNotBlank()) {
        var finalStart = selStart
        var finalEnd = selEnd

        val prefixLength = prefix!!.length
        if (prefixLength > selStart) {
            finalStart = prefixLength
        }

        if (prefixLength > selEnd) {
            finalEnd = prefixLength
        }

        if (finalStart == selStart && finalEnd == selEnd) {
            super.onSelectionChanged(finalStart, finalEnd)
        } else {
            val startWithPrefix = text?.startsWith(prefix ?: "") ?: prefix.isNullOrBlank()
            if (!startWithPrefix) {
                setText(prefix)
            }

            setSelection(finalStart, finalEnd)
        }
    }
}

public fun setPrefix(prefix: String) {
    editText.setText(prefix)
    editText.setSelection(prefix.length)
    this.prefix = prefix
}

1

我做了Kotlin扩展功能来添加前缀 EditText

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

1

为此目的,易于使用的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("+")

0

这是一个效率较低的解决方案,应该处理所有字符或单词在前缀周围的OR中删除/插入的情况。

prefix = "http://"
extra = "ahhttp://"
differencePrefix(prefix, extra) = "aht"

码:

public static String differencePrefix(String prefix, String extra) {
    if (extra.length() < prefix.length()) return "";
    StringBuilder sb = new StringBuilder();
    StringBuilder eb = new StringBuilder();
    int p = 0;
    for (int i = 0; i < extra.length(); i++) {
        if (i >= prefix.length()) {
            while(p < extra.length()) {
                eb.append(extra.charAt(p));
                p++;
            }
            break;
        }
        if (p >= extra.length()) break;
        char pchar = extra.charAt(p);
        char ichar = prefix.charAt(i);
        while(pchar != ichar) {
            //check if char was deleted
            int c = i + 1;
            if (c < prefix.length()) {
                char cchar = prefix.charAt(c);
                if (cchar == pchar) {
                    break;
                }
            }
            sb.append(pchar);
            p++;
            if (p >= extra.length()) break;
            pchar = extra.charAt(p);
        }
        p++;
    }

    return eb.toString() + sb.toString();
}

你可以这样使用

editText.addTextChangedListener(new TextWatcher() {
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {

    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {

    }

    @Override
    public void afterTextChanged(Editable s) {
        String input = s.toString();
        if (!input.startsWith(prefix)) {
            String extra = differencePrefix(prefix, input);
            String newInput = prefix + extra;
            editText.setText(newInput);
            editText.setSelection(newInput.length());
        }
    }
});

0

我刚刚找到了解决方案,该方法如何使前缀不可编辑,以及在尝试删除前缀时如何保存文本。这与@Rajitha Siriwardena的答案非常接近。您所错过的就是在应用任何更改之前保存文本。它将在afterTextChanged(...)中恢复。

码:

final String prefix = "http://";
editText.setText(prefix);
Selection.setSelection(editText.getText(), editText.getText().length());

editText.addTextChangedListener(new TextWatcher() {
    String text;
    @Override
    public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
        text = charSequence.toString();
    }

    @Override
    public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
    }

    @Override
    public void afterTextChanged(Editable editable) {
        if (!editable.toString().startsWith(prefix)) {
            editText.setText(text);
            Selection.setSelection(editText.getText(), editText.getText().length());
        }
    }
});

0
    EditText msg=new EditText(getContext());
                msg.setSingleLine(true);
                msg.setSingleLine();
                msg.setId(View.generateViewId());
                msg.measure(0,0);



                TextView count=new TextView(getContext());
                count.setTextColor(Color.parseColor("#666666"));
                count.setText("20");
                count.setPadding(0,0,(int)Abstract.getDIP(getContext(),10),0);
                count.measure(0,0);
float tenPIX =TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,10,getContext().getResources().getDisplayMetrics());

 msg.setPadding((int)tenPIX,(int)tenPIX,(int)(int)tenPIX+count.getMeasuredWidth(),(int)tenPIX);


                RelativeLayout ll1=new RelativeLayout(getContext());
                ll1.addView(msg,new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));

LayoutParams countlp=new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
                countlp.addRule(RelativeLayout.ALIGN_END,msg.getId());
                countlp.addRule(RelativeLayout.ALIGN_BASELINE,msg.getId());
                ll1.addView(count,countlp);

0

下面的代码对我有用。它可以处理以下情况:用户编辑前缀,删除前缀,从缓冲区插入文本,更改选定的文本。如果用户更改了前缀,则焦点将移到前缀的末尾。

    final String prefix = "http://";
    final String[] aLastText = {prefix};
    et.setText(prefix);
    et.addTextChangedListener(new TextWatcher() {
        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {}
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
        @Override
        public void afterTextChanged(Editable sNew) {

            if (!sNew.toString().startsWith(prefix)) {
                String sLast = aLastText[0];
                boolean isRemoving = sNew.length() < sLast.length();

                int start;
                int end = sNew.length() - 1;
                for (start = 0; start < sLast.length() && start < sNew.length(); start++) {
                    if (sLast.charAt(start) != sNew.charAt(start)) 
                        break;
                }
                int k = sLast.length() - 1;
                for (; end >= start && k >= 0; end--, k--) {
                    if (sLast.charAt(k) != sNew.charAt(end)) 
                        break;
                }
                String sEdited = sNew.toString().substring(start, ++end);
                k += isRemoving ? 1 : 0;
                k = k < prefix.length() ? prefix.length() : k;
                String sSuffix = sLast.substring(k, sLast.length());

                et.setText(prefix + sEdited + sSuffix);
                et.setSelection(et.getText().length() - sSuffix.length());
            }
            aLastText[0] = et.getText().toString();
        }
    });

例子:

ht5tp:// localhost,5http:// localhost,http:/ 5 / localhost-> http:// 5localhost

http:本地主机-> http://本地主机

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.