Theme.MaterialComponents相關的material庫使用

可參考https://material.io/develop/android/docs/getting-started/
material 簡介,左側列表有分類

2020902

最近看新版的maetrial庫,發現有些控件屬性都發生變化了,所以下邊的僅供參考,用的時候點進源碼,看下構造方法里都支持哪些參數就可以了。
建議看上邊的文檔,文檔是新的.

題外話

studio3.5已經修復無法預覽的bug,所以最好升級下Studio
使用這個material的話,會發現xml的布局預覽有問題,好像只有第一次啟動studio的時候能正常看到布局,過一會就無法預覽了,提示異常,感覺是個bug,臨時的解決辦法就是修改主題,xml布局界面上邊修改主題為藍色那個即可,這樣改以后布局可以看,只是material的效果都沒了。

image.png

另一種解決辦法
stackoverflow
比較簡單的辦法,添加一個預覽的style
上邊的帖子有說道3.5修復了這個bug,確實最新版可以正常預覽了

<com.google.android.material.button.MaterialButton
        tools:style="@style/Widget.MaterialComponents.Button.UnelevatedButton"

這樣加完以后,在xml里使用material主題就可以正常預覽了


image.png

開始使用

gradle里添加庫,我這種是androidX的,非x的好像是design庫里的吧。

    implementation 'com.google.android.material:material:1.1.0-alpha05'

首先要使用MaterialButton,要生效,有幾種方法
測試后建議第一種,唯一的問題就是把以前的Button都默認改成了MaterilaButton,高度低了點。
其他兩種,material控件少了好多默認值,所以控件看起來不舒服,還得自己配置參數

  1. MaterialComponents主題
    使用材料庫修改為這個主題以后,問題多多,簡單看下源碼,了解下新控件的特性。
<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">

使用了這個主題,默認所有的Button最后都都成了MaterialButton
這種按鈕因為帶陰影,所以它的高度看起來比以前的Button矮了許多而且默認背景色是主題色。
按鈕高度可以和我們設置的height一樣的,把inserttop和insetbottom改為0dp即可。

  1. 上邊的主題后邊加個Bridge
    下邊的可選
Theme.MaterialComponents.Bridge
Theme.MaterialComponents.Light.Bridge
Theme.MaterialComponents.NoActionBar.Bridge
Theme.MaterialComponents.Light.NoActionBar.Bridge
Theme.MaterialComponents.Light.DarkActionBar.Bridge

這種只對material下的組件生效,而對以前的Button不生效,它還是老板的Button,默認的灰色

  1. 繼續用Theme.AppCompat
    只是把materail需要的一些默認的屬性添加進去
<style name="Theme.MyApp" parent="Theme.AppCompat">

  <!-- Original AppCompat attributes. -->
  <item name="colorPrimary">@color/my_app_primary_color</item>
  <item name="colorSecondary">@color/my_app_secondary_color</item>
  <item name="android:colorBackground">@color/my_app_background_color</item>
  <item name="colorError">@color/my_app_error_color</item>

  <!-- New MaterialComponents attributes. -->
  <item name="colorPrimaryVariant">@color/my_app_primary_variant_color</item>
  <item name="colorSecondaryVariant">@color/my_app_secondary_variant_color</item>
  <item name="colorSurface">@color/my_app_surface_color</item>
  <item name="colorOnPrimary">@color/my_app_color_on_primary</item>
  <item name="colorOnSecondary">@color/my_app_color_on_secondary</item>
  <item name="colorOnBackground">@color/my_app_color_on_background</item>
  <item name="colorOnError">@color/my_app_color_on_error</item>
  <item name="colorOnSurface">@color/my_app_color_on_surface</item>
  <item name="scrimBackground">@color/mtrl_scrim_color</item>
  <item name="textAppearanceHeadline1">@style/TextAppearance.MaterialComponents.Headline1</item>
  <item name="textAppearanceHeadline2">@style/TextAppearance.MaterialComponents.Headline2</item>
  <item name="textAppearanceHeadline3">@style/TextAppearance.MaterialComponents.Headline3</item>
  <item name="textAppearanceHeadline4">@style/TextAppearance.MaterialComponents.Headline4</item>
  <item name="textAppearanceHeadline5">@style/TextAppearance.MaterialComponents.Headline5</item>
  <item name="textAppearanceHeadline6">@style/TextAppearance.MaterialComponents.Headline6</item>
  <item name="textAppearanceSubtitle1">@style/TextAppearance.MaterialComponents.Subtitle1</item>
  <item name="textAppearanceSubtitle2">@style/TextAppearance.MaterialComponents.Subtitle2</item>
  <item name="textAppearanceBody1">@style/TextAppearance.MaterialComponents.Body1</item>
  <item name="textAppearanceBody2">@style/TextAppearance.MaterialComponents.Body2</item>
  <item name="textAppearanceCaption">@style/TextAppearance.MaterialComponents.Caption</item>
  <item name="textAppearanceButton">@style/TextAppearance.MaterialComponents.Button</item>
  <item name="textAppearanceOverline">@style/TextAppearance.MaterialComponents.Overline</item>

</style>

1. TabLayout

以前修改TabLayout默認的字母大寫的問題,結果換了主題以后,提示這個style是私有的,需要添加
tools:override="true"才可以生效

    <style name="TextAppearance.Design.Tab"  tools:override="true" parent="TextAppearance.AppCompat.Button">
        <!--<item name="android:textSize">20sp</item>-->
        <item name="android:textColor">?android:textColorSecondary</item>
        <item name="textAllCaps">false</item>
    </style>

后來又更新了,完事發現上邊的不生效了,用下邊的方法

app:tabTextAppearance="@style/tabTextStyle"

    <style name="tabTextStyle" parent="TextAppearance.Design.Tab" >
        <item name="textAllCaps">false</item>
    </style>

TabLayout也增加了幾個屬性
以前只有tabIndicatorColor 來設置下邊的線條顏色,現在可以設置一張圖片,二選一
app:tabIndicator:圖片大小是按照選中的tab的寬度拉伸的,重心是bottom。

        app:tabIndicator="@drawable/iv_leaf_1"http://圖片默認是被主題色染色的,
        app:tabIconTint="#FF9800" //這個是給文字上邊那個icon染色用的
       app:tabRippleColor="#8BC34A"http://可以設置點擊的波紋顏色了

