Skip to content

Commit 1666fbc

Browse files
leticiarossidrchen
authored andcommittedAug 4, 2022
[Checkbox] Updated checkbox to M3 look.
The checkbox is now composed of an `app:buttonCompat` button drawable (the squared icon) and an `app:buttonIcon` icon drawable (the checkmark icon) layered on top of it. The animation has also been updated. PiperOrigin-RevId: 462244130 (cherry picked from commit 8fae47a)
1 parent 00c62d2 commit 1666fbc

18 files changed

+965
-126
lines changed
 

‎docs/components/Checkbox.md

+15-5
Original file line numberDiff line numberDiff line change
@@ -147,17 +147,22 @@ checkbox.setOnCheckedChangeListener { buttonView, isChecked ->
147147

148148
### Checkbox attributes
149149

150+
The checkbox is composed of an `app:buttonCompat` button drawable (the squared
151+
icon) and an `app:buttonIcon` icon drawable (the checkmark icon) layered on top
152+
of it.
153+
150154
Element | Attribute | Related method(s) | Default value
151155
-------------------------- | ------------------------------------------ | ---------------------------------------------------------- | -------------
152156
**To use material colors** | `app:useMaterialThemeColors` | `setUseMaterialThemeColors`<br/>`isUseMaterialThemeColors` | `true` (ignored if `app:buttonTint` is set)
153-
**Color** | `app:buttonTint` | `setButtonTintList`<br/>`getButtonTintList` | `?attr/colorOnSurface` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/checkbox/res/color/m3_checkbox_button_tint.xml))
157+
**Button drawable** | `app:buttonCompat` | `setButtonDrawable`<br/>`getButtonDrawable` | [@mtrl_checkbox_button](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/checkbox/res/drawable/mtrl_checkbox_button.xml)
158+
**Button tint** | `app:buttonTint` | `setButtonTintList`<br/>`getButtonTintList` | `?attr/colorOnSurface` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/checkbox/res/color/m3_checkbox_button_tint.xml))
159+
**Button icon drawable** | `app:buttonIcon` | `setButtonIconDrawable`<br/>`getButtonIconDrawable` | [@mtrl_checkbox_button_icon](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/checkbox/res/drawable/mtrl_checkbox_button_icon.xml)
160+
**Button icon tint** | `app:buttonIconTint` | `setButtonIconTintList`<br/>`getButtonIconTintList` | `?attr/colorOnPrimary` (see all [states](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/checkbox/res/color/m3_checkbox_button_icon_tint.xml))
154161
**Min size** | `android:minWidth`<br/>`android:minHeight` | `(set/get)MinWidth`<br/>`(set/get)MinHeight` | `?attr/minTouchTargetSize`
155162
**Centered icon if no text** | `app:centerIfNoTextEnabled` | `setCenterIfNoTextEnabled`<br/>`isCenterIfNoTextEnabled` | `true`
156163

157-
The color of the checkbox defaults to `?attr/colorOnSurface` (unchecked) and
158-
`?attr/colorPrimary` (checked) defined in your app theme. If you want to
159-
override this behavior, as you might with a custom drawable that should not be
160-
tinted, set `app:useMaterialThemeColors` to `false`:
164+
**Note:** If you'd like to change the default colors, override the above tint
165+
attributes or set them to `@null` and `app:useMaterialThemeColors` to `false`.
161166

162167
```xml
163168
<CheckBox
@@ -166,6 +171,11 @@ tinted, set `app:useMaterialThemeColors` to `false`:
166171
/>
167172
```
168173

174+
**Note:** If setting a custom `app:buttonCompat`, make sure to also set
175+
`app:buttonIcon` if an icon is desired. The checkbox does not support having a
176+
custom `app:buttonCompat` and preserving the default `app:buttonIcon` checkmark
177+
at the same time.
178+
169179
### Text label attributes
170180

171181
Element | Attribute | Related method(s) | Default value

‎lib/java/com/google/android/material/checkbox/MaterialCheckBox.java

+312-18
Large diffs are not rendered by default.

‎lib/java/com/google/android/material/checkbox/res-public/values/public.xml

+3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
<resources>
2020
<public name="Widget.Material3.CompoundButton.CheckBox" type="style"/>
2121

