2020.09.01.

Xamarin.Forms – Android – change the Theme at runtime

By bence960206

This project you can find at: https://github.com/officialdoniald/Xamarin.Forms—Change-Android-Theme-at-runtime

In the .NET Standard/PCL Project we can’t say that, please change the Theme right now, because wehave to implement some platform specific implementation in the Android project.

How can the two project communicate? Now, we will see another way, unlike the previous ones, we will use events, not dependency service.

So, let’s create our event class in the .NET Standard/PCL Project (https://github.com/officialdoniald/Xamarin.Forms—Change-Android-Theme-at-runtime/blob/master/XamarinFormsChangeAndoirdThemeRuntime/XamarinFormsChangeAndoirdThemeRuntime/GlobalEvents.cs):

using System;
namespace XamarinFormsChangeAndoirdThemeRuntime
{
public class GlobalEvents
{
public static event EventHandler OnAndroidThemeChangeNeeded;
    
public static void OnAndroidThemeChangeNeeded_Event(object sender, int themeid)
    {
        OnAndroidThemeChangeNeeded?.Invoke(sender, themeid);
    }
   }
}

In the Android site, we will use the SetTheme(themeid) function, that set the Theme on the Application. We have to subscribe int the event int the MainActivity, and we have to call the SetTheme(themeid) method, in the main thread (https://github.com/officialdoniald/Xamarin.Forms—Change-Android-Theme-at-runtime/blob/master/XamarinFormsChangeAndoirdThemeRuntime/XamarinFormsChangeAndoirdThemeRuntime.Android/MainActivity.cs):

using Android.App;
using Android.Content.PM;
using Android.OS;

namespace XamarinFormsChangeAndoirdThemeRuntime.Droid
{
    [Activity(Label = "XamarinFormsChangeAndoirdThemeRuntime", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        protected override void OnCreate(Bundle savedInstanceState)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(savedInstanceState);
            global::Xamarin.Forms.Forms.Init(this, savedInstanceState);

            GlobalVariables.MainThemeResourceID = Resource.Style.MainTheme;
            GlobalVariables.MainTransparentThemeResourceID = Resource.Style.MainTransparentTheme_Base;
            GlobalEvents.OnAndroidThemeChangeNeeded += GlobalEvents_OnAndroidThemeChangeNeeded; ;

            LoadApplication(new App());
        }

        private void GlobalEvents_OnAndroidThemeChangeNeeded(object sender, int themeid)
        {
            RunOnUiThread(() => {
                SetTheme(themeid);
            });
        }
    }
}

As you see, we gain the Theme’s Resource ID to the SetTheme(themeid) function. So we have to store theese Themes, in a global variable. Let’s create a class in the .NET Standard/PCL Project, and define static int variables as the Theme’s IDs (https://github.com/officialdoniald/Xamarin.Forms—Change-Android-Theme-at-runtime/blob/master/XamarinFormsChangeAndoirdThemeRuntime/XamarinFormsChangeAndoirdThemeRuntime/GlobalVariables.cs):

namespace XamarinFormsChangeAndoirdThemeRuntime
{
    public class GlobalVariables
    {
        public static int MainThemeResourceID { get; set; }

        public static int MainTransparentThemeResourceID { get; set; }
    }
}

And now we have to just try theese codes. Let’s create a Page with two Buttons, and subscribe on their Click events:

https://github.com/officialdoniald/Xamarin.Forms—Change-Android-Theme-at-runtime/blob/master/XamarinFormsChangeAndoirdThemeRuntime/XamarinFormsChangeAndoirdThemeRuntime/MainPage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamarinFormsChangeAndoirdThemeRuntime.MainPage">
    <StackLayout>
        <Button x:Name="mainThemeButton" 
                Clicked="MainThemeButton_Clicked" 
                Text="MainTheme"/>
        <Button x:Name="mainTransparentButton" 
                Clicked="MainTransparentButton_Clicked" 
                Text="TransparentTheme"/>
    </StackLayout>
</ContentPage>

https://github.com/officialdoniald/Xamarin.Forms—Change-Android-Theme-at-runtime/blob/master/XamarinFormsChangeAndoirdThemeRuntime/XamarinFormsChangeAndoirdThemeRuntime/MainPage.xaml.cs

using System;
using Xamarin.Forms;

namespace XamarinFormsChangeAndoirdThemeRuntime
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }

        private void MainThemeButton_Clicked(object sender, EventArgs e)
        {
            Device.BeginInvokeOnMainThread(() => 
            {
                GlobalEvents.OnAndroidThemeChangeNeeded_Event(null, GlobalVariables.MainThemeResourceID);
            });
        }

        private void MainTransparentButton_Clicked(object sender, EventArgs e)
        {
            Device.BeginInvokeOnMainThread(() =>
            {
                GlobalEvents.OnAndroidThemeChangeNeeded_Event(null, GlobalVariables.MainTransparentThemeResourceID);
            });
        }
    }
}

I defined two Themes, theese Themes change a listview’s color. You can find they at: https://github.com/officialdoniald/Xamarin.Forms—Change-Android-Theme-at-runtime/tree/master/XamarinFormsChangeAndoirdThemeRuntime/XamarinFormsChangeAndoirdThemeRuntime.Android/Resources/values

Their was defined at the color.xml(link above).

Of cource you don’t have to do it this way, that was an extreme way, you can implement in the Android code, but if you want to use it from the .NET Standard/PCL Project, you have to write an event, or write through dependency service.

Fun fact: this GitHub project’s name is too large, so you can’t Debug it without renaming.