android material design 风格组件(MaterialButton,chip,开源chip)(一)

1,932 阅读7分钟

准备打算出一个系列的material design风格组件的使用系列,这是第一篇先试试水😊

先来看看今天要完成的效果:

MaterialButton
MaterialButtonToggleGroup
Chip
ChipGroup
ChipDrawable
ChipsView
ChipsInput

⚠️:本系列结合官方文档以及自己的理解编写,暂不涉及源码部分,全是使用!

环境

  • system: macOS
  • android studio: 4.1.3
  • gradle:gradle-6.5-bin.zip
  • project gradle:4.1.3
  • kotilin:1.5.0
  • ViewBinding: true
  • material:1.4.0
  • appcompat:1.4.0

Material环境配置

设置主题

首先需要将主题(theme)改成material风格的

 // 原来使用AppCompat风格不带ActionBar的
 <style name="MyTheme" parent="Theme.AppCompat.Light.NoActionBar">
// 与AppCompat风格不带ActionBar的对应的 material主题
<style name="MyTheme" parent="Theme.MaterialComponents.Light.NoActionBar">

// 当前material风格主题(带ActionBar)
<style name="MyTheme" parent="Theme.MaterialComponents.Light">

主题参考链接

在xml中配置

默认不配置,看不出material风格组件的变化


选择对应的主题,这里就可以显示出material样式了

在已有的AppCompat主题中使用material主题控件

可以看到在AppCompat主题中使用material主题的空间会直接崩溃,他们之间是不兼容的... 在这里插入图片描述

若你的项目中在<application 中设置theme主题为AppCompat,那么我认为最好的解决办法为:

在这里插入图片描述 将需要用material组件的activity设置为material主题即可!

温馨提示:material主题兼容AppCompat主题,AppCompat主题不兼容material主题

接下来就步入主题吧!

MaterialButton

最原始的按钮:

 <com.google.android.material.button.MaterialButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="MD按钮" />

在这里插入图片描述

在文字某侧显示的materialButton:

<com.google.android.material.button.MaterialButton
      style="@style/Widget.MaterialComponents.Button.Icon"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="左侧有图标"
      app:icon="@drawable/ic_emoji_02"
      app:iconGravity="textStart"
      app:iconPadding="10dp"
      app:iconSize="40dp" />
  • app:iconPadding="dp" 图标和文字的距离
  • app:iconSize="dp" 图标的大小
  • app:iconGravity=“” 图标的模式

在这里插入图片描述 其他效果:

参数说明效果
左侧app:iconGravity="textStart"在这里插入图片描述
右侧app:iconGravity="textEnd"在这里插入图片描述
上侧app:iconGravity="textTop"在这里插入图片描述
可以看出,这里虽然实现了icon在文字的某一侧,但是图片为啥是黑色的??

我的原始图片长这样:


这里是需要再次设置icon的色调和色调模式:

如果需要正常显示可以这样设置:

 <com.google.android.material.button.MaterialButton
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
          ...
         app:iconTint="@color/white"
         app:iconTintMode="multiply" />
  • app:iconTintMode="" 设置色调模式
  • app:iconTint=“” 设置色调颜色 在这里插入图片描述

什么是色调颜色?比如这样:

 <com.google.android.material.button.MaterialButton
          ...
         app:iconTint="@color/red"
         app:iconTintMode="multiply" />

在这里插入图片描述

 <com.google.android.material.button.MaterialButton
            ..
            app:iconTint="@color/red"
            # app:iconTintMode="multiply"
            # app:iconTintMode="add"
            # app:iconTintMode="src_over"

	 />

其余色调模式展示:

multiplyaddsrc_over
在这里插入图片描述在这里插入图片描述在这里插入图片描述
背景色调
 <com.google.android.material.button.MaterialButton
            ...
            android:backgroundTint="@color/red"
            android:backgroundTintMode="add" />

在这里插入图片描述

背景色调和上图表类似,就不多做样式

水波纹[app:rippleColor]

<com.google.android.material.button.MaterialButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="水波纹颜色"
        app:rippleColor="@color/red" />

描边圆角

<com.google.android.material.button.MaterialButton
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="描边(stroke)"
       android:textAllCaps="false"
       app:cornerRadius="30dp"
       app:strokeColor="@color/red"
       app:strokeWidth="1dp" />

在这里插入图片描述

常见的样式

文本样式:

 <com.google.android.material.button.MaterialButton
            style="@style/Widget.MaterialComponents.Button.TextButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="文本样式"
            android:textAllCaps="false"
            app:icon="@drawable/ic_emoji_08" />

效果图:

自带描边样式:

    <com.google.android.material.button.MaterialButton
            style="?attr/materialButtonOutlinedStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="自带描边样式"
            android:textAllCaps="false" />

