Xamarin.Android中动态设置Switch滑块与轨道颜色的最优方案
优化Xamarin.Android中多Switch控件的统一颜色设置方案
现有实现
当前已通过XML布局和Drawable资源实现自定义样式的Switch:
布局代码
<Switch android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/switchLocationTracking" android:thumb="@drawable/thumb_background" android:track="@drawable/track_backgrounds" />
track_backgrounds.xml(轨道样式)
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_checked="true"> <shape android:shape="rectangle" > <solid android:color="#FFFF00" /> <corners android:radius="30dp" /> <size android:height="32dp" /> </shape> </item> <item android:state_checked="false"> <shape android:shape="rectangle"> <solid android:color="#ADD8E6" /> <corners android:radius="30dp" /> <size android:height="32dp" /> </shape> </item> </selector>
thumb_background.xml(滑块样式)
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_checked="true"> <shape android:shape="oval"> <solid android:color="#ffa500" /> <size android:width="30dp" android:height="30dp" /> </shape> </item> <item android:state_checked="false"> <shape android:shape="oval"> <solid android:color="#FF00FF" /> <size android:width="30dp" android:height="30dp" /> </shape> </item> </selector>
需求场景
应用启动时通过WebView获取Switch选中/未选中状态对应的滑块、轨道颜色,需要给应用内4个Switch控件统一设置这些颜色,不想在CheckedChange事件中逐个处理,寻求更高效的实现方式。
优化方案
方案1:自定义Switch控件
创建自定义Switch,在控件内部封装颜色设置逻辑,所有实例自动应用配置,无需重复代码:
public class ThemedSwitch : Switch { public ThemedSwitch(Context context) : base(context) => Init(); public ThemedSwitch(Context context, IAttributeSet attrs) : base(context, attrs) => Init(); public ThemedSwitch(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr) => Init(); private void Init() { // 替换为从WebView获取的实际颜色值 var checkedThumbColor = MainActivity.theme.checkedThumbColor; var uncheckedThumbColor = MainActivity.theme.uncheckedThumbColor; var checkedTrackColor = MainActivity.theme.checkedTrackColor; var uncheckedTrackColor = MainActivity.theme.uncheckedTrackColor; // 构建滑块状态选择器 var thumbSelector = new StateListDrawable(); // 选中状态滑块 var checkedThumb = new GradientDrawable { Shape = ShapeType.Oval }; checkedThumb.SetColor(Color.ParseColor(checkedThumbColor)); var thumbSize = (int)Context.Resources.GetDimension(Resource.Dimension.thumb_size); checkedThumb.SetSize(thumbSize, thumbSize); thumbSelector.AddState(new[] { Android.Resource.Attribute.StateChecked }, checkedThumb); // 未选中状态滑块 var uncheckedThumb = new GradientDrawable { Shape = ShapeType.Oval }; uncheckedThumb.SetColor(Color.ParseColor(uncheckedThumbColor)); uncheckedThumb.SetSize(thumbSize, thumbSize); thumbSelector.AddState(new int[] { }, uncheckedThumb); // 默认状态 ThumbDrawable = thumbSelector; // 构建轨道状态选择器 var trackSelector = new StateListDrawable(); // 选中状态轨道 var checkedTrack = new GradientDrawable { Shape = ShapeType.Rectangle }; checkedTrack.SetColor(Color.ParseColor(checkedTrackColor)); var trackRadius = Context.Resources.GetDimension(Resource.Dimension.track_radius); var trackHeight = (int)Context.Resources.GetDimension(Resource.Dimension.track_height); checkedTrack.SetCornerRadius(trackRadius); checkedTrack.SetSize(ViewGroup.LayoutParams.WrapContent, trackHeight); trackSelector.AddState(new[] { Android.Resource.Attribute.StateChecked }, checkedTrack); // 未选中状态轨道 var uncheckedTrack = new GradientDrawable { Shape = ShapeType.Rectangle }; uncheckedTrack.SetColor(Color.ParseColor(uncheckedTrackColor)); uncheckedTrack.SetCornerRadius(trackRadius); uncheckedTrack.SetSize(ViewGroup.LayoutParams.WrapContent, trackHeight); trackSelector.AddState(new int[] { }, uncheckedTrack); TrackDrawable = trackSelector; } }
布局中直接使用自定义控件:
<com.yourpackage.ThemedSwitch android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/switchLocationTracking" />
方案2:封装工具类批量处理
如果不想自定义控件,可编写静态工具方法,传入颜色参数和Switch列表统一设置:
public static class SwitchThemeHelper { public static void ApplySwitchTheme(IEnumerable<Switch> switches, string checkedThumbColor, string uncheckedThumbColor, string checkedTrackColor, string uncheckedTrackColor) { foreach (var sw in switches) { // 设置滑块 var thumbSelector = BuildThumbSelector(sw.Context, checkedThumbColor, uncheckedThumbColor); sw.ThumbDrawable = thumbSelector; // 设置轨道 var trackSelector = BuildTrackSelector(sw.Context, checkedTrackColor, uncheckedTrackColor); sw.TrackDrawable = trackSelector; } } private static StateListDrawable BuildThumbSelector(Context context, string checkedColor, string uncheckedColor) { var selector = new StateListDrawable(); var thumbSize = (int)context.Resources.GetDimension(Resource.Dimension.thumb_size); var checkedThumb = new GradientDrawable { Shape = ShapeType.Oval }; checkedThumb.SetColor(Color.ParseColor(checkedColor)); checkedThumb.SetSize(thumbSize, thumbSize); selector.AddState(new[] { Android.Resource.Attribute.StateChecked }, checkedThumb); var uncheckedThumb = new GradientDrawable { Shape = ShapeType.Oval }; uncheckedThumb.SetColor(Color.ParseColor(uncheckedColor)); uncheckedThumb.SetSize(thumbSize, thumbSize); selector.AddState(new int[] { }, uncheckedThumb); return selector; } private static StateListDrawable BuildTrackSelector(Context context, string checkedColor, string uncheckedColor) { var selector = new StateListDrawable(); var trackRadius = context.Resources.GetDimension(Resource.Dimension.track_radius); var trackHeight = (int)context.Resources.GetDimension(Resource.Dimension.track_height); var checkedTrack = new GradientDrawable { Shape = ShapeType.Rectangle }; checkedTrack.SetColor(Color.ParseColor(checkedColor)); checkedTrack.SetCornerRadius(trackRadius); checkedTrack.SetSize(ViewGroup.LayoutParams.WrapContent, trackHeight); selector.AddState(new[] { Android.Resource.Attribute.StateChecked }, checkedTrack); var uncheckedTrack = new GradientDrawable { Shape = ShapeType.Rectangle }; uncheckedTrack.SetColor(Color.ParseColor(uncheckedColor)); uncheckedTrack.SetCornerRadius(trackRadius); uncheckedTrack.SetSize(ViewGroup.LayoutParams.WrapContent, trackHeight); selector.AddState(new int[] { }, uncheckedTrack); return selector; } }
使用示例:
// 收集所有需要设置的Switch var switches = new List<Switch> { FindViewById<Switch>(Resource.Id.switchLocationTracking), FindViewById<Switch>(Resource.Id.switch1), FindViewById<Switch>(Resource.Id.switch2), FindViewById<Switch>(Resource.Id.switch3) }; // 批量应用主题 SwitchThemeHelper.ApplySwitchTheme(switches, MainActivity.theme.checkedThumbColor, MainActivity.theme.uncheckedThumbColor, MainActivity.theme.checkedTrackColor, MainActivity.theme.uncheckedTrackColor);
方案3:动态修改现有XML Selector颜色
如果想保留原有XML的形状和尺寸定义,可通过DrawableCompat动态修改不同状态的颜色:
public static void TintSwitchFromXml(Switch sw, string checkedThumbColor, string uncheckedThumbColor, string checkedTrackColor, string uncheckedTrackColor) { // 处理滑块 var thumbDrawable = sw.ThumbDrawable.Mutate(); // Mutate避免多实例共享状态 if (thumbDrawable is StateListDrawable thumbStateList) { // 设置选中状态滑块颜色 var checkedThumb = thumbStateList.GetStateDrawable(thumbStateList.GetIndexState(new[] { Android.Resource.Attribute.StateChecked })); if (checkedThumb != null) DrawableCompat.SetTint(checkedThumb, Color.ParseColor(checkedThumbColor)); // 设置未选中状态滑块颜色 var uncheckedThumb = thumbStateList.GetStateDrawable(thumbStateList.GetIndexState(new int[] { })); if (uncheckedThumb != null) DrawableCompat.SetTint(uncheckedThumb, Color.ParseColor(uncheckedThumbColor)); sw.ThumbDrawable = thumbStateList; } // 处理轨道 var trackDrawable = sw.TrackDrawable.Mutate(); if (trackDrawable is StateListDrawable trackStateList) { var checkedTrack = trackStateList.GetStateDrawable(trackStateList.GetIndexState(new[] { Android.Resource.Attribute.StateChecked })); if (checkedTrack != null) DrawableCompat.SetTint(checkedTrack, Color.ParseColor(checkedTrackColor)); var uncheckedTrack = trackStateList.GetStateDrawable(trackStateList.GetIndexState(new int[] { })); if (uncheckedTrack != null) DrawableCompat.SetTint(uncheckedTrack, Color.ParseColor(uncheckedTrackColor)); sw.TrackDrawable = trackStateList; } }
调用方式同方案2,批量遍历Switch执行该方法即可。
内容的提问来源于stack exchange,提问作者Gith
相关产品推荐
相关产品推荐

