Search code examples
androidmaterial-designmaterial-components-androidmaterial-componentsandroid-chips

How to add chips from Material Components library to input field in android?


I've seen that in android-P google add new material components library which contains material chips:

Material components for android

Material.io chips usage

Material components on GitHub

So I decided to add material input chips to my project, but unfortunately didn't find any tutorial how to make that. I want to create something like Gmail chips but without image on the start.

Because I'm using appcompat library I tried to use material chips by android.support.design.chip.Chip and android.support.design.chip.ChipGroup. But result was just chips without any input field. Also I tried to create a Standalone ChipDrawable and then add it to EditText using

Editable text = editText.getText();

text.setSpan(span, 0, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

But I got empty EditText without any chips. So how can I create chips input like in Gmail using this material components library? Maybe someone has expreience or knows any tutorials where I could see how to create this?

Thanks in advance!


Solution

  • Answer

    No default input field for adding chips in android. They mentioned input chips but i didn't find any layout or viewgroup for input chips. So i do with Chipdrawable method to add chips in edittext. Here am using AppCompatEdittext you can change to anyview which listening the text inputs. Reference.

    Step 1

    Add chip xml resource. chip.xml

    res -> xml -> chip.xml

    <?xml version="1.0" encoding="utf-8"?>
    <chip xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:textAppearance="@style/ChipTextAppearance"
     app:chipBackgroundColor="@color/colorAccent"
     app:chipIcon="@drawable/ic_call_white_24dp"
     app:closeIconEnabled="true"  <!--property for close icon if no need set to false. -->
     app:closeIconTint="@android:color/white" />
    

    Then add textappearance style in style.xml(For change textStyle)

    <style name="ChipTextAppearance" parent="TextAppearance.MaterialComponents.Chip">
        <item name="android:textColor">@android:color/white</item>
    </style>
    

    Step 2

    Add your view here am using AppCompatEdittext

      <android.support.v7.widget.AppCompatEditText
        android:id="@+id/phone"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tvt_Contact" />
    

    Step 3
    Add this code to your view to get the desired behaviour.

     private int SpannedLength = 0,chipLength = 4;
    
     AppCompatEditText Phone = findViewById(R.id.phone);
    
     Phone.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
    
            }
    
            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                if (charSequence.length() == SpannedLength - chipLength)
                {
                    SpannedLength = charSequence.length();
                }
            }
    
            @Override
            public void afterTextChanged(Editable editable) {
    
                if(editable.length() - SpannedLength == chipLength) {
                    ChipDrawable chip = ChipDrawable.createFromResource(getContext(), R.xml.chip);
                    chip.setChipText(editable.subSequence(SpannedLength,editable.length()));
                    chip.setBounds(0, 0, chip.getIntrinsicWidth(), chip.getIntrinsicHeight());
                    ImageSpan span = new ImageSpan(chip);
                    editable.setSpan(span, SpannedLength, editable.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                    SpannedLength = editable.length();
                }
    
            }
        });
    

    Change chipLength according to your need when new chip need to be added in edittext.

    OUTPUT

    enter image description here

    EDITED

    You can find more about how to align center the text with span Here.

    Here i added some code from the solution will fix for you..

    public class VerticalImageSpan extends ImageSpan {
    
    public VerticalImageSpan(Drawable drawable) {
        super(drawable);
    }
    
    @Override
    public int getSize(@NonNull Paint paint, CharSequence text, int start, int end,
                       Paint.FontMetricsInt fontMetricsInt) {
        Drawable drawable = getDrawable();
        Rect rect = drawable.getBounds();
        if (fontMetricsInt != null) {
            Paint.FontMetricsInt fmPaint = paint.getFontMetricsInt();
            int fontHeight = fmPaint.descent - fmPaint.ascent;
            int drHeight = rect.bottom - rect.top;
            int centerY = fmPaint.ascent + fontHeight / 2;
    
            fontMetricsInt.ascent = centerY - drHeight / 2;
            fontMetricsInt.top = fontMetricsInt.ascent;
            fontMetricsInt.bottom = centerY + drHeight / 2;
            fontMetricsInt.descent = fontMetricsInt.bottom;
        }
        return rect.right;
    }
    
    @Override
    public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end,
                     float x, int top, int y, int bottom, @NonNull Paint paint) {
    
        Drawable drawable = getDrawable();
        canvas.save();
        Paint.FontMetricsInt fmPaint = paint.getFontMetricsInt();
        int fontHeight = fmPaint.descent - fmPaint.ascent;
        int centerY = y + fmPaint.descent - fontHeight / 2;
        int transY = centerY - (drawable.getBounds().bottom - drawable.getBounds().top) / 2;
        canvas.translate(x, transY);
        drawable.draw(canvas);
        canvas.restore();
    }
    
    }
    

    And change your imagespan class like below

    VerticalImageSpan span = new VerticalImageSpan(chip);