动态更改 android 的强调色

Dynamically change android's accent color

我将 Android 的强调色设置为灰色,因此它在任何主题(浅色或深色)中看起来都很正常。例如,灰色非常适合编辑控件,但事实证明它也用于警告取消按钮的文本。所以现在它在浅色主题中看起来很好,但在深色主题中非常糟糕。

如何从 Xamarin.Forms 应用动态更改 Android 的 colorAccent

编辑:这是 my theme changing code 目前的情况。 (我没有使用 AppThemeBinding 因为这种方法允许两个以上的主题)

你不能,因为强调色是在主题中定义的,而主题是 read-only。我假设动态意味着以编程方式。

在Xamarin Forms中,我们可以使用DependencyService to call native method. Fortunately, Android document provide the method setLocalNightMode来修改本地的DarkMode。需要注意的是,此方法不能修改手机设置的配置。

现在我们可以创建一个IDarkModeService接口:

public interface IDarkModeService
{
    void SetDarkMode(bool value);
}

然后在Android解决方案中实现它的方法:

public class DarkModeService : IDarkModeService
{
    public void SetDarkMode(bool value)
    {
        if (value)
        {
            
     MainActivity.instance.Delegate.SetLocalNightMode(AppCompatDelegate.ModeNightYes);
            MainActivity.instance.Recreate();
        }
        else
        {
       
     MainActivity.instance.Delegate.SetLocalNightMode(AppCompatDelegate.ModeNightNo);
            MainActivity.instance.Recreate();
        }
      
    }
}

这里我们需要从MainActivity

创建一个静态的instance
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
    public static MainActivity instance { set; get; }

    protected override void OnCreate(Bundle savedInstanceState)
    {
        TabLayoutResource = Resource.Layout.Tabbar;
        ToolbarResource = Resource.Layout.Toolbar;

        instance = this;

        base.OnCreate(savedInstanceState);

        Xamarin.Essentials.Platform.Init(this, savedInstanceState);
        global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
        LoadApplication(new App());
    }
   
}

}

并且不要忘记在 styles.xml 中添加配置以使应用程序支持 DarkMode:

<style name="MainTheme" parent="Theme.AppCompat.DayNight.NoActionBar"></style>

最后,我们可以调用Xamarin Forms中的依赖方法如下:

private async void ShowDialog_Clicked(object sender, EventArgs e)
{
    await DisplayAlert("Alert", "You have been alerted", "OK");
}

private void SetDarkMode_Clicked(object sender, EventArgs e)
{
    DependencyService.Get<IDarkModeService>().SetDarkMode(true);
}

private void CancelDarkMode_Clicked(object sender, EventArgs e)
{
    DependencyService.Get<IDarkModeService>().SetDarkMode(false);
}

效果:

==================================更新======== ========================

如果需要自定义每个 Theme 的样式,可以在运行时交换 Theme

首先,您可以在 Xamrin Forms 中存储一个主题标志(DarkMode):

private void SetDarkMode_Clicked(object sender, EventArgs e)
{
    Preferences.Set("DarkMode", true);
    DependencyService.Get<IDarkModeService>().SetDarkMode(true);
}

private void CancelDarkMode_Clicked(object sender, EventArgs e)
{
    Preferences.Set("DarkMode", false);
    DependencyService.Get<IDarkModeService>().SetDarkMode(false);
}

然后在里面添加每个Theme样式styles.xml:

<?xml version="1.0" encoding="utf-8" ?>
<resources>

  <style name="MainTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
   
  </style>

  <style name="DayTheme" parent="MainTheme">
   
  </style>

  <style name="NightTheme" parent="MainTheme" >
    <item name="buttonBarPositiveButtonStyle">@style/positiveBtnStyle</item>
    <item name="buttonBarNegativeButtonStyle">@style/negativeBtnstyle</item>
  </style>

  <!--style of sure button-->
  <style name="positiveBtnStyle" parent="Widget.AppCompat.Button.ButtonBar.AlertDialog">
    <item name="android:textColor">#0000ff</item>
  </style>

  <!--style of cancel button-->
  <style name="negativeBtnstyle" parent="Widget.AppCompat.Button.ButtonBar.AlertDialog">
    <item name="android:textColor">#999999</item>
  </style>

