Skip to content

Commit 1e7bd27

Browse files
drchendsn5ft
authored andcommitted
[Switch] Implement track decoration in the new design
The default decoration will be a track outline in the unchecked state. PiperOrigin-RevId: 449013525
1 parent 29fddaa commit 1e7bd27

File tree

6 files changed

+260
-0
lines changed

6 files changed

+260
-0
lines changed

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

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,21 @@
2121
import static com.google.android.material.theme.overlay.MaterialThemeOverlay.wrap;
2222

2323
import android.content.Context;
24+
import android.content.res.ColorStateList;
25+
import android.graphics.PorterDuff;
26+
import android.graphics.PorterDuff.Mode;
27+
import android.graphics.drawable.Drawable;
28+
import android.graphics.drawable.LayerDrawable;
29+
import androidx.appcompat.content.res.AppCompatResources;
30+
import androidx.appcompat.widget.DrawableUtils;
2431
import androidx.appcompat.widget.SwitchCompat;
32+
import androidx.appcompat.widget.TintTypedArray;
2533
import android.util.AttributeSet;
34+
import androidx.annotation.DrawableRes;
2635
import androidx.annotation.NonNull;
2736
import androidx.annotation.Nullable;
37+
import androidx.core.graphics.drawable.DrawableCompat;
38+
import com.google.android.material.internal.ThemeEnforcement;
2839

