web-dev-qa-db-fra.com

Création d'un editText personnalisé avec une fonctionnalité de type balise

J'ai cherché autour mais je n'ai pas pu trouver de réponses à cela. Ce que j'essaye d'implémenter est un EditText similaire au champ 'To' trouvé dans l'écran de composition de l'application gmail ICS.

Voici une image décrivant ce que je veux: enter image description here

J'envisage d'étendre EditText et d'implémenter ma propre classe EditText personnalisée, mais je ne sais pas vraiment comment le faire ou même si c'est la meilleure solution. Des pensées?

29
bill-x

Hm, a mis du temps à trouver une question similaire mais néanmoins, voici la réponse la plus proche que j'ai trouvée . Je savais que d'autres personnes avaient ce genre de problème auparavant! Merci à CommonsWare de m'avoir indiqué dans la bonne direction.

7
bill-x

Adapté la solution de cette réponse . Sépare l'entrée automatiquement lors de l'insertion d'une virgule (le séparateur peut être ajusté). Crée un ImageSpan et un ClickableSpan (les entrées peuvent être supprimées en cliquant sur la partie droite).

public class TagEditText extends EditText {

    TextWatcher textWatcher;

    String lastString;

    String separator = ",";

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


    private void init() {
        setMovementMethod(LinkMovementMethod.getInstance());

        textWatcher = 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 thisString = s.toString();
                if (thisString.length() > 0 && !thisString.equals(lastString)) {
                    format();

                }
            }
        };

        addTextChangedListener(textWatcher);
    }


    private void format() {

        SpannableStringBuilder sb = new SpannableStringBuilder();
        String fullString = getText().toString();

        String[] strings = fullString.split(separator);


        for (int i = 0; i < strings.length; i++) {

            String string = strings[i];
            sb.append(string);

            if (fullString.charAt(fullString.length() - 1) != separator.charAt(0) && i == strings.length - 1) {
                break;
            }

            BitmapDrawable bd = (BitmapDrawable) convertViewToDrawable(createTokenView(string));
            bd.setBounds(0, 0, bd.getIntrinsicWidth(), bd.getIntrinsicHeight());

            int startIdx = sb.length() - (string.length());
            int endIdx = sb.length();

            sb.setSpan(new ImageSpan(bd), startIdx, endIdx, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

            MyClickableSpan myClickableSpan = new MyClickableSpan(startIdx, endIdx);
            sb.setSpan(myClickableSpan, Math.max(endIdx-2, startIdx), endIdx, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

            if (i < strings.length - 1) {
                sb.append(separator);
            } else if (fullString.charAt(fullString.length() - 1) == separator.charAt(0)) {
                sb.append(separator);
            }
        }


        lastString = sb.toString();

        setText(sb);
        setSelection(sb.length());

    }

    public View createTokenView(String text) {


        LinearLayout l = new LinearLayout(getContext());
        l.setOrientation(LinearLayout.HORIZONTAL);
        l.setBackgroundResource(R.drawable.bordered_rectangle_rounded_corners);

        TextView tv = new TextView(getContext());
        l.addView(tv);
        tv.setText(text);
        tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);

        ImageView im = new ImageView(getContext());
        l.addView(im);
        im.setImageResource(R.drawable.ic_cross_15dp);
        im.setScaleType(ImageView.ScaleType.FIT_CENTER);

        return l;
    }

    public Object convertViewToDrawable(View view) {
        int spec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        view.measure(spec, spec);
        view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());

        Bitmap b = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);

        Canvas c = new Canvas(b);

        c.translate(-view.getScrollX(), -view.getScrollY());
        view.draw(c);
        view.setDrawingCacheEnabled(true);
        Bitmap cacheBmp = view.getDrawingCache();
        Bitmap viewBmp = cacheBmp.copy(Bitmap.Config.ARGB_8888, true);
        view.destroyDrawingCache();
        return new BitmapDrawable(getContext().getResources(), viewBmp);
    }

    private class MyClickableSpan extends ClickableSpan{

        int startIdx;
        int endIdx;

        public MyClickableSpan(int startIdx, int endIdx) {
            super();
            this.startIdx = startIdx;
            this.endIdx = endIdx;
        }

        @Override
        public void onClick(View widget) {



            String s = getText().toString();

            String s1 = s.substring(0, startIdx);
            String s2 = s.substring(Math.min(endIdx+1, s.length()-1), s.length() );

            TagEditText.this.setText(s1 + s2);
        }

    }
}