自定义属性[app:shapeAppearance]

如果你需要自定义菱形边,你可以这样做: 在这里插入图片描述 可以参考官方的一张图来理解一下: 在这里插入图片描述

materialButton常用属性小结:

参数说明
app:icon图标
app:iconPadding图标和文字的距离
app:iconSize图标的大小
app:iconGravity图标的模式
app:iconTint图标的色调
app:iconTintMode图标的着色模式
app:cornerRadius按钮圆角
app:strokeColor按钮轮廓颜色
app:strokeWidth按钮轮廓粗细
app:rippleColor水波纹颜色
android:backgroundTint背景色调
android:backgroundTintMode背景色调模式
@style/Widget.MaterialComponents.Button.TextButton文字样式
?attr/materialButtonOutlinedStyle自带描边样式
app:shapeAppearance自定义属性(需配合cornerFamily,cornerSize使用)
cornerFamily切边模式
cornerSize切边大小

更多详细参数查看

MaterialButtonToggleGroup

文本样式:

<com.google.android.material.button.MaterialButtonToggleGroup
      android:id="@+id/materialButtonGroup1"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:orientation="horizontal"
       app:checkedButton="@+id/bt2"
       app:singleSelection="true">
       <com.google.android.material.button.MaterialButton
           android:id="@+id/bt1"
           style="@style/Widget.MaterialComponents.Button.TextButton"
           android:text="深色模式"
           ...
           />
    
       <com.google.android.material.button.MaterialButton
           android:id="@+id/bt2"
           style="@style/Widget.MaterialComponents.Button.TextButton"
           android:text="浅色模式"
           ... />

       <com.google.android.material.button.MaterialButton
           android:id="@+id/bt3"
           style="@style/Widget.MaterialComponents.Button.TextButton"
           android:text="跟随系统"
           ... />

</com.google.android.material.button.MaterialButtonToggleGroup>

温馨提示:MaterialButtonToggleGroup继承自LinearLayout

MaterialButtonToggleGroup参数介绍:

  • android:orientation 设置方向
  • app:singleSelection 是否为单选
  • app:checkedButton 默认选中

来看看效果:


为了防止超出屏幕,常使用MaterialButtonToggleGroup时,我喜欢给他添加一个HorizontalScrollView,用来避免超出屏幕

例如这样:

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

    <com.google.android.material.button.MaterialButtonToggleGroup
        android:id="@+id/materialButtonGroup2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <com.google.android.material.button.MaterialButton
            android:id="@+id/bt4"
            style="?attr/materialButtonOutlinedStyle"
            android:text="语文" />

        <com.google.android.material.button.MaterialButton
            android:id="@+id/bt5"
            style="?attr/materialButtonOutlinedStyle"
            android:text="数学" />

        <com.google.android.material.button.MaterialButton
            android:id="@+id/bt6"
            style="?attr/materialButtonOutlinedStyle"
            android:text="英语"
            android:textAllCaps="false" />

    </com.google.android.material.button.MaterialButtonToggleGroup>

</HorizontalScrollView>

当然,也可以通过动态代码来添加内容:

 val materialButtonList = listOf(
            MaterialButton(
                this,
                null,
                R.attr.materialButtonOutlinedStyle // 动态设置样式
            ).apply { text = "历史" },
            MaterialButton(
                this,
                null,
                R.attr.materialButtonOutlinedStyle // 动态设置样式
            ).apply { text = "化学" },
            MaterialButton(
                this,
                null,
                R.attr.materialButtonOutlinedStyle // 动态设置样式
            ).apply { text = "音乐" },
            MaterialButton(
                this,
                null,
                R.attr.materialButtonOutlinedStyle // 动态设置样式
            ).apply { text = "美术" },
        )

        // 动态添加
        materialButtonList.forEach {
            binding.materialButtonGroup2.addView(it)
        }

来看看效果:


切换状态:

在这里插入图片描述 切换状态这里用到了SwitchMaterial,奔着匠人精神的原则,本篇先不讲,后续一定会讲到!

这里的逻辑应该看一遍就明白了

首先监听到switch的状态,通过他的状态来判断是否是垂直|水平,在做对应的事情

这里的takeTrue是我写的一个小扩展,非0及true

fun Boolean.takeTrue(other: Int = 1) =
    if (!this) {
        0
    } else {
        other
    }

Chip

最简单的chip:

 <com.google.android.material.chip.Chip
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="最简单的chip" />

chip有一些属性和materialButton都是通用的,这里就直接全介绍了!

 <com.google.android.material.chip.Chip
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="左侧图片"
         android:checkable=""
         android:checked=""
         app:chipIconEnabled=“”
         app:checkedIcon=“”
         app:closeIcon=“”
         app:checkedIconEnabled=“”
         app:closeIcon=“”
         app:closeIconEnabled=“”
         app:chipIcon="@drawable/ic_emoji_01" />