22+
<public name="buttonIcon" type="attr"/>
23+
<public name="buttonIconTint" type="attr"/>
24+
<public name="buttonIconTintMode" type="attr"/>
2225
<public name="centerIfNoTextEnabled" type="attr"/>
2326
<public name="errorShown" type="attr"/>
2427
<public name="errorAccessibilityLabel" type="attr"/>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
~ Copyright (C) 2022 The Android Open Source Project
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ http://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
<selector xmlns:android="http://schemas.android.com/apk/res/android"
18+
xmlns:app="http://schemas.android.com/apk/res-auto">
19+
<!-- Disabled -->
20+
<item android:color="?attr/colorSurface" android:state_enabled="false"/>
21+
22+
<!-- Error -->
23+
<item android:color="?attr/colorOnError" app:state_error="true"/>
24+
25+
<!-- Checked -->
26+
<item android:color="?attr/colorOnPrimary" android:state_checked="true"/>
27+
28+
<!-- Unchecked -->
29+
<item android:color="?attr/colorOnPrimary" android:state_checked="false"/>
30+
</selector>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
~ Copyright (C) 2022 The Android Open Source Project
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ http://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
<animated-selector xmlns:android="http://schemas.android.com/apk/res/android"
18+
xmlns:tools="http://schemas.android.com/tools"
19+
tools:ignore="NewApi">
20+
<item
21+
android:id="@+id/checked"
22+
android:drawable="@drawable/mtrl_ic_checkbox_checked"
23+
android:state_checked="true"/>
24+
<item
25+
android:id="@+id/unchecked"
26+
android:drawable="@drawable/mtrl_ic_checkbox_unchecked"
27+
android:state_checked="false"/>
28+
29+
<transition
30+
android:fromId="@+id/checked"
31+
android:toId="@+id/unchecked"
32+
android:drawable="@drawable/mtrl_checkbox_button_checked_unchecked" />
33+
<transition
34+
android:fromId="@+id/unchecked"
35+
android:toId="@+id/checked"
36+
android:drawable="@drawable/mtrl_checkbox_button_unchecked_checked" />
37+
</animated-selector>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
~ Copyright (C) 2022 The Android Open Source Project
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ http://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
18+
xmlns:aapt="http://schemas.android.com/aapt"
19+
xmlns:tools="http://schemas.android.com/tools"
20+
android:drawable="@drawable/mtrl_ic_checkbox_checked"
21+
tools:ignore="NewApi">
22+
<target android:name="@string/mtrl_checkbox_button_path_group_name">
23+
<aapt:attr name="android:animation">
24+
<objectAnimator
25+
android:duration="@integer/m3_sys_motion_duration_150"
26+
android:interpolator="?attr/motionEasingEmphasizedAccelerateInterpolator"
27+
android:propertyName="scaleX"
28+
android:valueFrom="1.0"
29+
android:valueTo="0.6"
30+
android:valueType="floatType"/>
31+
</aapt:attr>
32+
</target>
33+
<target android:name="@string/mtrl_checkbox_button_path_group_name">
34+
<aapt:attr name="android:animation">
35+
<objectAnimator
36+
android:duration="@integer/m3_sys_motion_duration_150"
37+
android:interpolator="?attr/motionEasingEmphasizedAccelerateInterpolator"
38+
android:propertyName="scaleY"
39+
android:valueFrom="1.0"
40+
android:valueTo="0.6"
41+
android:valueType="floatType"/>
42+
</aapt:attr>
43+
</target>
44+
<target android:name="@string/mtrl_checkbox_button_path_name">
45+
<aapt:attr name="android:animation">
46+
<objectAnimator
47+
android:duration="@integer/m3_sys_motion_duration_50"
48+
android:interpolator="?attr/motionEasingLinearInterpolator"
49+
android:propertyName="fillAlpha"
50+
android:valueFrom="1.0"
51+
android:valueTo="0.0"
52+
android:valueType="floatType"/>
53+
</aapt:attr>
54+
</target>
55+
</animated-vector>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
~ Copyright (C) 2022 The Android Open Source Project
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ http://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
<animated-selector xmlns:android="http://schemas.android.com/apk/res/android"
18+
xmlns:tools="http://schemas.android.com/tools"
19+
tools:ignore="NewApi">
20+
<item
21+
android:id="@+id/checked"
22+
android:drawable="@drawable/mtrl_ic_check_mark"
23+
android:state_checked="true"/>
24+
<item
25+
android:id="@+id/unchecked"
26+
android:drawable="@android:color/transparent"
27+
android:state_checked="false"/>
28+
29+
<transition
30+
android:fromId="@+id/checked"
31+
android:toId="@+id/unchecked"
32+
android:drawable="@drawable/mtrl_checkbox_button_icon_checked_unchecked" />
33+
<transition
34+
android:fromId="@+id/unchecked"
35+
android:toId="@+id/checked"
36+
android:drawable="@drawable/mtrl_checkbox_button_icon_unchecked_checked" />
37+
</animated-selector>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
~ Copyright (C) 2022 The Android Open Source Project
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ http://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
18+
xmlns:aapt="http://schemas.android.com/aapt"
19+
xmlns:tools="http://schemas.android.com/tools"
20+
android:drawable="@drawable/mtrl_ic_check_mark"
21+
tools:ignore="NewApi">
22+
<target android:name="@string/mtrl_checkbox_button_icon_path_group_name">
23+
<aapt:attr name="android:animation">
24+
<objectAnimator
25+
android:duration="@integer/m3_sys_motion_duration_150"
26+
android:interpolator="?attr/motionEasingEmphasizedAccelerateInterpolator"
27+
android:propertyName="scaleX"
28+
android:valueFrom="1.0"
29+
android:valueTo="0.6"
30+
android:valueType="floatType"/>
31+
</aapt:attr>
32+
</target>
33+
<target android:name="@string/mtrl_checkbox_button_icon_path_group_name">
34+
<aapt:attr name="android:animation">
35+
<objectAnimator
36+
android:duration="@integer/m3_sys_motion_duration_150"
37+
android:interpolator="?attr/motionEasingEmphasizedAccelerateInterpolator"
38+
android:propertyName="scaleY"
39+
android:valueFrom="1.0"
40+
android:valueTo="0.6"
41+
android:valueType="floatType"/>
42+
</aapt:attr>
43+
</target>
44+
<target android:name="@string/mtrl_checkbox_button_icon_path_name">
45+
<aapt:attr name="android:animation">
46+
<objectAnimator
47+
android:duration="@integer/m3_sys_motion_duration_50"
48+
android:interpolator="?attr/motionEasingLinearInterpolator"
49+
android:propertyName="fillAlpha"
50+
android:valueFrom="1.0"
51+
android:valueTo="0.0"
52+
android:valueType="floatType"/>
53+
</aapt:attr>
54+
</target>
55+
</animated-vector>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
~ Copyright (C) 2022 The Android Open Source Project
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ http://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
18+
xmlns:aapt="http://schemas.android.com/aapt"
19+
xmlns:tools="http://schemas.android.com/tools"
20+
android:drawable="@drawable/mtrl_ic_check_mark"
21+
tools:ignore="NewApi">
22+
<target android:name="@string/mtrl_checkbox_button_icon_path_group_name">
23+
<aapt:attr name="android:animation">
24+
<objectAnimator
25+
android:duration="@integer/m3_sys_motion_duration_350"
26+
android:interpolator="?attr/motionEasingEmphasizedDecelerateInterpolator"
27+
android:propertyName="scaleX"
28+
android:valueFrom="0.6"
29+
android:valueTo="1.0"
30+
android:valueType="floatType"/>
31+
</aapt:attr>
32+
</target>
33+
<target android:name="@string/mtrl_checkbox_button_icon_path_group_name">
34+
<aapt:attr name="android:animation">
35+
<objectAnimator
36+
android:duration="@integer/m3_sys_motion_duration_350"
37+
android:interpolator="?attr/motionEasingEmphasizedDecelerateInterpolator"
38+
android:propertyName="scaleY"
39+
android:valueFrom="0.6"
40+
android:valueTo="1.0"
41+
android:valueType="floatType"/>
42+
</aapt:attr>
43+
</target>
44+
<target android:name="@string/mtrl_checkbox_button_icon_path_name">
45+
<aapt:attr name="android:animation">
46+
<objectAnimator
47+
android:duration="@integer/m3_sys_motion_duration_350"
48+
android:interpolator="?attr/motionEasingEmphasizedDecelerateInterpolator"
49+
android:propertyName="trimPathEnd"
50+
android:valueFrom="0.0"
51+
android:valueTo="1.0"
52+
android:valueType="floatType"/>
53+
</aapt:attr>
54+
</target>
55+
</animated-vector>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
~ Copyright (C) 2022 The Android Open Source Project
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ http://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
18+
xmlns:aapt="http://schemas.android.com/aapt"
19+
xmlns:tools="http://schemas.android.com/tools"
20+
android:drawable="@drawable/mtrl_ic_checkbox_checked"
21+
tools:ignore="NewApi">
22+
<target android:name="@string/mtrl_checkbox_button_path_group_name">
23+
<aapt:attr name="android:animation">
24+
<objectAnimator
25+
android:duration="@integer/m3_sys_motion_duration_350"
26+
android:interpolator="?attr/motionEasingEmphasizedDecelerateInterpolator"
27+
android:propertyName="scaleX"
28+
android:valueFrom="0.6"
29+
android:valueTo="1.0"
30+
android:valueType="floatType"/>
31+
</aapt:attr>
32+
</target>
33+
<target android:name="@string/mtrl_checkbox_button_path_group_name">
34+
<aapt:attr name="android:animation">
35+
<objectAnimator
36+
android:duration="@integer/m3_sys_motion_duration_350"
37+
android:interpolator="?attr/motionEasingEmphasizedDecelerateInterpolator"
38+
android:propertyName="scaleY"
39+
android:valueFrom="0.6"
40+
android:valueTo="1.0"
41+
android:valueType="floatType"/>
42+
</aapt:attr>
43+
</target>
44+
<target android:name="@string/mtrl_checkbox_button_path_name">
45+
<aapt:attr name="android:animation">
46+
<objectAnimator
47+
android:duration="@integer/m3_sys_motion_duration_50"
48+
android:interpolator="?attr/motionEasingLinearInterpolator"
49+
android:propertyName="fillAlpha"
50+
android:valueFrom="0.0"
51+
android:valueTo="1.0"
52+
android:valueType="floatType"/>
53+
</aapt:attr>
54+
</target>
55+
</animated-vector>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
~ Copyright (C) 2022 The Android Open Source Project
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ http://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
18+
xmlns:tools="http://schemas.android.com/tools"
19+
android:width="32dp"
20+
android:height="32dp"
21+
android:viewportWidth="32"
22+
android:viewportHeight="32"
23+
tools:ignore="NewApi">
24+
<group
25+
android:name="@string/mtrl_checkbox_button_icon_path_group_name"
26+
android:pivotX="16"
27+
android:pivotY="16">
28+
<path
29+
android:name="@string/mtrl_checkbox_button_icon_path_name"
30+
android:fillColor="@android:color/white"
31+
android:pathData="@string/mtrl_checkbox_button_icon_path_checked"/>
32+
</group>
33+
</vector>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
~ Copyright (C) 2022 The Android Open Source Project
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ http://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
18+
xmlns:tools="http://schemas.android.com/tools"
19+
android:width="32dp"
20+
android:height="32dp"
21+
android:viewportWidth="32"
22+
android:viewportHeight="32"
23+
tools:ignore="NewApi">
24+
<group>
25+
<path
26+
android:fillColor="@android:color/black"
27+
android:pathData="@string/mtrl_checkbox_button_path_unchecked"/>
28+
</group>
29+
<group
30+
android:name="@string/mtrl_checkbox_button_path_group_name"
31+
android:pivotX="16"
32+
android:pivotY="16">
33+
<path
34+
android:name="@string/mtrl_checkbox_button_path_name"
35+
android:fillColor="@android:color/black"
36+
android:pathData="@string/mtrl_checkbox_button_path_checked"/>
37+
</group>
38+
</vector>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
~ Copyright (C) 2022 The Android Open Source Project
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ http://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
18+
xmlns:tools="http://schemas.android.com/tools"
19+
android:width="32dp"
20+
android:height="32dp"
21+
android:viewportWidth="32"
22+
android:viewportHeight="32"
23+
tools:ignore="NewApi">
24+
<group>
25+
<path
26+
android:fillColor="@android:color/black"
27+
android:pathData="@string/mtrl_checkbox_button_path_unchecked"/>
28+
</group>
29+
</vector>