2940
/**
3041
* A class that creates a Material Themed Switch. This class is intended to provide a brand new
@@ -34,6 +45,13 @@
3445
public class MaterialSwitch extends SwitchCompat {
3546
private static final int DEF_STYLE_RES = R.style.Widget_Material3_CompoundButton_MaterialSwitch;
3647

48+
@Nullable private Drawable trackDrawable;
49+
@Nullable private Drawable trackDecorationDrawable;
50+
51+
@Nullable private ColorStateList trackTintList;
52+
@Nullable private ColorStateList trackDecorationTintList;
53+
@NonNull private PorterDuff.Mode trackDecorationTintMode;
54+
3755
public MaterialSwitch(@NonNull Context context) {
3856
this(context, null);
3957
}
@@ -44,5 +62,173 @@ public MaterialSwitch(@NonNull Context context, @Nullable AttributeSet attrs) {
4462

4563
public MaterialSwitch(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
4664
super(wrap(context, attrs, defStyleAttr, DEF_STYLE_RES), attrs, defStyleAttr);
65+
// Ensure we are using the correctly themed context rather than the context that was passed in.
66+
context = getContext();
67+
68+
trackDrawable = super.getTrackDrawable();
69+
trackTintList = super.getTrackTintList();
70+
super.setTrackTintList(null); // Always use our custom tinting logic
71+
72+
TintTypedArray attributes =
73+
ThemeEnforcement.obtainTintedStyledAttributes(
74+
context, attrs, R.styleable.MaterialSwitch, defStyleAttr, DEF_STYLE_RES);
75+
76+
trackDecorationDrawable =
77+
attributes.getDrawable(R.styleable.MaterialSwitch_trackDecoration);
78+
trackDecorationTintList =
79+
attributes.getColorStateList(R.styleable.MaterialSwitch_trackDecorationTint);
80+
trackDecorationTintMode =
81+
DrawableUtils.parseTintMode(
82+
attributes.getInt(R.styleable.MaterialSwitch_trackDecorationTintMode, -1), Mode.SRC_IN);
83+
84+
attributes.recycle();
85+
86+
refreshTrackDrawable();
87+
}
88+
89+
@Override
90+
public void setTrackDrawable(@Nullable Drawable track) {
91+
trackDrawable = track;
92+
refreshTrackDrawable();
93+
}
94+
95+
@Override
96+
@Nullable
97+
public Drawable getTrackDrawable() {
98+
return trackDrawable;
99+
}
100+
101+
@Override
102+
public void setTrackTintList(@Nullable ColorStateList tint) {
103+
trackTintList = tint;
104+
refreshTrackDrawable();
105+
}
106+
107+
@Override
108+
@Nullable
109+
public ColorStateList getTrackTintList() {
110+
return trackTintList;
111+
}
112+
113+
@Override
114+
public void setTrackTintMode(@Nullable PorterDuff.Mode tintMode) {
115+
super.setTrackTintMode(tintMode);
116+
refreshTrackDrawable();
117+
}
118+
119+
/**
120+
* Set the drawable used for the track decoration that will be drawn upon the track.
121+
*
122+
* @param resId Resource ID of a track decoration drawable
123+
*
124+
* @attr ref com.google.android.material.R.styleable#MaterialSwitch_trackDecoration
125+
*/
126+
public void setTrackDecorationResource(@DrawableRes int resId) {
127+
setTrackDecorationDrawable(AppCompatResources.getDrawable(getContext(), resId));
128+
}
129+
130+
/**
131+
* Set the drawable used for the track decoration that will be drawn upon the track.
132+
*
133+
* @param trackDecoration Track decoration drawable
134+
*
135+
* @attr ref com.google.android.material.R.styleable#MaterialSwitch_trackDecoration
136+
*/
137+
public void setTrackDecorationDrawable(@Nullable Drawable trackDecoration) {
138+
trackDecorationDrawable = trackDecoration;
139+
refreshTrackDrawable();
140+
}
141+
142+
/**
143+
* Get the drawable used for the track decoration that will be drawn upon the track.
144+
*
145+
* @attr ref com.google.android.material.R.styleable#MaterialSwitch_trackDecoration
146+
*/
147+
@Nullable
148+
public Drawable getTrackDecorationDrawable() {
149+
return trackDecorationDrawable;
150+
}
151+
152+
/**
153+
* Applies a tint to the track decoration drawable. Does not modify the current
154+
* tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
155+
*
156+
* <p>Subsequent calls to {@link #setTrackDecorationDrawable(Drawable)} will
157+
* automatically mutate the drawable and apply the specified tint and tint
158+
* mode using {@link DrawableCompat#setTintList(Drawable, ColorStateList)}.
159+
*
160+
* @param tint the tint to apply, may be {@code null} to clear tint
161+
*
162+
* @attr ref com.google.android.material.R.styleable#MaterialSwitch_trackDecorationTint
163+
*/
164+
public void setTrackDecorationTintList(@Nullable ColorStateList tint) {
165+
trackDecorationTintList = tint;
166+
refreshTrackDrawable();
167+
}
168+
169+
/**
170+
* Returns the tint applied to the track decoration drawable
171+
*
172+
* @attr ref com.google.android.material.R.styleable#MaterialSwitch_trackDecorationTint
173+
*/
174+
@Nullable
175+
public ColorStateList getTrackDecorationTintList() {
176+
return trackDecorationTintList;
177+
}
178+
179+
/**
180+
* Specifies the blending mode used to apply the tint specified by
181+
* {@link #setTrackDecorationTintList(ColorStateList)}} to the track decoration drawable.
182+
* The default mode is {@link PorterDuff.Mode#SRC_IN}.
183+
*
184+
* @param tintMode the blending mode used to apply the tint
185+
186+
* @attr ref com.google.android.material.R.styleable#MaterialSwitch_trackDecorationTintMode
187+
*/
188+
public void setTrackDecorationTintMode(@NonNull PorterDuff.Mode tintMode) {
189+
trackDecorationTintMode = tintMode;
190+
refreshTrackDrawable();
191+
}
192+
193+
/**
194+
* Returns the blending mode used to apply the tint to the track decoration drawable
195+
*
196+
* @attr ref com.google.android.material.R.styleable#MaterialSwitch_trackDecorationTintMode
197+
*/
198+
@NonNull
199+
public PorterDuff.Mode getTrackDecorationTintMode() {
200+
return trackDecorationTintMode;
201+
}
202+
203+
private void refreshTrackDrawable() {
204+
trackDrawable = setDrawableTintListIfNeeded(trackDrawable, trackTintList, getTrackTintMode());
205+
trackDecorationDrawable = setDrawableTintListIfNeeded(
206+
trackDecorationDrawable, trackDecorationTintList, trackDecorationTintMode);
207+
208+
Drawable finalTrackDrawable;
209+
if (trackDrawable != null && trackDecorationDrawable != null) {
210+
finalTrackDrawable =
211+
new LayerDrawable(new Drawable[]{ trackDrawable, trackDecorationDrawable});
212+
} else if (trackDrawable != null) {
213+
finalTrackDrawable = trackDrawable;
214+
} else {
215+
finalTrackDrawable = trackDecorationDrawable;
216+
}
217+
super.setTrackDrawable(finalTrackDrawable);
218+
}
219+
220+
private static Drawable setDrawableTintListIfNeeded(
221+
Drawable drawable, ColorStateList tintList, Mode tintMode) {
222+
if (drawable == null) {
223+
return null;
224+
}
225+
if (tintList != null) {
226+
drawable = DrawableCompat.wrap(drawable).mutate();
227+
}
228+
DrawableCompat.setTintList(drawable, tintList);
229+
if (tintList != null && tintMode != null) {
230+
DrawableCompat.setTintMode(drawable, tintMode);
231+
}
232+
return drawable;
47233
}
48234
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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+
<item android:color="@android:color/transparent" android:state_checked="true"/>
19+
<item
20+
android:alpha="@dimen/material_emphasis_disabled_background"
21+
android:color="?attr/colorOnSurface"
22+
android:state_enabled="false"/>
23+
<item android:color="?attr/colorOutline"/>
24+
</selector>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?xml version="1.0" encoding="utf-8"?>
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+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
18+
xmlns:tools="http://schemas.android.com/tools"
19+
android:width="@dimen/mtrl_switch_track_width"
20+
android:height="@dimen/mtrl_switch_track_height"
21+
android:viewportWidth="@integer/mtrl_switch_track_viewport_width"
22+
android:viewportHeight="@integer/mtrl_switch_track_viewport_height"
23+
tools:ignore="NewApi">
24+
25+
<path
26+
android:name="outline"
27+
android:strokeColor="?attr/colorOutline"
28+
android:strokeWidth="2"
29+
android:pathData="@string/mtrl_switch_track_decoration_path" />
30+
31+
</vector>

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,19 @@
1616
<resources>
1717
<!-- Style to use for MaterialSwitch in the theme. -->
1818
<attr name="materialSwitchStyle" type="reference"/>
19+
20+
<!-- Drawable used for the track decoration that will be drawn upon the track.
21+
By default it will draw an outline on the track in the unchecked state. -->
22+
<attr name="trackDecoration" type="reference"/>
23+
<!-- Tint that will be applied to the track decoration drawable.. -->
24+
<attr name="trackDecorationTint" type="reference"/>
25+
<!-- The blending mode used to apply the tint specified by trackDecorationTint
26+
to trackDecoration. The default mode is SRC_IN if not specified. -->
27+
<attr name="trackDecorationTintMode" type="reference"/>
28+
29+
<declare-styleable name="MaterialSwitch">
30+
<attr name="trackDecoration"/>
31+
<attr name="trackDecorationTint"/>
32+
<attr name="trackDecorationTintMode"/>
33+
</declare-styleable>
1934
</resources>

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,6 @@
3636

3737
<!-- Path data to draw the switch track. -->
3838
<string name="mtrl_switch_track_path" translatable="false">M0,16 A16,16 0 0,1 16,0 H36 A16,16 0 0,1 36,32 H16 A16,16 0 0,1 0,16</string>
39+
<!-- Path data to draw the switch track decor. -->
40+
<string name="mtrl_switch_track_decoration_path" translatable="false">M1,16 A15,15 0 0,1 16,1 H36 A15,15 0 0,1 36,31 H16 A15,15 0 0,1 1,16</string>
3941
</resources>

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
<style name="Widget.Material3.CompoundButton.MaterialSwitch" parent="Widget.AppCompat.CompoundButton.Switch">
1818
<item name="android:thumb">@drawable/mtrl_switch_thumb</item>
1919
<item name="track">@drawable/mtrl_switch_track</item>
20+
<item name="trackDecoration">@drawable/mtrl_switch_track_decoration</item>
2021
<item name="thumbTint">@color/mtrl_switch_thumb_tint</item>
2122
<item name="trackTint">@color/mtrl_switch_track_tint</item>
23+
<item name="trackDecorationTint">@color/mtrl_switch_track_decoration_tint</item>
2224
</style>
2325
</resources>

0 commit comments

Comments
 (0)