参数类型说明
android:checkableBooleantrue可以激活chip,为false就像一个button
android:checkedBoolean是否默认选中
------
app:chipIcondrawableicon(默认在文侧)
app:chipIconEnabledbooleanapp:chipIcon是否激活
------
app:checkedIcondrawable左侧点击后的按钮(会覆盖chipIcon)
app:checkedIconEnabledBooleanapp:checkedIcon是否激活(默认false)
-------
app:closeIcondrawable右侧按钮
app:closeIconEnabledBooleanapp:closeIcon按钮是否激活
-------
app:chipBackgroundColorcolor背景颜色
app:chipStrokeColorcolor描边颜色
app:chipStrokeWidthdp描边宽度
app:chipCornerRadiusdp圆角
app:rippleColorcolor水波纹颜色
android:elevationdp高度/深度(Z轴距离 default:0)
app:shapeAppearancestyle自定义属性(需配合cornerFamily)
cornerFamilyenum自定义切边样式(单独使用无效)
cornerSize自定义切边大小(单独使用无效)

效果图:

说明效果
icon的一些操作
阴影点击

ChipGroup

多选

tips:ChipGroup继承自FlowLayout(可以自动换行)

先来看看最简单的使用:

<com.google.android.material.chip.ChipGroup
      android:id="@+id/chipGroup1"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       app:checkedChip="@id/chipPython"
       app:chipSpacingVertical="5dp"
       app:chipSpacingHorizontal="@dimen/dp_40">
       <com.google.android.material.chip.Chip
           ...
           android:text="java" />

       <com.google.android.material.chip.Chip
           ...
           android:text="kotlin" />

       <com.google.android.material.chip.Chip
           ...
           android:text="c" />

       <com.google.android.material.chip.Chip
           ...
           android:text="dart" />

       <com.google.android.material.chip.Chip
           ...
           android:text="C#" />

       <com.google.android.material.chip.Chip
           ...
           android:text="python" />

       <com.google.android.material.chip.Chip
           ...
           android:text="JS" />

   </com.google.android.material.chip.ChipGroup>
  • app:checkedChip 默认选中
  • app:chipSpacingHorizontal 水平距离
  • app:chipSpacingVertical 垂直间距

来看看效果图:


chipGroup#setOnCheckedChangeListener监听点击 请添加图片描述

注意,这段代码是无效的!只有在单选模式下可以使用,来看看官方解释: 在这里插入图片描述

Register a callback to be invoked when the checked chip changes in this group. This callback is only invoked in single selection mode. 注册一个回调,以便在该组中检查的芯片发生更改时调用。此回调仅在单一选择模式下调用。(百度翻译)

如果你想强行点击,你可以这样做:

// 霸王硬上弓
binding.chipGroup1.forEach { childView ->
	  if (childView is Chip) {
	      // 点击监听
	      childView.click {
	          "${childView.text}-${childView.isChecked}" toast this
	      }
	
	      // 关闭监听
	      childView.setOnCloseIconClickListener {
	
	      }
	  }
}

原理就是循环chipGroup中的每一个chip,都添加点击事件即可.

单选

<HorizontalScrollView
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
<com.google.android.material.chip.ChipGroup
     android:id="@+id/chipGroup2"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     app:checkedChip="@+id/chipChinese"
     app:singleLine="true"
     app:singleSelection="true">

     <com.google.android.material.chip.Chip
         android:id="@+id/chipChinese"
         style="@style/Widget.MaterialComponents.Chip.Filter"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="语文" />

     <com.google.android.material.chip.Chip
         style="@style/Widget.MaterialComponents.Chip.Filter"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="数学" />

     <com.google.android.material.chip.Chip
         style="@style/Widget.MaterialComponents.Chip.Filter"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="英语" />

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

chipGroup参数介绍:

  • app:singleLine="true" 设置一行
  • app:singleSelection="true" 单选

当然,也可以动态添加view,例如这样:

在这里插入图片描述 来看看效果图:


chipGroup参数总结:

参数类型说明default
app:singleLineBoolean是否保持一行false
app:singleSelectionBoolean是否单选false
app:checkedChipid默认选中
app:chipSpacingHorizontaldp水平间距
app:chipSpacingVerticaldp垂直间距
chipGroup#checkedChipIdsint[]获取当前选中的view集合
chipGroup#setOnCheckedChangeListener(ChipGroup,checkedId)点击选中(只针对设置app:singleSelection=“true"使用)
chipGroup#setOnHierarchyChangeListeneronChildViewAdded(parent: View?, child: View)
onChildViewRemoved(parent: View?, child: View)
添加和删除监听