‎lib/java/com/google/android/material/checkbox/res/values/attrs.xml

+28-1
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,35 @@
2121
be ignored. This value should be set to false when using custom drawables
2222
that should not be tinted. This value is ignored if a buttonTint is set. -->
2323
<attr name="useMaterialThemeColors"/>
24-
<!-- Tint for the checkbox. -->
24+
<!-- Tint for the checkbox button. Set to null if using custom drawables
25+
that should not be tinted. -->
2526
<attr name="buttonTint"/>
27+
<!-- Icon of the checkbox button. -->
28+
<attr name="buttonIcon" format="reference"/>
29+
<!-- Tint for the icon of the checkbox button, if present. Set to null if
30+
using custom drawables that should not be tinted. -->
31+
<attr name="buttonIconTint" format="reference|color"/>
32+
<!-- The blending mode used to apply the tint specified by buttonIconTint
33+
to buttonIcon. The default mode is SRC_IN if not specified. -->
34+
<attr name="buttonIconTintMode">
35+
<!-- The tint is drawn on top of the drawable.
36+
[Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
37+
<enum name="src_over" value="3" />
38+
<!-- The tint is masked by the alpha channel of the drawable. The drawable’s
39+
color channels are thrown out. [Sa * Da, Sc * Da] -->
40+
<enum name="src_in" value="5" />
41+
<!-- The tint is drawn above the drawable, but with the drawable’s alpha
42+
channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
43+
<enum name="src_atop" value="9" />
44+
<!-- Multiplies the color and alpha channels of the drawable with those of
45+
the tint. [Sa * Da, Sc * Dc] -->
46+
<enum name="multiply" value="14" />
47+
<!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
48+
<enum name="screen" value="15" />
49+
<!-- Combines the tint and drawable color and alpha channels, clamping the
50+
result to valid color values. Saturate(S + D) -->
51+
<enum name="add" value="16" />
52+
</attr>
2653
<!-- Whether the MaterialCheckBox button drawable (checkbox icon) will be
2754
centered when there is no text set for the checkbox. Default is true. -->
2855
<attr name="centerIfNoTextEnabled" format="boolean"/>

‎lib/java/com/google/android/material/checkbox/res/values/strings.xml

+10
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,14 @@
1919
<string name="error_a11y_label" description="Screen reader announcement for when a checkbox is on error. [CHAR LIMIT=NONE]">
2020
Error: invalid
2121
</string>
22+
<string name="mtrl_checkbox_button_path_group_name" translatable="false">button</string>
23+
<string name="mtrl_checkbox_button_path_name" translatable="false">button path</string>
24+
<string name="mtrl_checkbox_button_icon_path_group_name" translatable="false">icon</string>
25+
<string name="mtrl_checkbox_button_icon_path_name" translatable="false">icon path</string>
26+
<!-- Path data to draw a square when the checkbox is checked. -->
27+
<string name="mtrl_checkbox_button_path_checked" translatable="false">M23,7H9C7.9,7,7,7.9,7,9v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V9C25,7.9,24.1,7,23,7z</string>
28+
<!-- Path data to draw an outlined square when the checkbox is unchecked. -->
29+
<string name="mtrl_checkbox_button_path_unchecked" translatable="false">M23,7H9C7.9,7,7,7.9,7,9v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V9C25,7.9,24.1,7,23,7z M23,23H9V9h14V23z</string>
30+
<!-- Path data to draw a checkmark icon when the checkbox is checked. -->
31+
<string name="mtrl_checkbox_button_icon_path_checked" translatable="false">M14,18.2 11.4,15.6 10,17 14,21 22,13 20.6,11.6z</string>
2232
</resources>

‎lib/java/com/google/android/material/checkbox/res/values/styles.xml

+9-1
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,16 @@
2626
<style name="Base.Widget.Material3.CompoundButton.CheckBox" parent="Widget.MaterialComponents.CompoundButton.CheckBox">
2727
<!-- Inherit default text color since the component doesn't draw a surface. -->
2828
<item name="android:textAppearance">?attr/textAppearanceBodyMedium</item>
29-
<item name="buttonTint">@color/m3_checkbox_button_tint</item>
3029
<item name="errorAccessibilityLabel">@string/error_a11y_label</item>
30+
<item name="buttonTint">@color/m3_checkbox_button_tint</item>
31+
<item name="buttonIconTint">@color/m3_checkbox_button_icon_tint</item>
32+
<!-- We null the button and icon here to preserve pre-existing behaviors of
33+
when the checkbox didn't have the concept of an icon drawable separate
34+
from the button drawable. Both default drawables will be set in
35+
MaterialCheckBox. -->
36+
<item name="buttonCompat">@null</item>
37+
<item name="android:button">@null</item>
38+
<item name="buttonIcon">@null</item>
3139
</style>
3240

3341
<style name="Widget.Material3.CompoundButton.CheckBox" parent="Base.Widget.Material3.CompoundButton.CheckBox" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
* Copyright (C) 2022 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.android.material.internal;
18+
19+
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
20+
21+
import android.content.res.ColorStateList;
22+
import android.graphics.PorterDuff.Mode;
23+
import android.graphics.drawable.Drawable;
24+
import android.graphics.drawable.LayerDrawable;
25+
import android.os.Build.VERSION;
26+
import android.os.Build.VERSION_CODES;
27+
import android.view.Gravity;
28+
import androidx.annotation.NonNull;
29+
import androidx.annotation.Nullable;
30+
import androidx.annotation.RestrictTo;
31+
import androidx.core.graphics.drawable.DrawableCompat;
32+
import java.util.Arrays;
33+
34+
/**
35+
* Utils class for {@link Drawable}.
36+
*
37+
* @hide
38+
*/
39+
@RestrictTo(LIBRARY_GROUP)
40+
public class DrawableUtils {
41+
42+
private DrawableUtils() {}
43+
44+
@Nullable
45+
public static Drawable createTintableDrawableIfNeeded(
46+
@Nullable Drawable drawable, @Nullable ColorStateList tintList, @Nullable Mode tintMode) {
47+
if (drawable == null) {
48+
return null;
49+
}
50+
if (tintList != null) {
51+
drawable = DrawableCompat.wrap(drawable).mutate();
52+
if (tintMode != null) {
53+
DrawableCompat.setTintMode(drawable, tintMode);
54+
}
55+
}
56+
return drawable;
57+
}
58+
59+
/**
60+
* Composites two drawables, returning a drawable instance of {@link LayerDrawable}, with the
61+
* second on top of the first. If any of the drawables is null, this method will return the other.
62+
*
63+
* @param bottomLayerDrawable the drawable to be on the first layer (bottom)
64+
* @param topLayerDrawable the drawable to be on the second layer (top)
65+
*/
66+
@Nullable
67+
public static Drawable compositeTwoLayeredDrawable(
68+
@Nullable Drawable bottomLayerDrawable, @Nullable Drawable topLayerDrawable) {
69+
if (bottomLayerDrawable == null) {
70+
return topLayerDrawable;
71+
}
72+
if (topLayerDrawable == null) {
73+
return bottomLayerDrawable;
74+
}
75+
LayerDrawable drawable =
76+
new LayerDrawable(new Drawable[] {bottomLayerDrawable, topLayerDrawable});
77+
int topLayerNewWidth;
78+
int topLayerNewHeight;
79+
if (topLayerDrawable.getIntrinsicWidth() == -1 || topLayerDrawable.getIntrinsicHeight() == -1) {
80+
// If there's no intrinsic width or height, keep bottom layer's size.
81+
topLayerNewWidth = bottomLayerDrawable.getIntrinsicWidth();
82+
topLayerNewHeight = bottomLayerDrawable.getIntrinsicHeight();
83+
} else if (topLayerDrawable.getIntrinsicWidth() <= bottomLayerDrawable.getIntrinsicWidth()
84+
&& topLayerDrawable.getIntrinsicHeight() <= bottomLayerDrawable.getIntrinsicHeight()) {
85+
// If the top layer is smaller than the bottom layer in both its width and height, keep top
86+
// layer's size.
87+
topLayerNewWidth = topLayerDrawable.getIntrinsicWidth();
88+
topLayerNewHeight = topLayerDrawable.getIntrinsicHeight();
89+
} else {
90+
float topLayerRatio =
91+
(float) topLayerDrawable.getIntrinsicWidth() / topLayerDrawable.getIntrinsicHeight();
92+
float bottomLayerRatio =
93+
(float) bottomLayerDrawable.getIntrinsicWidth()
94+
/ bottomLayerDrawable.getIntrinsicHeight();
95+
if (topLayerRatio >= bottomLayerRatio) {
96+
// If the top layer is wider in ratio than the bottom layer, shrink it according to its
97+
// width.
98+
topLayerNewWidth = bottomLayerDrawable.getIntrinsicWidth();
99+
topLayerNewHeight = (int) (topLayerNewWidth / topLayerRatio);
100+
} else {
101+
// If the top layer is taller in ratio than the bottom layer, shrink it according to its
102+
// height.
103+
topLayerNewHeight = bottomLayerDrawable.getIntrinsicHeight();
104+
topLayerNewWidth = (int) (topLayerRatio * topLayerNewHeight);
105+
}
106+
}
107+
// Centers the top layer inside the bottom layer. Before M there's no layer gravity support, we
108+
// need to use layer insets to adjust the top layer position manually.
109+
if (VERSION.SDK_INT >= VERSION_CODES.M) {
110+
drawable.setLayerSize(1, topLayerNewWidth, topLayerNewHeight);
111+
drawable.setLayerGravity(1, Gravity.CENTER);
112+
} else {
113+
int horizontalInset = (bottomLayerDrawable.getIntrinsicWidth() - topLayerNewWidth) / 2;
114+
int verticalInset = (bottomLayerDrawable.getIntrinsicHeight() - topLayerNewHeight) / 2;
115+
drawable.setLayerInset(1, horizontalInset, verticalInset, horizontalInset, verticalInset);
116+
}
117+
return drawable;
118+
}
119+
120+
/** Returns a new state that adds the checked state to the input state. */
121+
@NonNull
122+
public static int[] getCheckedState(@NonNull int[] state) {
123+
for (int i = 0; i < state.length; i++) {
124+
if (state[i] == android.R.attr.state_checked) {
125+
return state;
126+
} else if (state[i] == 0) {
127+
int[] newState = state.clone();
128+
newState[i] = android.R.attr.state_checked;
129+
return newState;
130+
}
131+
}
132+
int[] newState = Arrays.copyOf(state, state.length + 1);
133+
newState[state.length] = android.R.attr.state_checked;
134+
return newState;
135+
}
136+
137+
/** Returns a new state that removes the checked state from the input state. */
138+
@NonNull
139+
public static int[] getUncheckedState(@NonNull int[] state) {
140+
int[] newState = new int[state.length];
141+
int i = 0;
142+
for (int subState : state) {
143+
if (subState != android.R.attr.state_checked) {
144+
newState[i++] = subState;
145+
}
146+
}
147+
return newState;
148+
}
149+
}

‎lib/java/com/google/android/material/materialswitch/MaterialSwitch.java

+15-101
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,17 @@
2727
import android.graphics.PorterDuff.Mode;
2828
import android.graphics.drawable.Drawable;
2929
import android.graphics.drawable.LayerDrawable;
30-
import android.os.Build.VERSION;
31-
import android.os.Build.VERSION_CODES;
3230
import androidx.appcompat.content.res.AppCompatResources;
33-
import androidx.appcompat.widget.DrawableUtils;
3431
import androidx.appcompat.widget.SwitchCompat;
3532
import androidx.appcompat.widget.TintTypedArray;
3633
import android.util.AttributeSet;
37-
import android.view.Gravity;
3834
import androidx.annotation.DrawableRes;
3935
import androidx.annotation.NonNull;
4036
import androidx.annotation.Nullable;
4137
import androidx.core.graphics.drawable.DrawableCompat;
38+
import com.google.android.material.internal.DrawableUtils;
4239
import com.google.android.material.internal.ThemeEnforcement;
43-
import java.util.Arrays;
40+
import com.google.android.material.internal.ViewUtils;
4441

4542
/**
4643
* A class that creates a Material Themed Switch. This class is intended to provide a brand new
@@ -95,15 +92,15 @@ public MaterialSwitch(@NonNull Context context, @Nullable AttributeSet attrs, in
9592
thumbIconDrawable = attributes.getDrawable(R.styleable.MaterialSwitch_thumbIcon);
9693
thumbIconTintList = attributes.getColorStateList(R.styleable.MaterialSwitch_thumbIconTint);
9794
thumbIconTintMode =
98-
DrawableUtils.parseTintMode(
95+
ViewUtils.parseTintMode(
9996
attributes.getInt(R.styleable.MaterialSwitch_thumbIconTintMode, -1), Mode.SRC_IN);
10097

10198
trackDecorationDrawable =
10299
attributes.getDrawable(R.styleable.MaterialSwitch_trackDecoration);
103100
trackDecorationTintList =
104101
attributes.getColorStateList(R.styleable.MaterialSwitch_trackDecorationTint);
105102
trackDecorationTintMode =
106-
DrawableUtils.parseTintMode(
103+
ViewUtils.parseTintMode(
107104
attributes.getInt(R.styleable.MaterialSwitch_trackDecorationTintMode, -1), Mode.SRC_IN);
108105

109106
attributes.recycle();
@@ -128,8 +125,8 @@ protected int[] onCreateDrawableState(int extraSpace) {
128125
mergeDrawableStates(drawableState, STATE_SET_WITH_ICON);
129126
}
130127

131-
currentStateUnchecked = getUncheckedState(drawableState);
132-
currentStateChecked = getCheckedState(drawableState);
128+
currentStateUnchecked = DrawableUtils.getUncheckedState(drawableState);
129+
currentStateChecked = DrawableUtils.getCheckedState(drawableState);
133130

134131
return drawableState;
135132
}
@@ -364,67 +361,26 @@ public PorterDuff.Mode getTrackDecorationTintMode() {
364361

365362
private void refreshThumbDrawable() {
366363
thumbDrawable =
367-
createTintableDrawableIfNeeded(thumbDrawable, thumbTintList, getThumbTintMode());
364+
DrawableUtils.createTintableDrawableIfNeeded(
365+
thumbDrawable, thumbTintList, getThumbTintMode());
368366
thumbIconDrawable =
369-
createTintableDrawableIfNeeded(thumbIconDrawable, thumbIconTintList, thumbIconTintMode);
367+
DrawableUtils.createTintableDrawableIfNeeded(
368+
thumbIconDrawable, thumbIconTintList, thumbIconTintMode);
370369

371370
updateDrawableTints();
372371

373-
super.setThumbDrawable(compositeThumbAndIconDrawable(thumbDrawable, thumbIconDrawable));
372+
super.setThumbDrawable(
373+
DrawableUtils.compositeTwoLayeredDrawable(thumbDrawable, thumbIconDrawable));
374374

375375
refreshDrawableState();
376376
}
377377

378-
@Nullable
379-
private static Drawable compositeThumbAndIconDrawable(
380-
@Nullable Drawable thumbDrawable, @Nullable Drawable thumbIconDrawable) {
381-
if (thumbDrawable == null) {
382-
return thumbIconDrawable;
383-
}
384-
if (thumbIconDrawable == null) {
385-
return thumbDrawable;
386-
}
387-
LayerDrawable drawable = new LayerDrawable(new Drawable[]{thumbDrawable, thumbIconDrawable});
388-
int iconNewWidth;
389-
int iconNewHeight;
390-
if (thumbIconDrawable.getIntrinsicWidth() <= thumbDrawable.getIntrinsicWidth()
391-
&& thumbIconDrawable.getIntrinsicHeight() <= thumbDrawable.getIntrinsicHeight()) {
392-
// If the icon is smaller than the thumb in both its width and height, keep icon's size.
393-
iconNewWidth = thumbIconDrawable.getIntrinsicWidth();
394-
iconNewHeight = thumbIconDrawable.getIntrinsicHeight();
395-
} else {
396-
float thumbIconRatio =
397-
(float) thumbIconDrawable.getIntrinsicWidth() / thumbIconDrawable.getIntrinsicHeight();
398-
float thumbRatio =
399-
(float) thumbDrawable.getIntrinsicWidth() / thumbDrawable.getIntrinsicHeight();
400-
if (thumbIconRatio >= thumbRatio) {
401-
// If the icon is wider in ratio than the thumb, shrink it according to its width.
402-
iconNewWidth = thumbDrawable.getIntrinsicWidth();
403-
iconNewHeight = (int) (iconNewWidth / thumbIconRatio);
404-
} else {
405-
// If the icon is taller in ratio than the thumb, shrink it according to its height.
406-
iconNewHeight = thumbDrawable.getIntrinsicHeight();
407-
iconNewWidth = (int) (thumbIconRatio * iconNewHeight);
408-
}
409-
}
410-
// Centers the icon inside the thumb. Before M there's no layer gravity support, we need to use
411-
// layer insets to adjust the icon position manually.
412-
if (VERSION.SDK_INT >= VERSION_CODES.M) {
413-
drawable.setLayerSize(1, iconNewWidth, iconNewHeight);
414-
drawable.setLayerGravity(1, Gravity.CENTER);
415-
} else {
416-
int horizontalInset = (thumbDrawable.getIntrinsicWidth() - iconNewWidth) / 2;
417-
int verticalInset = (thumbDrawable.getIntrinsicHeight() - iconNewHeight) / 2;
418-
drawable.setLayerInset(1, horizontalInset, verticalInset, horizontalInset, verticalInset);
419-
}
420-
return drawable;
421-
}
422-
423378
private void refreshTrackDrawable() {
424379
trackDrawable =
425-
createTintableDrawableIfNeeded(trackDrawable, trackTintList, getTrackTintMode());
380+
DrawableUtils.createTintableDrawableIfNeeded(
381+
trackDrawable, trackTintList, getTrackTintMode());
426382
trackDecorationDrawable =
427-
createTintableDrawableIfNeeded(
383+
DrawableUtils.createTintableDrawableIfNeeded(
428384
trackDecorationDrawable, trackDecorationTintList, trackDecorationTintMode);
429385

430386
updateDrawableTints();
@@ -484,34 +440,6 @@ private void updateDrawableTints() {
484440
}
485441
}
486442

487-
/** Returns a new state that removes the checked state from the input state. */
488-
private static int[] getUncheckedState(int[] state) {
489-
int[] newState = new int[state.length];
490-
int i = 0;
491-
for (int subState : state) {
492-
if (subState != android.R.attr.state_checked) {
493-
newState[i++] = subState;
494-
}
495-
}
496-
return newState;
497-
}
498-
499-
/** Returns a new state that adds the checked state to the input state. */
500-
private static int[] getCheckedState(int[] state) {
501-
for (int i = 0; i < state.length; i++) {
502-
if (state[i] == android.R.attr.state_checked) {
503-
return state;
504-
} else if (state[i] == 0) {
505-
int[] newState = state.clone();
506-
newState[i] = android.R.attr.state_checked;
507-
return newState;
508-
}
509-
}
510-
int[] newState = Arrays.copyOf(state, state.length + 1);
511-
newState[state.length] = android.R.attr.state_checked;
512-
return newState;
513-
}
514-
515443
/**
516444
* Tints the given drawable with the interpolated color according to the provided thumb position
517445
* between unchecked and checked states. The reference color in unchecked and checked states will
@@ -534,18 +462,4 @@ private static void setInterpolatedDrawableTintIfPossible(
534462
tint.getColorForState(stateChecked, 0),
535463
thumbPosition));
536464
}
537-
538-
private static Drawable createTintableDrawableIfNeeded(
539-
Drawable drawable, ColorStateList tintList, Mode tintMode) {
540-
if (drawable == null) {
541-
return null;
542-
}
543-
if (tintList != null) {
544-
drawable = DrawableCompat.wrap(drawable).mutate();
545-
if (tintMode != null) {
546-
DrawableCompat.setTintMode(drawable, tintMode);
547-
}
548-
}
549-
return drawable;
550-
}
551465
}

0 commit comments

Comments
 (0)
Please sign in to comment.