【问题标题】:How to add chips from Material Components library to input field in android?如何将材料组件库中的芯片添加到android中的输入字段?
【发布时间】:2018-11-07 13:16:57
【问题描述】:

我看到在 android-P google 中添加了包含材料芯片的新材料组件库:

Material components for android

Material.io chips usage

Material components on GitHub

所以我决定在我的项目中添加材料输入芯片,但不幸的是没有找到任何教程如何制作它。我想创建类似 Gmail 芯片但一开始没有图像的东西。

因为我使用的是 appcompat 库,所以我尝试使用 android.support.design.chip.Chipandroid.support.design.chip.ChipGroup 的材料芯片。 但结果只是没有任何输入字段的筹码。我还尝试创建一个独立的 ChipDrawable,然后使用

将其添加到EditText
Editable text = editText.getText();

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

但我得到了没有任何筹码的空 EditText。那么如何使用这个材料组件库像在 Gmail 中一样创建芯片输入呢?也许有人有经验或知道我可以看到如何创建它的任何教程?

提前致谢!

【问题讨论】:

标签: android material-design material-components-android material-components android-chips


【解决方案1】:

回答

在 android 中没有添加芯片的默认输入字段。他们提到了输入芯片,但我没有找到输入芯片的任何布局或视图组。所以我用Chipdrawable 方法在edittext 中添加芯片。这里使用AppCompatEdittext,您可以更改为监听文本输入的任何视图。 Reference.

第 1 步

添加芯片xml资源。 chip.xml

res -> xml -> 芯片.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" />

然后在 style.xml 中添加 textappearance 样式(用于更改 textStyle)

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

第 2 步

使用 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" />

第 3 步
将此代码添加到您的视图中以获得所需的行为。

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

        }
    });

edittext中需要添加新芯片时,根据需要更改chipLength。

输出

已编辑

您可以找到有关如何将文本与 span Here 对齐的更多信息。

在这里,我添加了一些解决方案中的代码,将为您修复..

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

}

并像下面这样更改您的图像跨度类

VerticalImageSpan span = new VerticalImageSpan(chip);

【讨论】:

  • 如何使输入文本center vertical ?使芯片内外的文字处于同一水平
  • @SviatVolkov 您需要添加自定义类才能获取文本查看我编辑的答案。
  • 如果我使用过滤器从联系人列表中选择联系人,如何添加芯片作为背景。
  • 在我的仪器测试的ChipDrawable 实例化过程中,我遇到了java.lang.IllegalArgumentException: The style on this component requires your app theme to be Theme.MaterialComponents (or a descendant).。有什么线索吗?我的应用主题的父级是Theme.MaterialComponents.Light.NoActionBar.Bridge
【解决方案2】:

不是理想的解决方案,但绝对容易

<FrameLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <com.google.android.material.chip.Chip
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:chipMinTouchTargetSize="0dp" />

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal"
                android:weightSum="10">

                <EditText
                    android:id="@+id/add_frag_label_et_name"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center"
                    android:layout_marginStart="8dp"
                    android:layout_weight="9"
                    android:background="@null"
                    android:hint="Enter Label"
                    android:inputType="text"
                    android:paddingStart="8dp"
                    android:paddingEnd="0dp"
                    android:textAppearance="@style/TextAppearance.AppCompat.Medium"
                    android:textColor="@color/black"
                    android:textSize="14sp" />

                <ImageView
                    android:id="@+id/add_frag_label_iv_add"
                    android:layout_width="32dp"
                    android:layout_height="32dp"
                    android:layout_gravity="center"
                    app:tint="#6b6a6c"
                    android:layout_weight="1"
                    android:contentDescription="Add Label to note."
                    android:src="@mipmap/ic_plus_foreground" />
            </LinearLayout>
        </FrameLayout>

【讨论】:

  • 我使用芯片布局作为背景。而已。最重要的是,有一个正常的编辑文本和一个加号图像。您可以在其上添加点击侦听器以触发事件。这是一个快速修复,而不是解决方案。
【解决方案3】:

如果您想在多行上使用芯片实现类似 gmail 的行为,那么所有以前的解决方案都不适用于我。 为了做到这一点,我不得不避免使用 ChipGroup,而是使用 FlexboxLayout

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

现在的诀窍是在组中添加一个新筹码,但排名倒数第二。像这样的:

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

【讨论】:

  • 谢谢!它工作得很好,我仍然需要检查 Android 版本
  • 好主意!您不需要使用芯片视图,膨胀自己的布局并在 FlexboxLayout 中添加,我认为芯片仅在您使用材料组件完整时才有用。感谢您的帮助。
  • 好主意,我只需要保持edittext背景透明并在线性布局后画一条线来伪造edittext中的引导线。 tks
  • 好主意,可以分享完整代码吗?
  • 如果需要对芯片进行定制,可以像 val chip = LayoutInflater.from(context).inflate(R.layout.my_chip_layout, chipGroup, false) as Chip 这样膨胀,其中 my_chip_layout 可以有 &lt;com.google.android.material.chip.Chip 作为父级
【解决方案4】:

您可以在build.gradle中添加材料芯片“com.google.android.material.chip.Chip”和“implementation 'com.google.android.material:material:1.0.0'”

过滤器 style="@style/Widget.MaterialComponents.Chip.Filter"

选择筹码 style="@style/Widget.MaterialComponents.Chip.Choice"

输入输入: style="@style/Widget.MaterialComponents.Chip.Entry"

【讨论】:

    【解决方案5】:

    我们可以通过使用材料芯片设计本身来做到这一点,而无需添加任何额外的样式。

    将其添加到 AndroidX 的应用程序 gradle 中

    实现'com.google.android.material:material:1.0.0-beta01'

    AndroidX 之前使用这个

    实现'com.android.support:design:28.0.0'

    片段

    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 文件:

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

    更多参考Click Here

    【讨论】:

    • 适用于具有自动完成功能的 AndroidX github.com/karanatwal/MaterialChipsInput
    • 嗨,Naveen,这个解决方案很棒。我有一个问题,有什么方法可以让我们在 ChipGroup 中传递一个字符串列表,它会像回收站视图一样自动膨胀?
    • @NileshRathore 通过 uisng addChipToGroup 函数本身你可以膨胀。只是你想要通过编程而不是打字来实现。因此,将列表传递给此函数并在每个循环中对其进行迭代,将视图添加到chipGroup。这里chipGroup只是一个空容器
    • 是的,我现在已经在这样做了,但问题是如果我从某个地方获得一个新更新的列表,那么我必须删除所有以前添加的视图并再次迭代以添加它一。在回收站视图中,我们有 Diff Util,因此只有更改的项目才会更新。
    • 是的,这是唯一的方法。或者你可以直接在 list_row 中使用带有 ChipGroup 的回收器视图
    猜你喜欢
    • 2021-10-23
    • 2016-05-06
    • 2019-02-03
    • 2019-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多