Search code examples
androidcolorscontrolsmaui

Setting the TrackColor of a Switch on Android MAUI, using a Handler, doesn't stay set when switch is toggled


I am creating a mobile app using Microsoft's MAUI framework. To toggle a particular feature on and off, I have put a Switch element in the TitleView of the NavigationPage I'm working on.

Because I used an Android theme with a dark-colored action bar, the track of the Switch fades into the background on that platform. I fixed this by using a Handler, which lets me set the TrackColor of the Switch on Android.

The catch: when I toggle the Switch, the colors snap back to the originals.

In the XAML file:

<NavigationPage.TitleView>
        <StackLayout Orientation="Horizontal">
            <Switch x:Name="mySwitch" IsVisible="True" OnColor="White" HorizontalOptions="End" Toggled="MySwitchToggled">
                <VisualStateManager.VisualStateGroups>
                    <VisualStateGroup x:Name="CommonStates">
                        <VisualState x:Name="On">
                            <VisualState.Setters>
                                <Setter Property="ThumbColor" Value="#0082FC" /> <!--switch handle bright blue when on-->
                            </VisualState.Setters>
                        </VisualState>
                        <VisualState x:Name="Off">
                            <VisualState.Setters>
                                <Setter Property="ThumbColor" Value="White" />
                            </VisualState.Setters>
                        </VisualState>
                    </VisualStateGroup>
                </VisualStateManager.VisualStateGroups>
            </Switch>
        </StackLayout>
    </NavigationPage.TitleView>

In the codebehind file:

public ItemListPage(int areaId, string areaName)
        {
            InitializeComponent();
            //some unrelated code removed here
            ModifySwitchColors(); //fix switch colors on Android
        }

        private void ModifySwitchColors()
        {
#if ANDROID
            var bgColor = Microsoft.Maui.Graphics.Colors.Gray;
            SwitchHandler.Mapper.AppendToMapping("CustomColors", (handler, view) => DependencyService.Get<INativeCalls>().ModifySwitchTrackColor(handler, view, bgColor));
#endif
        }

In the implementation of the platform-specific code interface:

public void ModifySwitchTrackColor(ISwitchHandler handler, ISwitch view, Microsoft.Maui.Graphics.Color inColor)
        {
            inColor.ToRgba(out var rByte, out var gByte, out var bByte, out var aByte);
            var color = new Android.Graphics.Color(rByte, gByte, bByte, aByte);
            if (Build.VERSION.SdkInt >= BuildVersionCodes.Q)
            {
                handler.PlatformView.TrackDrawable.SetColorFilter(new Android.Graphics.BlendModeColorFilter(color, Android.Graphics.BlendMode.SrcAtop));
            }
            else
            {
                handler.PlatformView.TrackDrawable.SetColorFilter(color, Android.Graphics.PorterDuff.Mode.SrcAtop);
            }
        }

Is there a way I can fix this?


Solution

  • I finally solved it, but it took some digging. The solution was to edit the track tint list of the switch, instead of adding a color filter:

    public void ModifySwitchTrackColor(ISwitchHandler handler)
    {
        //colors use argb format.
        //16842912 is Android.r.attr.state_checked which isn't in the MAUI interfaces for some reason
        handler.PlatformView.TrackTintList = new ColorStateList(
        new int[][] {
            new int[] { 16842912 },
            new int[] { } //default
        },
        new int[] {
            Android.Graphics.Color.ParseColor("#FFFFFFFF"), //checked
            Android.Graphics.Color.ParseColor("#FFBBBBBB") //unchecked
        }
        );
    }
    

    Note that I had to directly specify the constant for the android.R.attr.state_checked state - MAUI's files didn't have it. I don't know why this is.

    Then, instead of calling this function using AppendToMapping, I called it directly after the Page loaded:

    void PostOnAppearing(object sender, EventArgs e) //page Loaded event handler
    {            
        DependencyService.Get<INativeCalls>().ModifySwitchTrackColor(mySwitch.Handler as ISwitchHandler);
    }
    

    After calling it once, it sticks for the rest of the page load. Hooray!