R.drawable.bordered_rectangle_rounded_corners:

<shape xmlns:Android="http://schemas.Android.com/apk/res/Android">
    <solid
        Android:color="@color/transparent"/>
    <stroke Android:width="1dp" Android:color="#AAAAAA" />
    <corners
        Android:radius="100dp" />
    <padding
        Android:left="5dp"
        Android:top="5dp"
        Android:right="5dp"
        Android:bottom="5dp" />
</shape>

La dernière chose à ajouter est le png pour le "x-Button". Fonctionne bien jusqu'à présent, le seul problème est que le fait d'appuyer longuement sur la touche de suppression ne fonctionne pas (si quelqu'un a une idée de comment le faire fonctionner, n'hésitez pas à commenter)

8
AljoSt

Je ne pouvais pas trouver une bonne solution, donc je construirais ma propre bibliothèque pour gérer cela: TokenAutoComplete . Voici un exemple de base:

public class ContactsCompletionView extends TokenCompleteTextView {
    public ContactsCompletionView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected View getViewForObject(Object object) {
        Person p = (Person)object;

        LayoutInflater l = (LayoutInflater)getContext().getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        LinearLayout view = (LinearLayout)l.inflate(R.layout.contact_token, (ViewGroup)ContactsCompletionView.this.getParent(), false);
        ((TextView)view.findViewById(R.id.name)).setText(p.getName());

        return view;
    }

    @Override
    protected Object defaultObject(String completionText) {
        //Stupid simple example of guessing if we have an email or not
        int index = completionText.indexOf('@');
        if (index == -1) {
            return new Person(completionText, completionText.replace(" ", "") + "@example.com");
        } else {
            return new Person(completionText.substring(0, index), completionText);
        }
    }
}

Code de mise en page pour contact_token (vous devrez trouver votre propre x dessinable)

<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_height="wrap_content"
    Android:layout_width="wrap_content"
    Android:background="@drawable/token_background">
    <TextView Android:id="@+id/name"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:textColor="@Android:color/white"
        Android:textSize="14sp"
        Android:text="Test Me"
        Android:padding="2dp" />

    <ImageView
        Android:layout_height="10dp"
        Android:layout_width="10dp"
        Android:src="@drawable/x"
        Android:layout_gravity="center_vertical"
        Android:layout_marginLeft="3dp"
        Android:layout_marginRight="5dp" />
</LinearLayout>

Fond de jeton dessinable

<shape xmlns:Android="http://schemas.Android.com/apk/res/Android" >
    <solid Android:color="#ffafafaf" />
    <corners
        Android:topLeftRadius="5dp"
        Android:bottomLeftRadius="5dp"
        Android:topRightRadius="5dp"
        Android:bottomRightRadius="5dp" />
</shape>

Code objet personne

public class Person implements Serializable {
    private String name;
    private String email;

    public Person(String n, String e) { name = n; email = e; }

    public String getName() { return name; }
    public String getEmail() { return email; }

    @Override
    public String toString() { return name; }
}

Exemple d'activité

public class TokenActivity extends Activity {
    ContactsCompletionView completionView;
    Person[] people;
    ArrayAdapter<Person> adapter;

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

        people = new Person[]{
                new Person("Marshall Weir", "[email protected]"),
                new Person("Margaret Smith", "[email protected]"),
                new Person("Max Jordan", "[email protected]"),
                new Person("Meg Peterson", "[email protected]"),
                new Person("Amanda Johnson", "[email protected]"),
                new Person("Terry Anderson", "[email protected]")
        };

        adapter = new ArrayAdapter<Person>(this, Android.R.layout.simple_list_item_1, people);

        completionView = (ContactsCompletionView)findViewById(R.id.searchView);
        completionView.setAdapter(adapter);
        completionView.setTokenClickStyle(TokenCompleteTextView.TokenClickStyle.Delete);
    }
}

Code de mise en page

<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">

    <com.tokenautocomplete.ContactsCompletionView
        Android:id="@+id/searchView"
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content" />

</RelativeLayout>
4
Marshall Weir