web-dev-qa-db-fra.com

Mettre du texte constant dans EditText qui devrait être non éditable - Android

Je veux avoir un texte constant dans editText comme:

http://<here_user_can_write>

L'utilisateur ne devrait pas pouvoir supprimer les caractères de "http://", j'ai cherché à ce sujet et trouvé ceci:

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) : "";
        }
    }
}); 

mais je ne sais pas si cela empêche l'utilisateur de supprimer tous les caractères de start to end limit. Je ne pouvais pas non plus comprendre l'utilisation de la classe Spanned.

Une solution serait un bon choix si nous pouvons insérer une TextView dans EditText mais je ne pense pas que ce soit possible sous Android, car les deux sont des vues, est-ce possible?

55
Shrikant

Avez-vous essayé cette méthode? 

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

            }

        }
    });
114

Voilà comment vous pouvez le faire avec un 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;
        }
      }
    }
});

Si vous souhaitez également que le préfixe ne puisse pas être sélectionné, vous pouvez ajouter le code suivant.

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);
12
Michael

Il y avait un léger problème avec la réponse de @Rajitha Siriwardena . Elle suppose que toute la chaîne, à l'exception du suffixe, a été supprimée avant que le suffixe ait un sens si vous avez la chaîne.

http://stackoverflow.com/

et essayez de supprimer toute partie de http://, vous supprimerez stackoverflow.com/ pour obtenir uniquement http://.

J'ai également ajouté une vérification au cas où l'utilisateur essaie de saisir avant le préfixe.

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

Remarque: cela ne gère pas le cas où l'utilisateur tente de modifier le préfixe lui-même uniquement avant et après.

9

Vous avez eu presque raison, essayez

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;
            }
        }});
4
slezadav

CODE POUR AJOUTER UN PRÉFIXE PERSONNALISÉ À VOTRE EDITTEXT (PRÉFIXE NON ÉDITABLE)

Code de Medium par ALi Muzaffar

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

Et XML

<com.yourClassPath.PrefixEditText
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:gravity="bottom"
    Android:textSize="14sp"
    Android:tag="€ " />
4
Jack the Ripper

Tiré du blog de ALi Muzaffar, voir le message original pour plus de détails.

Utilisez la vue personnalisée EditText pour tracer le texte du préfixe et ajouter un remplissage en fonction de la taille du texte

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 Jadav

Je sais que je ressuscite un ancien message, mais je souhaite partager avec la communauté les difficultés que j'ai rencontrées avec ce sujet ces derniers jours et j’ai trouvé que le fait de placer une TextView sur une EditText est non seulement parfaitement faisable (pour répondre à la deuxième partie de la question), beaucoup plus dans ce cas où le texte constant est nécessaire dans la position de départ, mais préférable, aussi. De plus, le curseur ne bouge même pas avant le texte "mutable", ce qui est un effet élégant ... Je préfère cette solution car elle n’ajoute pas de charge de travail ni de complexité à mon application avec les auditeurs.

Voici un exemple de code de ma solution:

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

En encadrant les vues dans une RelativeLayout, elles seront superposées . L'astuce consiste à jouer avec la propriété Android:paddingStart de la EditText, pour que le texte commence juste après la TextView, tandis que les propriétés Android:layout_centerVertical="true" et Android:layout_marginStart="3dp" de la TextView s'assurent que son texte est correctement aligné avec le texte saisi et avec le début de la ligne sous-jacente de la variable EditText (ou du moins, cela se produit lors de l’utilisation de la matière à thème).

1
Cristina De Rito

Celui-ci consiste essentiellement à ajouter le préfixe "+91" au champ de saisie de votre numéro de téléphone.

1.Ajouter ce code à oncreate () of activity

@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.Déclarez une méthode appelée 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.dclarez "digits_number" dans votre fichier de valeurs

    <string name="digits_number">1234567890+</string>
1
Ankit Beura
    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
Mr.Nestorjava

Basé sur @ demaksee comment. J'étends EditText et remplace la fonction onSelectionChanged. Donc, même l'utilisateur ne peut pas éditer le préfixe. Très simple et utile. Kotlin:

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

Voici une solution moins efficace qui devrait gérer tous les cas de figure lorsque des caractères OR sont supprimés/insérés dans OR autour du préfixe.

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

Code:

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

Vous pouvez l'utiliser comme ça

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
Veedka

Le code ci-dessous fonctionne pour moi. Il gère les cas où l'utilisateur édite le préfixe, le supprime, insère du texte à partir du tampon, modifie le texte sélectionné. Si l'utilisateur change de préfixe, le focus se déplace vers la fin du préfixe.

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

Exemples:

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

http: localhost -> http: // localhost

0
Pavel Kataykin

Je viens de trouver la solution pour rendre le préfixe non modifiable et comment enregistrer du texte si vous essayez de supprimer le préfixe. C'est très proche de @Rajitha Siriwardena répondre. Tout ce que vous avez manqué, c'est de sauvegarder le texte avant d'appliquer les modifications. Il sera restauré dans afterTextChanged (...).

Code:

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