如何设置应用程序的主题,以避免错误的颜色转换?

背景

我正在开发“应用程序pipe理器”应用程序中的主题select器function,并且成功地为每个活动设置了主题。

再一次:这不是关于设定活动的主题。 这实际上对我来说工作正常。

问题

活动正在展示正确的主题,但应用程序本身在启动应用程序时显示错误,无论我做什么。

这是一个问题,因为当用户打开应用程序时,他会看到应用程序主题的背景,只有过了一会儿,活动才会显示出用户select的主题。

因此,如果应用程序具有白色背景,并且用户select了黑色背景的主题,则顺序为:

应用程序显示白色背景 – >活动正在启动,并显示黑色背景。

在屏幕截图中:

在这里输入图像说明

所以这是错误的。 在这种情况下,我需要它显示黑色到黑色的背景。

只有当用户select了一个基于Holo-light的主题(应用程序默认)时,它才能正常工作,因为颜色与打开应用程序时显示的活动颜色相匹配。

我试过了

我有一个想法设置应用程序的主题是空的一切,希望没有过渡将显示,使用像这样的东西:

<application ... android:theme="@android:style/Theme.Translucent.NoTitleBar" > 

事实上,这里有人提出了类似的解决scheme。

这是有效的,但是这会导致糟糕的体验,因为在某些设备上,直到显示第一个活动需要一些时间,因此,有相当多的时间用户什么都看不到,好像应用程序不是正在启动。

这个问题

我如何解决这个问题?

我已经尝试在从Application扩展的类中设置主题,但无论在什么地方,我都会调用它。

Solutions Collecting From Web of "如何设置应用程序的主题,以避免错误的颜色转换?"

透明的应用程序主题与淡入animation

我最初的build议是使用透明的全屏应用程序主题(无操作栏)。

与此相结合,我总是build议从应用程序主题到活动主题淡入淡出的alphaanimation。 当操作栏出现时,这可以防止对用户的干扰。

OP的代码将保持几乎完全相同,除了更改清单主题,并在一些基本活动类的onCreate()方法中添加alphaanimation,如下例所示:


清单主题定义为:

 android:theme="@android:style/Theme.Translucent.NoTitleBar" 

基本活动onCreate()方法:

 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // set your custom theme here before setting layout super.setTheme(android.R.style.Theme_Holo_Light_DarkActionBar); setContentView(R.layout.activity_main); overridePendingTransition(R.anim.fade_in, R.anim.fade_out); } 

基本淡入:

 <?xml version="1.0" encoding="utf-8"?> <alpha xmlns:android="http://schemas.android.com/apk/res/android" android:duration="2000" android:fromAlpha="0.0" android:toAlpha="1.0" /> 

基本淡出(不是真的需要,但为了完整性):

 <?xml version="1.0" encoding="utf-8"?> <alpha xmlns:android="http://schemas.android.com/apk/res/android" android:duration="2000" android:fromAlpha="1.0" android:toAlpha="0.0" /> 

当然,这里的animation持续时间要比你投入生产的时间要长 – 它们很长,所以你可以在开发阶段看到它们。


更新#1:

后来在@EmanuelMoecklin,@androiddeveloper的评论中已经注意到这一点。 这也包括在dentex的答案 。 然而,正如OP指出的,特别是在老设备上的弱点是用户在尝试启动应用程序时没有得到任何反馈。 看来这款应用需要很长时间才能发布。

在KitKat上,情况并非如此,因为状态栏和软键从透明变为黑色,而屏幕的其余部分仍然是透明的。

另一种方法是使用全屏黑色背景作为应用程序主题。 这就是Bitspin为Timely所做的事情 ,他们是由谷歌在该应用程序的惊人的UI的基础上显然买了。 看来这种方法在很多情况下是完全可以接受的。


更新#2:

为了加速发布的感知,纯黑色主题的另一种select是使用全屏应用程序徽标的图像 – “闪屏”风格。 一旦启动,再次褪色到活动。

对于透明主题,这是不可能的,使用透明的全屏图像。 Android忽略图像的透明度(或将透明图像覆盖到黑色背景上)。 OP在评论中指出了这一点。

我们可以有一个没有图像的透明主题,也可以有一个图像不透明的主题(也许是另一个问题的一个有趣的话题)。


关于使用清单别名的说明

@ sergio91pt的另一个build议是在清单中使用别名进行不同的活动。

虽然在某些情况下这可能是一种有用的技术,但在这种情况下,它有一些缺点:

  1. 用户为活动创build的任何主屏幕快捷方式将在主启动程序别名更改时停止工作,即每次用户更改主题时。
  2. 一些设备/启动器启动和停用不同的别名的速度很慢。 根据我的经验,这可能需要几秒钟(Galaxy Nexus 4.1 iirc),在此期间,您无法看到启动图标,或者您有2个图标。
  3. 每个可能的主题都需要一个不同的别名 – 如果有很多不同的主题,这可能会很麻烦。

有点晚了,但这可能是答案。 我偶然发现了它。

没有入场活动,没有自定义animation,没有黑客入侵。 只是主题中的一个属性。 Android深埋在资源之内。