更多属性查看

style的使用总结:

@style/Widget.MaterialComponents.Chip.Action 默认

@style/Widget.MaterialComponents.Chip.Entry@style/@style/Widget.MyApp.Chip.Choice@style/Widget.MaterialComponents.Chip.Filter
默认右侧删除按钮,选中后会出现选中状态选中会留色在chipGroup中使用,选中后左侧会出现选中图标

如果你选中的同时想要有选中的图标并且自定义你选中的颜色,你可以这样做:

在这里插入图片描述 可以自定义一个选择器,当选中状态的时候,自定义你的颜色即可

ChipDrawable

ChipDrawable常配合editTextView来使用,例如这样

xml:

<androidx.appcompat.widget.AppCompatTextView
            ...
            android:text="ChipDrawable and Edit:" />

<androidx.appcompat.widget.AppCompatEditText
     android:id="@+id/edit"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:hint="请输入4位" />

 <com.google.android.material.button.MaterialButton
     android:id="@+id/btPrintEdit"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:text="点我输出结果" />

ChipEditActivity:

// 可以自定义长度.. 
 val tempLength = 4
 
 binding.edit.addTextChangedListener(object : TextWatcher {
     override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
     }

     override fun onTextChanged(
         charSequence: CharSequence,
         start: Int,
         before: Int,
         count: Int
     ) {
     }

     override fun afterTextChanged(editable: Editable) {
         // 如果
         if (editable.length % tempLength == 0 && editable.isNotEmpty()) {
             val chip: ChipDrawable =
                 ChipDrawable.createFromResource(this@ChipEditActivity, R.xml.item_chip)

             chip.text = editable.subSequence(editable.length - tempLength, editable.length)
             chip.setBounds(0, 0, chip.intrinsicWidth, chip.intrinsicHeight)

             val span = ImageSpan(chip)

             editable.setSpan(
                 span,
                 editable.length - tempLength,
                 editable.length,
                 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
             )
         }
     }
 })

R.xml.item_chip:

<?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/cccccc"
    app:chipIcon="@drawable/ic_emoji_05"
    app:closeIconEnabled="false"
    app:closeIconTint="@android:color/white"/>

说实话,我感觉这个缺陷很大,因为他不能点击,我认为他只是一种“效果”..还有待发展

来看看效果:

其余细节,请下载代码查看..

ChipsInput

ChipsInput是老外开源的一个chips组件官方地址

不过已经有很多年没有维护了,使用起来也或多或少有一些bug

所以我就把他的module导入到我的项目中,改了改他的bug,也学习了一下他的代码思路

使用场景:

  • 搜索框
  • 一些特定场景的输入框

使用:

<com.pchmn.materialchips.ChipsInput
     android:id="@+id/chips_input"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     app:hint="下拉输入框[可输入 'e' 来搜索 em 小图标]"
     app:maxRows="3" />

自行探索:

  • app:chip_labelColor="@color/black"
  • app:chip_hasAvatarIcon="true"
  • app:chip_backgroundColor="@color/black"
  • app:chip_deletable="false"
  • app:hintColor="@color/black"
  • app:textColor="@color/black"
  • app:chip_deleteIconColor="@color/teal_200"
  • app:chip_detailed_textColor="@color/teal_200"
  • app:chip_detailed_backgroundColor="@color/teal_200"
  • app:chip_detailed_deleteIconColor="@color/teal_200"
  • app:filterable_list_backgroundColor="@color/teal_200"
  • app:filterable_list_textColor="@color/teal_200"

再来看看动态代码: 在这里插入图片描述 来看看效果:


可以看出,这里只定义了em1 - em9 的内容,所以只能搜索到这些.

另外还有一些监听,例如输入监听:

binding.chipsInput.addChipsListener(object : ChipsInput.ChipsListener {
      override fun onChipAdded(chip: ChipInterface, newSize: Int) {
          Log.e(TAG, "chip added, $newSize")
      }

      override fun onChipRemoved(chip: ChipInterface?, newSize: Int) {
          Log.e(TAG, "chip removed, $newSize")
      }

      override fun onTextChanged(text: CharSequence?) {
          // 输入监听
          Log.e(TAG, "chip text changed: " + text.toString())
      }
  })

其余细节请下载代码自行查看!

ChipsView

ChipsView也是老外写的一个开源的逐渐原文地址,这个也是很久没有维护了,还是用v7包写的,还有一些小bug,我还是吧他的module导入到我的项目中改了改,使用很简单!

ChipsView我是做了一个和chipGroup联动的效果,例如这样:

代码有点多,这里只看效果,具体细节请下载代码查看!

完整代码

原创不易,您的点赞就是对我最大的支持!