代碼和效果如下

        tab.apply {
            addTab(tab.newTab().setText("hello").setIcon(R.drawable.iv_leaf_3))
            addTab(tab.newTab().setText("good").setIcon(R.drawable.iv_leaf_2))
            addTab(tab.newTab().setText("morning").setIcon(R.drawable.iv_leaf_3))
        }
image.png
自定義TabLayout的問題

以前繼承TabLayout,獲取indicator的高度和顏色,然后自己處理,現在發現這個高度成0了,顏色也不對


image.png

臨時解決辦法,在xml布局里手動添加高度

app:tabIndicatorHeight="2dp"
app:tabIndicatorColor="@color/colorPrimary"

查看源碼找到源頭

  1. app:tabIndicatorColor
    這個顏色不對頭,是代碼有問題
    源碼構造方法里有個默認的style,而我這里自定義的把這個構造方法省了,所以默認的這個style也沒了
`  public TabLayout(Context context, AttributeSet attrs) {
    this(context, attrs, R.attr.tabStyle);
  }

修改成如下的代碼即可


image.png
  1. app:tabIndicatorHeight
    這個高度在當前的Theme.MaterialComponents下確實沒有默認值了,具體的自己可以點擊主題,搜索上邊的默認的tabStyle
<item name="tabStyle">@style/Widget.MaterialComponents.TabLayout</item>

    <style name="Widget.MaterialComponents.TabLayout" parent="Widget.Design.TabLayout">
    <item name="enforceMaterialTheme">true</item>
    <item name="enforceTextAppearance">true</item>
    <item name="android:background">?attr/colorSurface</item>
    <item name="tabIconTint">@color/mtrl_tabs_icon_color_selector</item>
    <item name="tabIndicatorAnimationDuration">@integer/mtrl_tab_indicator_anim_duration_ms</item>
    <item name="tabIndicatorColor">?attr/colorPrimary</item>
    <item name="tabTextAppearance">?attr/textAppearanceButton</item>
    <item name="tabTextColor">@color/mtrl_tabs_icon_color_selector</item>
    <item name="tabRippleColor">@color/mtrl_tabs_ripple_color</item>
    <item name="tabUnboundedRipple">true</item>
  </style>
    <style name="Widget.MaterialComponents.TabLayout.Colored">
    <item name="android:background">?attr/colorPrimary</item>
    <item name="tabIconTint">@color/mtrl_tabs_icon_color_selector_colored</item>
    <item name="tabIndicatorColor">?attr/colorOnPrimary</item>
    <item name="tabTextColor">@color/mtrl_tabs_icon_color_selector_colored</item>
    <item name="tabRippleColor">@color/mtrl_tabs_colored_ripple_color</item>
  </style>

繼續父類點擊

<item name="tabIndicator">@drawable/mtrl_tabs_default_indicator</item>

那么看下源碼,為啥系統默認的游標有個高度,這個高度哪來的。


image.png

tabSelectedIndicator:就是設置的指針圖片,這個系統是有默認值的,看下高度是2dp
上邊圖片代碼可以看到,indicatorHeight首先獲取的是這張圖片的高度,完事再看用戶設置的selectedIndicatorHeight高度是否大于0,再改為這個,默認值沒有這個,所以是0,最后就是第一個if條件的值了,也就是下邊的圖片高度了。

<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item>
    <shape
        android:shape="rectangle">
      <solid android:color="@android:color/white"/>
      <size android:height="2dp"/>
    </shape>
  </item>
</selector>

看到這里就順道看下,indicator到底咋畫的
tabSelectedIndicator:這個就是上邊那個2dp的圖片了。
defaultSelectionIndicator:這個就是個GradientDrawable,啥也畫不出來。

image.png

selectedIndicatorPaint的顏色就是在構造方法里設置的indicatorColor,材料主題下就是primaryColor

    void setSelectedIndicatorColor(int color) {
      if (selectedIndicatorPaint.getColor() != color) {
        selectedIndicatorPaint.setColor(color);
        ViewCompat.postInvalidateOnAnimation(this);
      }
    }

所以啊,如果你不想要那線條,很簡單了,下邊這個屬性弄成null,就成了defaultSelectionIndicator,啥也畫不出來。

    app:tabIndicator="@null"

現在也就知道了,下邊的color可以給那圖片染色拉

    app:tabIndicatorColor="#F44336"
    app:tabIndicator="@drawable/iv_leaf_1"

Tab 可以支持加個小紅點了


image.png

不過感覺默認的小紅點有點小啊,還得自定義字體大小,麻煩.

// Get badge from tab (or create one if none exists)
val badge = tab.getOrCreateBadge()
// Customize badge
badge.number = number
// Remove badge from tab
tab.removeBadge()

2 FloatingActionButton

看下材料主題下的默認屬性


    <item name="floatingActionButtonStyle">@style/Widget.MaterialComponents.FloatingActionButton</item>
    <style name="Widget.MaterialComponents.FloatingActionButton" parent="Widget.Design.FloatingActionButton">
    <item name="android:background">@null</item>
    <item name="enforceMaterialTheme">true</item>
    <item name="ensureMinTouchTargetSize">true</item>
    <item name="elevation">@dimen/mtrl_fab_elevation</item>
    <item name="backgroundTint">?attr/colorSecondary</item>
    <item name="tint">?attr/colorOnSecondary</item>
    <item name="hoveredFocusedTranslationZ">@dimen/mtrl_fab_translation_z_hovered_focused</item>
    <item name="pressedTranslationZ">@dimen/mtrl_fab_translation_z_pressed</item>
    <item name="rippleColor">@color/mtrl_fab_ripple_color</item>
    <item name="showMotionSpec">@animator/mtrl_fab_show_motion_spec</item>
    <item name="hideMotionSpec">@animator/mtrl_fab_hide_motion_spec</item>
    <item name="shapeAppearance">?attr/shapeAppearanceSmallComponent</item>
    <item name="shapeAppearanceOverlay">
      @style/ShapeAppearanceOverlay.MaterialComponents.FloatingActionButton
    </item>
  </style>

可以看到FloatingActionButton的默認顏色<item name="backgroundTint">?attr/colorSecondary</item>
看你主題里設置的這個item是啥顏色就是啥,如果沒設置,看下默認的

    <item name="colorSecondary">@color/design_dark_default_color_secondary</item>
image.png

弄個實際列子說下參數的意義

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/iv_leaf_1"http://中心圖片
        app:backgroundTint="@color/colorAccent" //背景色
        app:borderWidth="20dp"http://邊界寬,圖片用紅框圈起來了,可以看到中間顏色比較深
        app:fabCustomSize="100dp"http://自定義button的大小
        app:fabSize="auto"http://如果不自定義大小,這個button的大小其實默認就兩種,normal和mini
        app:maxImageSize="20dp"http://控制圖片的最大值
        app:rippleColor="#2196F3" />//波紋顏色
app:tint //圖片染色

如果顯示隱藏的時候帶動畫,可以調用hide和show方法
效果圖


image.png

繼續看下另一個

3 ExtendedFloatingActionButton

    <item name="extendedFloatingActionButtonStyle">@style/Widget.MaterialComponents.ExtendedFloatingActionButton.Icon</item>

public class ExtendedFloatingActionButton extends MaterialButton implements AttachedBehavior
這玩意好像就是比MaterialButton 多了個動畫。
動畫還得研究,不知道咋生效。。

    <style name="Widget.MaterialComponents.ExtendedFloatingActionButton" parent="Widget.MaterialComponents.Button">
    <item name="android:insetTop">0dp</item>
    <item name="android:insetBottom">0dp</item>
    <item name="android:maxLines">1</item>
    <item name="android:minHeight">@dimen/mtrl_extended_fab_min_height</item>
    <item name="android:minWidth">@dimen/mtrl_extended_fab_min_width</item>
    <item name="android:paddingTop">@dimen/mtrl_extended_fab_top_padding</item>
    <item name="android:paddingBottom">@dimen/mtrl_extended_fab_bottom_padding</item>
    <item name="android:paddingStart" ns1:ignore="NewApi">
      @dimen/mtrl_extended_fab_start_padding
    </item>
    <item name="android:paddingEnd" ns1:ignore="NewApi">
      @dimen/mtrl_extended_fab_end_padding
    </item>
    <item name="android:paddingLeft">@dimen/mtrl_extended_fab_start_padding</item>
    <item name="android:paddingRight">@dimen/mtrl_extended_fab_end_padding</item>
    <item name="android:stateListAnimator" ns1:ignore="NewApi">
      @animator/mtrl_extended_fab_state_list_animator
    </item>
    <item name="android:textColor">@color/mtrl_extended_fab_text_color_selector</item>
    <item name="backgroundTint">@color/mtrl_extended_fab_bg_color_selector</item>
    <item name="elevation">@dimen/mtrl_extended_fab_elevation</item>
    <item name="iconPadding">@dimen/mtrl_extended_fab_icon_text_spacing</item>
    <item name="iconSize">@dimen/mtrl_extended_fab_icon_size</item>
    <item name="iconTint">@color/mtrl_extended_fab_text_color_selector</item>
    <item name="rippleColor">@color/mtrl_extended_fab_ripple_color</item>
    <item name="shapeAppearanceOverlay">
      @style/ShapeAppearanceOverlay.MaterialComponents.FloatingActionButton
    </item>
  </style>

默認寬高,單行顯示,啥也不設置看下結果

    <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
        android:id="@+id/efab3"
        android:layout_marginTop="10dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

第三個是默認的


image.png
動畫測試

這個有4種狀態,對應4個方法
show()
hidden()
顯示隱藏沒啥說的,就是gone和visible的轉換
說下下邊這兩個,展開收縮,是有前提條件的
shrink()
extend()
icon和text必須同時存在,只有文字或者只有圖片,動畫都不會執行的

    if (extended == this.isExtended || getIcon() == null || TextUtils.isEmpty(getText())) {
      return;
    }

測試的時候,寬高是可以隨便設置的,初始顯示的時候也都可以,可進行展開收縮以后,初始設置的寬高就沒意義了。
如下圖,初始修改了下高度


image.png

執行shrink方法以后,直接就成原樣了


image.png

來看下源碼,展開和收縮就是反向的,我們看一個就行。看下extend吧
  private boolean isExtended = true;//默認這個是true,所以先調用extend是無效的,得先shrink才有效
  private void setExtended(
      final boolean extended, boolean animate, @Nullable final OnChangedListener listener) {
    if (extended == this.isExtended || getIcon() == null || TextUtils.isEmpty(getText())) {
      return;
    }
    this.isExtended = extended;
    if (currentCollapseExpandAnimator != null) {
      currentCollapseExpandAnimator.cancel();
    }

    if (animate && shouldAnimateVisibilityChange()) {
//展開的時候,寬高的測量,用的unspecified,也就是wrap的方式了,你設置的寬高固定值沒啥用了
      measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
      Animator collapseExpandAnimator =
          createShrinkExtendAnimator(
              isExtended ? getCurrentExtendMotionSpec() : getCurrentShrinkMotionSpec(),
              !isExtended);

先看下系統默認提供的motionSpec文件,可以看到,里邊只有屬性,沒有對應的值,那么這個值哪里設置的。

<set xmlns:android="http://schemas.android.com/apk/res/android">
  <objectAnimator
      android:propertyName="width"
      android:startOffset="0"
      android:duration="200"
      android:interpolator="@interpolator/mtrl_fast_out_slow_in"/>
  <objectAnimator
      android:propertyName="height"
      android:startOffset="0"
      android:duration="200"
      android:interpolator="@interpolator/mtrl_fast_out_slow_in"/>
  <objectAnimator
      android:propertyName="cornerRadius"
      android:startOffset="0"
      android:duration="200"
      android:interpolator="@interpolator/mtrl_fast_out_slow_in"/>
</set>

繼續看源碼里的方法,其中用到了一個類MotionSpec,用來存儲上邊xml里的動畫屬性的,如下

  public static MotionSpec createFromResource(Context context, @AnimatorRes int id) {
    try {
      Animator animator = AnimatorInflater.loadAnimator(context, id);
      if (animator instanceof AnimatorSet) {
        AnimatorSet set = (AnimatorSet) animator;
        return createSpecFromAnimators(set.getChildAnimations());
      } else if (animator != null) {
        List<Animator> animators = new ArrayList<>();
        animators.add(animator);
        return createSpecFromAnimators(animators);
      } else {
        return null;
      }
    } catch (Exception e) {
      return null;
    }
  }

然后是數據的設置

  private AnimatorSet createShrinkExtendAnimator(@NonNull MotionSpec spec, boolean shrinking) {
    int collapsedSize = ViewCompat.getPaddingStart(this) * 2 + getIconSize();
//收縮的時候,控件的大小就是icon的size加上2倍的paddingStart
//至于展開的大小,就是下邊getMeasuredWidth了,上邊動畫開始前進行了測量,用的unspecified
    if (spec.hasPropertyValues("width")) {
      PropertyValuesHolder[] widthValues = spec.getPropertyValues("width");
      if (shrinking) {
        widthValues[0].setFloatValues(getMeasuredWidth(), collapsedSize);
      } else {
//valueanimator都知道,需要一個起始和結束值,下邊2個參數就是
        widthValues[0].setFloatValues(getWidth(), getMeasuredWidth());
      }
      spec.setPropertyValues("width", widthValues);
    }

    if (spec.hasPropertyValues("height")) {
      PropertyValuesHolder[] heightValues = spec.getPropertyValues("height");
      if (shrinking) {
        heightValues[0].setFloatValues(getMeasuredHeight(), collapsedSize);
      } else {
        heightValues[0].setFloatValues(getHeight(), getMeasuredHeight());
      }
      spec.setPropertyValues("height", heightValues);
    }

    if (spec.hasPropertyValues("cornerRadius")) {
      PropertyValuesHolder[] cornerRadiusValues = spec.getPropertyValues("cornerRadius");
      if (shrinking) {
        cornerRadiusValues[0].setFloatValues(getCornerRadius(), getAdjustedRadius(collapsedSize));
      } else {
        cornerRadiusValues[0].setFloatValues(getCornerRadius(), getAdjustedRadius(getHeight()));
      }
      spec.setPropertyValues("cornerRadius", cornerRadiusValues);
    }

    return createAnimator(spec);
  }

繼續看,可以看到這個控件支持5種屬性變化,源碼里可以看到默認的動畫,沒有opacity和scale,如果想支持,可以自己寫個動畫文件。而且系統默認的動畫,高度是wrap的,至于寬度,收縮的時候就是icon大小+2paddding,展開的時候就是wrap大小了。

  private AnimatorSet createAnimator(@NonNull MotionSpec spec) {
    List<Animator> animators = new ArrayList<>();

    if (spec.hasPropertyValues("opacity")) {
      animators.add(spec.getAnimator("opacity", this, View.ALPHA));
    }

    if (spec.hasPropertyValues("scale")) {
      animators.add(spec.getAnimator("scale", this, View.SCALE_Y));
      animators.add(spec.getAnimator("scale", this, View.SCALE_X));
    }

    if (spec.hasPropertyValues("width")) {
      animators.add(spec.getAnimator("width", this, WIDTH));
    }

    if (spec.hasPropertyValues("height")) {
      animators.add(spec.getAnimator("height", this, HEIGHT));
    }

    if (spec.hasPropertyValues("cornerRadius")) {
      animators.add(spec.getAnimator("cornerRadius", this, CORNER_RADIUS));
    }

    AnimatorSet set = new AnimatorSet();
    AnimatorSetCompat.playTogether(set, animators);
    return set;
  }

上邊源碼分析完了,也知道,寬,高,角度,源碼里自己處理,這個我們沒有可操作的空間,而透明度和拉伸,系統沒處理,我們可以自己寫,動畫時間也可以自己處理下。

        app:extendMotionSpec="@animator/mtrl_extended_fab_extend_motion_spec2"
        app:shrinkMotionSpec="@animator/mtrl_extended_fab_shrink_motion_spec2"

寫在布局里就行,當然了代碼里也可以的。
res下新建個animator目錄,添加下邊的文件即可

<set xmlns:android="http://schemas.android.com/apk/res/android">
  <objectAnimator
      android:duration="2000"
      android:interpolator="@interpolator/mtrl_fast_out_slow_in"
      android:propertyName="opacity"
      android:startOffset="0"
      android:valueFrom="0.1"
      android:valueTo="1"
      android:valueType="floatType" />
  <objectAnimator
      android:duration="2000"
      android:interpolator="@interpolator/mtrl_fast_out_slow_in"
      android:propertyName="scale"
      android:startOffset="0"
      android:valueFrom="0.1"
      android:valueTo="1"
      android:valueType="floatType" />
  <objectAnimator
      android:propertyName="width"
      android:startOffset="0"
      android:duration="2000"
      android:interpolator="@interpolator/mtrl_fast_out_slow_in"/>
  <objectAnimator
      android:propertyName="height"
      android:startOffset="0"
      android:duration="2000"
      android:interpolator="@interpolator/mtrl_fast_out_slow_in"/>
  <objectAnimator
      android:propertyName="cornerRadius"
      android:startOffset="0"
      android:duration="2000"
      android:interpolator="@interpolator/mtrl_fast_out_slow_in"/>
</set>

話說這玩意是繼承

4 MaterialButton的

而public class MaterialButton extends AppCompatButton implements Checkable
簡單再看下,都有哪些屬性
主要就是多了個icon的屬性,可以在左邊加個圖片,單色圖片,如果你是彩色的圖片,最后看到的就是一片白或黑【和白天黑夜模式有關,如果你沒有設iconTint的話】
至于material的通性就不細究了,比如ripple,陰影,邊界,圓角等
iconTint:圖片染色用的
注意事項
button以前的android:background屬性無效了。現在設置背景只能通過app:background,只能是個顏色,可以是帶狀態的顏色。

    <style name="Widget.MaterialComponents.Button" parent="Widget.AppCompat.Button">
    <item name="enforceMaterialTheme">true</item>
    <item name="enforceTextAppearance">true</item>
    <item name="android:textAppearance">?attr/textAppearanceButton</item>
    <item name="android:textColor">@color/mtrl_btn_text_color_selector</item>
    <item name="android:paddingLeft">@dimen/mtrl_btn_padding_left</item>
    <item name="android:paddingRight">@dimen/mtrl_btn_padding_right</item>
    <item name="android:paddingTop">@dimen/mtrl_btn_padding_top</item>
    <item name="android:paddingBottom">@dimen/mtrl_btn_padding_bottom</item>
    <item name="android:insetLeft">0dp</item>
    <item name="android:insetRight">0dp</item>
    <item name="android:insetTop">@dimen/mtrl_btn_inset</item>
    <item name="android:insetBottom">@dimen/mtrl_btn_inset</item>
    <item name="android:stateListAnimator" ns1:ignore="NewApi">@animator/mtrl_btn_state_list_anim</item>
    <item name="cornerRadius">@null</item>
    <item name="elevation">@dimen/mtrl_btn_elevation</item>
    <item name="iconPadding">@dimen/mtrl_btn_icon_padding</item>
    <item name="iconTint">@color/mtrl_btn_text_color_selector</item>
    <item name="rippleColor">@color/mtrl_btn_ripple_color</item>
    <item name="backgroundTint">@color/mtrl_btn_bg_color_selector</item>
    <item name="shapeAppearance">?attr/shapeAppearanceSmallComponent</item>
  </style>

效果


image.png

具體添加了哪些屬性,可以看下源碼

    <declare-styleable name="MaterialButton">
    <!-- Whether the button can be checked. -->
    <attr name="android:checkable"/>
    <attr name="android:insetLeft"/>
    <attr name="android:insetRight"/>
    <attr name="android:insetTop"/>
    <attr name="android:insetBottom"/>
    <!-- Background for the MaterialButton -->
    <attr name="backgroundTint"/>
    <attr name="backgroundTintMode"/>
    <!-- Elevation for the MaterialButton. -->
    <attr name="elevation"/>
    <!-- Icon drawable to display at the start of this view. -->
    <attr format="reference" name="icon"/>
    <!-- Specifies the width and height to use for the icon drawable. -->
    <attr format="dimension" name="iconSize"/>
    <!-- Padding between icon and button text. -->
    <attr format="dimension" name="iconPadding"/>
    <!-- Specifies how the icon should be positioned on the X axis. -->
    <attr name="iconGravity">
      <!-- Push icon to the start of the button. -->
      <flag name="start" value="0x1"/>
      <!-- Push the icon to the start of the text keeping a distance equal to
           {@link R.attr#iconPadding} from the text. -->
      <flag name="textStart" value="0x2"/>
    </attr>
    <!-- Tint for icon drawable to display. -->
    <attr format="color" name="iconTint"/>
    <!-- Tint mode for icon drawable to display. -->
    <attr name="iconTintMode"/>
    <!-- Shape appearance style reference for MaterialButton. Attribute declaration is in the Shape
     package. -->
    <attr name="shapeAppearance"/>
    <!-- Shape appearance overlay style reference for MaterialButton. To be used to augment
         attributes declared in the shapeAppearance. Attribute declaration is in the Shape package.
    -->
    <attr name="shapeAppearanceOverlay"/>
    <!-- Specifies the color used to draw the path outline of the button. Attribute type definition
         is in resources package. -->
    <attr name="strokeColor"/>
    <!-- Width of the stroke path of the button. Default is 0. Attribute type definition is in
         resources package. -->
    <attr name="strokeWidth"/>
    <!--
        Specifies the radius for the corners of the button. Default is 0, for non-rounded corners.
    -->
    <attr format="dimension" name="cornerRadius"/>
    <!-- Ripple color for the button. This may be a color state list, if the desired ripple color
         should be stateful. Attribute type definition is in resources package. -->
    <attr name="rippleColor"/>
  </declare-styleable>

使用中問題

  1. 文字比較大的時候發現間距也很大
    如下 上邊的button和下邊的textview的字體大小顏色是一樣的,結果明顯上邊的字體間距大


    image.png

    查下源碼,看下materialButton的默認字體,可以看到它設置了fontFamily,而且還設置了
    android:letterSpacing

    <style name="TextAppearance.MaterialComponents.Button" parent="Base.TextAppearance.MaterialComponents.Button">
    <!-- Roboto Medium was added in this api level -->
    <item name="fontFamily">sans-serif-medium</item>
    <item name="android:fontFamily">sans-serif-medium</item>
    <item name="android:textStyle">normal</item>
  </style>

    <style name="Base.TextAppearance.MaterialComponents.Button" parent="TextAppearance.AppCompat.Button">
    <!-- Fake Roboto Medium. -->
    <item name="fontFamily">sans-serif-medium</item>
    <item name="android:fontFamily">sans-serif-medium</item>
    <item name="android:textStyle">bold</item>
    <item name="android:textAllCaps">true</item>
    <item name="android:textSize">14sp</item>
    <item name="android:letterSpacing">0.0892857143</item>
  </style>

要和textview顯示的文字一樣,button里添加如下的屬性

        android:fontFamily=""
        android:letterSpacing="0"
  1. button的高度看著小很多
    xml布局里可以修改這兩個值,弄小點就ok了
    <item name="android:insetTop">@dimen/mtrl_btn_inset</item>
    <item name="android:insetBottom">@dimen/mtrl_btn_inset</item>

5 MaterialButtonToggleGroup

3個屬性可以設置,
app:singleSelection="true" //是否單選
app:checkedButton="@+id/mbtn1"http://默認選中的button id
app:selectionRequired="true"http://Sets whether we prevent all child buttons from being deselected. 是否阻止所有的child取消選擇,可以防止單選的時候選中那個再點一下取消選擇了.
public class MaterialButtonToggleGroup extends LinearLayout

實際使用,看名字就知道,它的child只支持MaterialButton,源碼addView里能看到限制

    <com.google.android.material.button.MaterialButtonToggleGroup
        android:id="@+id/mbtg"
        app:singleSelection="true"
        app:checkedButton="@+id/mbtn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <com.google.android.material.button.MaterialButton
            android:layout_width="wrap_content"
            android:id="@+id/mbtn1"
            android:text="btn1"
            android:checkable="true"
            android:textColor="@color/txt_black_white"
            app:backgroundTint="#FF9800"
            android:layout_height="wrap_content" />

6 MaterialCardView

public class MaterialCardView extends CardView implements Checkable
比以前的CardView多個icon

    <com.google.android.material.card.MaterialCardView
        android:id="@+id/mcv"
        app:rippleColor="#CDDC39"
        app:strokeWidth="2dp"
        app:strokeColor="#E91E63"
        app:cardBackgroundColor="#FFC107"
        app:cardCornerRadius="10dp"
        android:checkable="true"
        android:clickable="true"
        android:focusable="true"
        app:checkedIcon="@drawable/iv_leaf_1"
        app:checkedIconTint="#673AB7"
        app:contentPadding="10dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
image.png

check狀態生效并是圖片顯示出來,需要設置這兩種

        android:checkable="true"
        android:clickable="true"

setChecked(true)可以看到圖片是在右上角的


image.png

簡單源碼分析,布局過程

public class MaterialCardView extends CardView implements Checkable {
  public MaterialCardView(Context context, AttributeSet attrs, int defStyleAttr) {
//CardView 本來就是個FrameLayout,這里又new了一個添加進來
//特殊處理都放在helper里了
    contentLayout = new FrameLayout(context);
//這里調用的super是正常添加方法,它自己本身的addview重寫了,
    super.addView(contentLayout, -1, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
    cardViewHelper = new MaterialCardViewHelper(this, attrs, defStyleAttr, DEF_STYLE_RES);

}

  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    cardViewHelper.onMeasure(getMeasuredWidth(), getMeasuredHeight());
  }
//可以看到自己本該add的view,都添加到我們new的那個FrameLayout里了
  public void addView(View child, int index, ViewGroup.LayoutParams params) {
    contentLayout.addView(child, index, params);
  }

  @Override
  public void removeAllViews() {
    contentLayout.removeAllViews();
  }

  @Override
  public void removeView(View view) {
    contentLayout.removeView(view);
  }

看下helper里的方法

  void onMeasure(int measuredWidth, int measuredHeight) {
    if (materialCardView.isCheckable() && clickableForegroundDrawable != null) {
      Resources resources = materialCardView.getResources();
      // TODO: support custom sizing
      int margin = resources.getDimensionPixelSize(R.dimen.mtrl_card_checked_icon_margin);
      int size = resources.getDimensionPixelSize(R.dimen.mtrl_card_checked_icon_size);
      int left = measuredWidth - margin - size;
      int bottom = measuredHeight - margin - size;
      int right = margin;
      if (ViewCompat.getLayoutDirection(materialCardView) == View.LAYOUT_DIRECTION_RTL) {
        // swap left and right
        int tmp = right;
        right = left;
        left = tmp;
      }

//CHECKED_ICON_LAYER_INDEX這個索引就是那個icon圖層的索引
//看下4個方向的偏移量就知道了是在右上角的,偏移了margin以及icon的size距離
      clickableForegroundDrawable.setLayerInset(
          CHECKED_ICON_LAYER_INDEX, left, margin /* top */, right, bottom);
    }
  }

簡單看下上邊用到的drawable
3層,ripple,foregroundContent,以及checked icon[索引是2也就是CHECKED_ICON_LAYER_INDEX]

    if (clickableForegroundDrawable == null) {
      Drawable checkedLayer = createCheckedIconLayer();
      clickableForegroundDrawable =
          new LayerDrawable(
              new Drawable[] {rippleDrawable, foregroundContentDrawable, checkedLayer});
      clickableForegroundDrawable.setId(CHECKED_ICON_LAYER_INDEX, R.id.mtrl_card_checked_layer_id);
    }
實際測試中,這玩意有bug

測試條件,在修改這個CardView的setChecked狀態的同時,調用了同級組件ExtendedFloatingActionButton的extend或者shrink方法,會發現check圖片顯示異常,cardview的大小也越來越大。如下


image.png

7 ChipGroup

public class ChipGroup extends FlowLayout
顧名思義,就是個容器,流式布局,一行裝不下就換行
看下有撒屬性

    <com.google.android.material.chip.ChipGroup
        android:id="@+id/chip_group"
        android:layout_width="500dp"
        android:layout_height="wrap_content"
        app:checkedChip="@+id/cp1"
        app:chipSpacing="20dp"
        app:chipSpacingHorizontal="20dp"
        app:chipSpacingVertical="10dp"
        app:itemSpacing="80dp"
        app:lineSpacing="50dp"
        app:singleLine="false"
        app:singleSelection="true">

app:chipSpacingHorizontal:就是item之間的距離
app:chipSpacingVertical:每行之間的間隔
app:checkedChip="@+id/cp1" //默認選中的chip id
app:singleLine="false" //是否是單行
app:singleSelection="true"http://是否是單選 ,它的child都是Chip 也就是checkbox
app:selectionRequired="true"http://如果單選的 時候最少得有一個,需要加上這個
app:itemSpacing="80dp"
app:lineSpacing="50dp"
這兩個是父類FlowLayout的屬性,在這里是無效的,被chipSpacing替換掉了

image.png

看下這個就知道為啥上邊的itemSpacing和lineSpacing無效了


image.png

8 Chip

public class Chip extends AppCompatCheckBox implements Delegate, Shapeable
先看下
第一個Chip 是設置了chipIcon 文字左邊的,以及closeIcon,文字右邊
最后一個是系統默認的選中狀態,以及默認的close icon。其他是啥都沒設置的
如果設置了chipIcon又設置了可以選中,那么選中的對號會蓋在chipIcon上的


image.png

image.png

這個控件默認checkable是false的,如果需要選中狀態,那么得手動設置checkable為true,
其他屬性看名字大概就知道干啥的。

        <com.google.android.material.chip.Chip
            android:id="@+id/cp1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:checkable="true"
            android:text="green"
            app:checkedIconEnabled="true"
            app:checkedIconVisible="true"http://是否需要那個對號
            app:chipBackgroundColor="#2196F3"
            app:chipCornerRadius="10dp"http://修改圓角半徑的,默認左右是半圓
            app:chipEndPadding="10dp"
            app:chipIcon="@drawable/iv_leaf_3"
            app:chipIconVisible="true"http://文字前邊顯示的圖片
            app:chipStartPadding="10dp"
            app:chipStrokeColor="#E91E63"
            app:chipStrokeWidth="2dp"
            app:closeIcon="@drawable/ic_capture_delete"http://后邊那個叉號
            app:closeIconEnabled="true" />

如果需要這種,默認就個邊框,選中帶背景的,可以這樣設置style
因為這東西其實是個checkbox也就是button,所以有個灰色的背景的,修改android:backgroundTint即可

    <style name="chip_test_style">
        <item name="android:checkable">true</item>
        <item name="checkedIconVisible">false</item>
        <item name="chipCornerRadius">10dp</item>
        <item name="chipStrokeWidth">1dp</item>
        <item name="chipStrokeColor">@color/default_selector_stroke_color</item>
        <item name="android:backgroundTint">@color/default_selector_action_bg</item>
        <item name="android:textColor">@color/default_selector_text_color</item>
    </style>

測試的時候還發現有兩個chip開頭的屬性,可感覺沒啥用啊,只是測試surfaceColor會蓋住backgroundColor
可這兩種都會被android:backgroundTint影響啊,那我要這兩個干啥?

chipBackgroundColor
chipSurfaceColor
使用中碰到的問題

studio 3.5已修復此問題
只有布局里代碼MaterialButton或者它的子類,預覽大部分時候是掛掉的,偶爾是好的。
日志如下

java.lang.IllegalArgumentException: java.lang.ClassCastException@7c1e37a5
    at sun.reflect.GeneratedMethodAccessor4200.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at android.animation.PropertyValuesHolder_Delegate.callMethod(PropertyValuesHolder_Delegate.java:108)
    at android.animation.PropertyValuesHolder_Delegate.nCallFloatMethod(PropertyValuesHolder_Delegate.java:143)
    at android.animation.PropertyValuesHolder.nCallFloatMethod(PropertyValuesHolder.java)
    at android.animation.PropertyValuesHolder.access$400(PropertyValuesHolder.java:38)
    at android.animation.PropertyValuesHolder$FloatPropertyValuesHolder.setAnimatedValue(PropertyValuesHolder.java:1387)
    at android.animation.ObjectAnimator.animateValue(ObjectAnimator.java:990)
    at android.animation.ValueAnimator.setCurrentFraction(ValueAnimator.java:674)
    at android.animation.ValueAnimator.setCurrentPlayTime(ValueAnimator.java:637)
    at android.animation.ValueAnimator.start(ValueAnimator.java:1069)
    at android.animation.ValueAnimator.start(ValueAnimator.java:1088)
    at android.animation.ObjectAnimator.start(ObjectAnimator.java:852)
    at android.animation.ValueAnimator.startWithoutPulsing(ValueAnimator.java:1081)
    at android.animation.AnimatorSet.handleAnimationEvents(AnimatorSet.java:1142)
    at android.animation.AnimatorSet.startAnimation(AnimatorSet.java:1227)
    at android.animation.AnimatorSet.start(AnimatorSet.java:729)
    at android.animation.AnimatorSet.start(AnimatorSet.java:684)
    at android.animation.StateListAnimator.start(StateListAnimator.java:188)
    at android.animation.StateListAnimator.setState(StateListAnimator.java:181)
    at android.view.View.drawableStateChanged(View.java:21105)
    at android.widget.TextView.drawableStateChanged(TextView.java:5283)
    at androidx.appcompat.widget.AppCompatButton.drawableStateChanged(AppCompatButton.java:156)
  1. 發現個異常的地方
    前提條件:
    線性布局,水平方向的,然后里邊添加MaterialButton和TextView,高度一樣,結果textview的高度不正常最后發現不是高度不正常,是位置不正常,打印了下textview的日志,發現top是40,bottom是140.一部分跑到屏幕下邊去了。
    看效果圖以及代碼
    image.png
   <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="50dp"
        android:layout_marginTop="10dp"
        android:background="#ffffff"
        android:orientation="horizontal">

        <com.google.android.material.button.MaterialButton
            android:id="@+id/btn_a"
            android:layout_width="200dp"
            android:layout_height="100dp"
            android:text="hello"
 />
        <TextView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:background="#00BCD4"
            android:text="what" />

        <TextView
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:background="#993345"
            android:text="ddd" />
    </LinearLayout>

又添加一個wrap_content的Button,如下,感覺他們是文字對齊啊?


image.png

看下線性布局水平方向的onLayout方法
看下child的top獲取方法,如下,默認的重心就是Top了。

                    case Gravity.TOP:
                        childTop = paddingTop + lp.topMargin;
                        if (childBaseline != -1) {
                            childTop += maxAscent[INDEX_TOP] - childBaseline;
                        }
                        break;

通過反射打印了下數組 [-1, 55, -1, -1], 而Index_top 是1也就是55了
然后看下幾個view的baseLine
第一個button是55,第二個是29,第三個textView是15,和最終的layout位置符合。這里就說明了為啥結果是這樣了。
而這個數組的賦值是在下邊的方法里,循環child,找到最大的baseLine賦值給數組。

 void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec)

解決辦法很簡單了,讓TextView的gravity為center即可。

9 BottomAppBar

看名字,這個是顯示在底部用的,是個Toolbar,主要用來和FloatingActionBar一起的

<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

  <!-- Other components and views -->

  <com.google.android.material.bottomappbar.BottomAppBar
      android:id="@+id/bar"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_gravity="bottom"
      app:navigationIcon="@drawable/ic_menu_24"/>

  <com.google.android.material.floatingactionbutton.FloatingActionButton
      android:id="@+id/fab"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      app:layout_anchor="@id/bar"/>

</android.support.design.widget.CoordinatorLayout>

上邊是默認的設置,看下效果


image.png

打開構造方法,找到默認的style,看下默認的值先

    <style name="Widget.MaterialComponents.BottomAppBar" parent="Widget.AppCompat.Toolbar">
    <item name="enforceMaterialTheme">true</item>
    <item name="backgroundTint">?attr/colorSurface</item>
    <item name="fabCradleMargin">@dimen/mtrl_bottomappbar_fab_cradle_margin</item>
    <item name="fabCradleRoundedCornerRadius">
      @dimen/mtrl_bottomappbar_fab_cradle_rounded_corner_radius
    </item>
    <item name="fabCradleVerticalOffset">@dimen/mtrl_bottomappbar_fab_cradle_vertical_offset</item>
    <item name="android:minHeight">@dimen/mtrl_bottomappbar_height</item>
    <item name="maxButtonHeight">@dimen/mtrl_bottomappbar_height</item>
    <item name="elevation">8dp</item>
  </style>

    <dimen name="mtrl_bottomappbar_fab_cradle_margin">5dp</dimen>
<dimen name="mtrl_bottomappbar_fab_cradle_rounded_corner_radius">8dp</dimen>
<dimen name="mtrl_bottomappbar_fab_cradle_vertical_offset">0dp</dimen>
image.png

特殊屬性就是為了設置fab的位置的

            app:menu="@menu/menu_nav_bottom"http://和toolbar一樣設置item的
            app:backgroundTint="#673AB7"http://背景色
            app:fabAlignmentMode="end"http://fab的位置,默認居中的,這個end是在右邊
            app:fabAnimationMode="slide"http://
            app:fabCradleVerticalOffset="0dp"
            app:fabCradleMargin="20dp"
            app:fabCradleRoundedCornerRadius="10dp"
            app:navigationIcon="@drawable/iv_leaf_1"http://最左邊那個圖片
 app:hideOnScroll="true"http://頁面滾動的時候這個appbar會自動隱藏顯示的

app:fabCradleVerticalOffset
這個是fab的中心位置和appbar的top位置的距離,默認是0,fab的中心剛好在top上,修改這個可以修改fab的位置
app:fabCradleMargin
這個就是上圖那個紫色箭頭的距離 ,就是fab和appbar之間的留白距離,也可以理解為appbar上那個半圓的半徑位置就是fab的半徑加上這個margin
app:fabCradleRoundedCornerRadius
這個就是上圖那個淺藍色框起來的圓弧半徑。

復制的英文解釋
The starting alignment mode (fabAlignmentMode) can be set to either center or end. Changing the fabCradleMargin will increase or decrease the distance between the FloatingActionButton and the BottomAppBar. The fabCradleRoundedCornerRadius specifies the roundness of the corner around the cutout. The fabCradleVerticalOffset specifies the vertical offset between the FloatingActionButton and the BottomAppBar. If fabCradleVerticalOffset is 0, the center of the FloatingActionButton will be aligned with the top of the BottomAppBar.

item的點擊事件處理

  1. xml里添加menu
app:menu="@menu/menu_nav_bottom"
        bar.setOnMenuItemClickListener {
           
            return@setOnMenuItemClickListener true
        }
  1. 設置為actionbar,系統處理
    上邊說了這個就是個toolbar,你也可以按照以前的調用setsupportActionBar(bar),
    然后下邊2個方法加載menu,以及處理menu的點擊事件了
    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        menuInflater.inflate(R.menu.article_add,menu)
        return super.onCreateOptionsMenu(menu)
    }
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
       
        return super.onOptionsItemSelected(item)
    }

其他,源碼如下,可以看到,不支持title和subTitle

  public void setTitle(CharSequence title) {
    // Don't do anything. BottomAppBar can't have a title.
  }

  @Override
  public void setSubtitle(CharSequence subtitle) {
    // Don't do anything. BottomAppBar can't have a subtitle.
  }

checkbox 里的button屬性染色

    <CheckBox
        android:id="@+id/iv_volume"
        android:layout_width="wrap_content"
        android:layout_height="70dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:button="@drawable/ic_volume"
        android:buttonTint="@android:color/holo_red_dark" />

補充知識

前提,我們的主題用的是
影響是代碼里的Button,CheckBox等基礎控件,運行的時候自動就會被轉換成對應的MaterialXXX了,所以有時候狀態可能就不對了,button上邊講了這里是沒研究 的

<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">

1.CheckBox
如代碼,我們自定義了下button的圖片,可結果和我們的不一樣

        <CheckBox
            android:button="@drawable/checkbox"

未選中是白框,選中是黑色對號


image.png

可運行起來效果就不對了,沒選中是灰框,選中竟然是純色


image.png

image.png

解決辦法

不要系統系統的主題,因為使用了以后它就自動給我們染色了,我們的select狀態圖片是純色的,所以被染色了以后就成主題色了

<com.google.android.material.checkbox.MaterialCheckBox
            app:useMaterialThemeColors="false"

看下源碼,這里要知道使用了MaterialComponents以后自動就變成了MaterialCheckBox了
默認的主題,useMaterialThemeColors為true

    <style name="Widget.MaterialComponents.CompoundButton.CheckBox" parent="Widget.AppCompat.CompoundButton.CheckBox">
    <item name="enforceMaterialTheme">true</item>
    <item name="useMaterialThemeColors">true</item>
    <item name="android:minWidth">?attr/minTouchTargetSize</item>
    <item name="android:minHeight">?attr/minTouchTargetSize</item>
  </style>

代碼

//默認使用的主題就是上邊的,所以這個為true
    useMaterialThemeColors =
        attributes.getBoolean(R.styleable.MaterialCheckBox_useMaterialThemeColors, false);

  protected void onAttachedToWindow() {
    super.onAttachedToWindow();

//我們沒有設置buttonTint
    if (useMaterialThemeColors && CompoundButtonCompat.getButtonTintList(this) == null) {
      setUseMaterialThemeColors(true);//就是給button染色
    }
  }

補充

先復習下之前的圓形漸變動畫,對任意view有效
https://developer.android.com/training/animation/reveal-or-hide-view#Reveal

    fun test(){
        val myView=anyView
        // Check if the runtime version is at least Lollipop
            // get the center for the clipping circle
            val cx = myView.width / 2
            val cy = myView.height / 2

            // get the final radius for the clipping circle
            val finalRadius = Math.hypot(cx.toDouble(), cy.toDouble()).toFloat()

            // create the animator for this view (the start radius is zero)
            val anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0f, finalRadius)
            // make the view visible and start the animation
            myView.visibility = View.VISIBLE
            anim.start()
    }

然后看下材料庫里的這些類

com.google.android.material.circularreveal.CircularRevealFrameLayout
com.google.android.material.circularreveal.CircularRevealGridLayout
com.google.android.material.circularreveal.CircularRevealLinearLayout
com.google.android.material.circularreveal.CircularRevealRelativeLayout
com.google.android.material.circularreveal.cardview.CircularRevealCardView
com.google.android.material.circularreveal.coordinatorlayout.CircularRevealCoordinatorLayout

其實就是對應的viewgroup實現了CircularRevealWidget接口
動畫的使用也非常簡單

CircularRevealCompat.createCircularReveal(CircularRevealView,90f,90f,1f,100f).start()

參數的意思如下:

  public static Animator createCircularReveal(
      CircularRevealWidget view, float centerX, float centerY, float startRadius, float endRadius) 

//或者這種,不過這種view需要提前設置一個RevealInfo來獲取startRadius
  public static Animator createCircularReveal(
      @NonNull CircularRevealWidget view, float centerX, float centerY, float endRadius) 
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 《裕語言》速成開發手冊3.0 官方用戶交流:iApp開發交流(1) 239547050iApp開發交流(2) 10...
    葉染柒丶閱讀 27,859評論 5 19
  • 在iOS中隨處都可以看到絢麗的動畫效果,實現這些動畫的過程并不復雜,今天將帶大家一窺iOS動畫全貌。在這里你可以看...
    F麥子閱讀 5,141評論 5 13
  • 問答題47 /72 常見瀏覽器兼容性問題與解決方案? 參考答案 (1)瀏覽器兼容問題一:不同瀏覽器的標簽默認的外補...
    _Yfling閱讀 13,807評論 1 92
  • 1 CALayer IOS SDK詳解之CALayer(一) http://doc.okbase.net/Hell...
    Kevin_Junbaozi閱讀 5,210評論 3 23
  • Swift1> Swift和OC的區別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,144評論 1 32