将以下属性添加到您的应用主题中:

 <!-- ~ From Theme.NoDisplay, this disables the empty preview window probably ~ with an incorrect theme. --> <item name="android:windowDisablePreview">true</item> 

你完成了。 好好享受!

为了修复应用程序启动时的任何闪烁(动作栏,标题…),我已经设置了清单

android:theme="@android:style/Theme.NoTitleBar"

对于我的主要活动(一个标签容器和一个设置活动,我从哪里切换主题,基于全黑和光)

如果你使用一些“发射器活动”或“飞溅活动”,也可以为他们应用Theme.NoTitleBar ,那么:

已宣布Theme.NoTitleBar ,每一个活动,在onCreate你必须:

  1. 使用setTitle(...)和THEN正确设置标题

  2. 设置主题setTheme(R.style.CustomAppTheme) BEFORE setContentView(...)
    (你已经这样做了);

这将防止在切换主题(如果在“即时”完成)和应用程序启动时闪烁的操作栏/标题。

如果您想要自定义操作栏外观,这意味着默认的操作栏不会在您之前闪烁。

转换颜色是从清单上的活动主题(如果未设置,应用程序)中检索的。

目前,解决这个限制的唯一方法是为每个真实活动创build一个虚拟子类,例如。 MyActivityLight ,声明不同的主题。 活动别名将不起作用,该属性将被忽略。

对于使用IntentFilter的活动,只能使用PackageManager#setComponentEnabledSetting()来维护每个“types”中的一个。 请注意,更改可能需要几秒钟的时间。

对于由类名开始的活动,可以根据用户的主题推断出正确的前缀。


所以,让我们假设你有两个主题: AppTheme.DarkAppTheme.Light以及一些活动。 黑暗的主题是默认的主题。

原始清单:

 <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example"> <application android:theme="@style/AppTheme.Dark"> <activity android:name=".PrivateActivity" android:exported="false" /> <activity android:name=".ShowActivity"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> </intent-filter> </activity> </application> </manifest> 

将上面的所有活动更改为抽象类,并创build后缀LightDark虚拟子类。

那么清单应该像这样改变:

 <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example"> <!-- No application theme --> <application> <activity android:name=".PrivateActivityDark" android:theme="@style/AppTheme.Dark" android:exported="false" /> <activity android:name=".PrivateActivityLight" android:theme="@style/AppTheme.Light" android:exported="false" android:enabled="false" /> <activity android:name=".ShowActivityDark" android:theme="@style/AppTheme.Dark"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> </intent-filter> </activity> <activity android:name=".ShowActivityLight" android:enabled="false" android:theme="@style/AppTheme.Light"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> </intent-filter> </activity> </application> </manifest> 

那么你可以有这样的事情来获得主题的Activity类,给定一个抽象的Activity:

 public static ComponentName getThemedActivityName( Context ctx, Class<? extends Activity> clazz) { // Probably gets some value off SharedPreferences boolean darkTheme = isUsingDarkTheme(ctx); String baseName = clazz.getName(); String name += (darkTheme) ? "Dark" : "Light"; return new ComponentName(ctx, name); } public static void startThemedActivity( Activity ctx, Class<? extends Activity> clazz) { Intent intent = new Intent(); intent.setComponent(getThemedActivityName(ctx, clazz)); ctx.startActivity(intent); } 

当主题发生变化时, 还可以根据需要更改启用状态。

 public void onThemeChanged(Context ctx, boolean dark) { // save theme to SharedPreferences or similar and... final PackageManager pm = ctx.getPackageManager(); final String pckgName = ctx.getPackageName(); final PackageInfo pckgInfo; try { final int flags = PackageManager.GET_ACTIVITIES | PackageManager.GET_DISABLED_COMPONENTS; pckgInfo = pm.getPackageInfo(pckgName, flags); } catch (PackageManager.NameNotFoundException e) { throw new RuntimeException(e); } final ActivityInfo[] activities = pckgInfo.activities; for (ActivityInfo info: activities) { final boolean enable; if (info.theme == R.style.AppTheme_Light) { enable = !dark; } else if (info.theme == R.style.AppTheme_Dark) { enable = dark; } else { continue; } final int state = (enable) ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : PackageManager.COMPONENT_ENABLED_STATE_DISABLED; final String name = info.targetActivity; final ComponentName cmp = new ComponentName(pckgName, name); pm.setComponentEnabledSetting(cmp, state, PackageManager.DONT_KILL_APP); } } 

如果在循环中执行IPC会让你感到恐慌,只要对onThemeChanged()多次调用依次运行,就可以在辅助线程上asynchronous执行此操作。

请注意,在此示例中,我更改了所有活动(具有已知主题)的启用状态,但只有为具有意向filter的活动执行此操作。 如果这些活动没有被硬编码,那么这种方式更容易。

重要提示:正如Richard Le Mesurier等人所指出的,在启动器活动中使用这种技术可以删除或禁用主屏幕上的快捷方式(如果存在)。 这只是非启动器活动的解决scheme。