</resources>

最后,在MainActivity.cs中创建视图之前更改主题:

public static MainActivity instance { set; get; }
protected override void OnCreate(Bundle savedInstanceState)
{
    TabLayoutResource = Resource.Layout.Tabbar;
    ToolbarResource = Resource.Layout.Toolbar;

    instance = this;

    var darkMode = Preferences.Get("DarkMode", false);
    if (darkMode)
    {
        this.SetTheme(Resource.Style.NightTheme);
    }
    else
    {
        this.SetTheme(Resource.Style.DayTheme);
    }

    base.OnCreate(savedInstanceState);

    Xamarin.Essentials.Platform.Init(this, savedInstanceState);
    global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
    LoadApplication(new App());
}

现在我们可以看到按钮的颜色样式会发生变化:

我找到了解决方案!

  1. 所有你必须为 MyApp.Android\Resources\values\colors.xml 中的浅色主题和 MyApp.Android\Resources\values-night\colors.xml 中的深色主题添加强调(或任何其他)颜色。然后在 styles.xml <item name="colorAccent">@color/colorAccent</item> 的主题中按名称引用该颜色。 现在,当设备切换到深色主题时,强调色也会改变。
  2. 现在。如果您的应用程序中有手动主题控制怎么办?您可以强制 Android 显示浅色或深色主题的颜色。为共享项目添加类似界面:
namespace MyApp.Core.Models.InterplatformCommunication
{
    public interface INightModeManager
    {
        NightModeStyle DefaultNightMode { get; set; }
    }

    public enum NightModeStyle
    {
        /// <summary>
        /// An unspecified mode for night mode.
        /// </summary>
        Unspecified = -100,

        /// <summary>
        /// Mode which uses the system's night mode setting to determine if it is night or not.
        /// </summary>
        FollowSystem = -1,

        /// <summary>
        /// Night mode which uses always uses a light mode, enabling non-night qualified resources regardless of the time.
        /// </summary>
        No = 1,

        /// <summary>
        /// Night mode which uses always uses a dark mode, enabling night qualified resources regardless of the time.
        /// </summary>
        Yes = 2,

        /// <summary>
        /// Night mode which uses a dark mode when the system's 'Battery Saver' feature is enabled, otherwise it uses a 'light mode'.
        /// </summary>
        AutoBattery = 3
    }
}
  1. 在 Android 项目中添加此实现。设置 AppCompatDelegate.DefaultNightMode 强制应用在不重启应用的情况下加载浅色或深色主题的资源。
[assembly: Xamarin.Forms.Dependency(typeof(NightModeManager))]
namespace MyApp.Droid.Dependences
{
    public class NightModeManager : INightModeManager
    {
        public NightModeStyle DefaultNightMode
        {
            get => (NightModeStyle)AppCompatDelegate.DefaultNightMode;
            set => AppCompatDelegate.DefaultNightMode = (int)value;
        }
    }
}
  1. 更改应用主题时添加此逻辑(AppTheme 是自定义枚举):
private static void UpdateNativeStyle(AppTheme selectedTheme)
        {
            NightModeStyle style = selectedTheme switch
            {
                AppTheme.Dark => NightModeStyle.Yes,
                AppTheme.Light => NightModeStyle.No,
                AppTheme.FollowSystem => NightModeStyle.FollowSystem,
                _ => throw new InvalidOperationException("Unsupported theme"),
            };

            var nightModeManager = DependencyService.Get<INightModeManager>();
            nightModeManager.DefaultNightMode = style;
        }

更多相关信息:

如旁注,设置后:

AppCompatDelegate.DefaultNightMode = ... 

您需要重新创建 activity 才能将更改应用到当前页面:

activity.Recreate();

例如,如果您正在使用 Xamarin 的 CrossCurrentActivity 插件,您可以执行以下操作:

CrossCurrentActivity.Current.Activity.Recreate();