Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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!

like image 943
IDmikael Avatar asked May 29 '18 00:05

IDmikael


People also ask

How do you use material chips?

Tap a chip to select it. Multiple chips can be selected or unselected. An icon can be added to indicate when a filter chip is selected. Filter chip suggestions can dynamically change as users start to select filters.

What is ChipGroup in android?

A ChipGroup is used to hold multiple Chip s. By default, the chips are reflowed across multiple lines. Set the app:singleLine attribute to constrain the chips to a single horizontal line. If you do so, you'll usually want to wrap this ChipGroup in a HorizontalScrollView .

What is chip button android?

It is a rounded button that consists of a label, an optional chip icon, and an optional close icon. A chip can either be clicked or toggled if it is checkable. Chips may be placed in a, which can be configured to layout its chips in a single horizontal line or reflowed across multiple lines.


3 Answers

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); 
like image 185
Mohamed Mohaideen AH Avatar answered Sep 22 '22 01:09

Mohamed Mohaideen AH


All of the previous solutions didn't work for me if you want to achieve a gmail like behaviour with chips on multiple lines. In order to do that I had to avoid using the ChipGroup and instead using a FlexboxLayout.

enter image description here

your_recipient_layout:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">  <LinearLayout     android:layout_width="match_parent"     android:layout_height="wrap_content"     android:orientation="horizontal">      <TextView         android:id="@+id/recipient_label_TV"         android:layout_width="wrap_content"         android:layout_height="wrap_content"         android:layout_marginStart="8dp"         android:layout_marginEnd="8dp"         android:layout_gravity="center_vertical" />      <com.google.android.flexbox.FlexboxLayout         android:id="@+id/recipient_group_FL"         android:layout_width="match_parent"         android:layout_height="wrap_content"         android:layout_marginStart="8dp"         android:layout_gravity="center_vertical"         app:flexWrap="wrap"         app:alignItems="stretch"         app:alignContent="space_around"         app:showDivider="beginning|middle|end"         app:dividerDrawable="@drawable/divider">          <EditText             android:id="@+id/recipient_input_ET"             android:layout_width="wrap_content"             android:layout_height="32dp"             app:layout_flexGrow="1"             android:background="@android:color/transparent"             android:imeOptions="actionDone"             android:inputType="text"/>      </com.google.android.flexbox.FlexboxLayout>  </LinearLayout>  <androidx.recyclerview.widget.RecyclerView     android:id="@+id/recipients_list_RV"     android:layout_width="match_parent"     android:layout_height="wrap_content"     android:visibility="gone" /> 

The trick now is adding a new chip to the group but as second last position. Something like this:

private fun addNewChip(person: String, chipGroup: FlexboxLayout) {     val chip = Chip(context)     chip.text = person     chip.chipIcon = ContextCompat.getDrawable(requireContext(), R.mipmap.ic_launcher_round)     chip.isCloseIconEnabled = true     chip.isClickable = true     chip.isCheckable = false     chipGroup.addView(chip as View, chipGroup.childCount - 1)     chip.setOnCloseIconClickListener { chipGroup.removeView(chip as View) } } 
like image 40
br00 Avatar answered Sep 22 '22 01:09

br00


We can do this by using material chips design itself without adding any extra styles.

Add it on app gradle For AndroidX

implementation 'com.google.android.material:material:1.0.0-beta01'

For earlier than AndroidX use this

implementation 'com.android.support:design:28.0.0'

enter image description here

Fragment

class EntryChipDemoFragment : Fragment() {
    private lateinit var mView: View

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        mView = inflater.inflate(R.layout.fragment_entry_chip_demo, container, false)

        mView.etValue.setOnEditorActionListener(TextView.OnEditorActionListener { v, actionId, _ ->
            if (actionId == EditorInfo.IME_ACTION_DONE) {
                val txtVal = v.text
                if(!txtVal.isNullOrEmpty()) {
                    addChipToGroup(txtVal.toString(), mView.chipGroup2)
                    mView.etValue.setText("")
                }

                return@OnEditorActionListener true
            }
            false
        })

        return mView
    }


    private fun addChipToGroup(txt: String, chipGroup: ChipGroup) {
        val chip = Chip(context)
        chip.text = txt
//        chip.chipIcon = ContextCompat.getDrawable(requireContext(), baseline_person_black_18)
        chip.isCloseIconEnabled = true
        chip.setChipIconTintResource(R.color.chipIconTint)

        // necessary to get single selection working
        chip.isClickable = false
        chip.isCheckable = false
        chipGroup.addView(chip as View)
        chip.setOnCloseIconClickListener { chipGroup.removeView(chip as View) }
        printChipsValue(chipGroup)
    }

    private fun printChipsValue(chipGroup: ChipGroup) {
        for (i in 0 until chipGroup.childCount) {
            val chipObj = chipGroup.getChildAt(i) as Chip
            Log.d("Chips text :: " , chipObj.text.toString())

        }
    }

    companion object {
        @JvmStatic
        fun newInstance() = EntryChipDemoFragment()
    }
}

XML File:

<HorizontalScrollView
    android:id="@+id/chipGroup2HorizontalView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="8dp"
    android:layout_marginTop="16dp"
    android:scrollbars="none"
    app:layout_constraintVertical_bias="0.62">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:orientation="horizontal">

        <androidx.appcompat.widget.AppCompatTextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Skills: " />

        <com.google.android.material.chip.ChipGroup
            android:id="@+id/chipGroup2"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:duplicateParentState="false">

        </com.google.android.material.chip.ChipGroup>

        <com.google.android.material.textfield.TextInputLayout
            android:id="@+id/textInputLayout"
            android:layout_width="wrap_content"
            android:layout_height="43dp"
            android:layout_marginStart="8dp"
            android:layout_marginEnd="8dp"
            android:layout_marginBottom="5dp"
            android:minWidth="32dp"
            android:visibility="visible"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@id/chipGroup2HorizontalView"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintWidth_min="32dp">

            <androidx.appcompat.widget.AppCompatEditText
                android:id="@+id/etValue"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@android:color/transparent"
                android:imeOptions="actionDone"
                android:maxLines="1"
                android:singleLine="true" />

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

    </LinearLayout>


</HorizontalScrollView>

For more reference Click Here

like image 36
Naveen Kumar M Avatar answered Sep 21 '22 01:09

Naveen Kumar M