如何从android中的另一个应用程序资源更改主题?

Posted

技术标签:

【中文标题】如何从android中的另一个应用程序资源更改主题?【英文标题】:How to change theme from another app resource in android? 【发布时间】:2017-06-11 20:09:36 【问题描述】:

我知道有一种方法可以通过在 styles.xml 中定义来设置主题并像那样使用它

setTheme(android.R.style.MyTheme);

但是,我想从我开发的另一个应用程序中获取主题。我知道资源名称,实际上我可以使用此代码块获取主题 ID;

Resources res = getPackageManager().getResourcesForApplication("com.example.theme");
int resThemeId = res.getIdentifier("my_theme","style","com.example.theme");

调试时,我看到 resThemeId 不为零。

然后,我需要设置这个主题的最终命令。在 super.onCreate() 函数之前,我尝试实现这个方法,但它似乎不起作用

setTheme(resThemeId);

但不是这个,如果我写下面的语句,我工作得很好

setTheme(android.R.style.Theme_Holo_Light);

那么,我应该怎么做才能使用来自不同包资源的主题?

【问题讨论】:

您可以从另一个包中获取资源 ID,但这些资源在您的 APK 中不存在。使用PackageManager#getResourcesForApplication(String packageName) 加载资源应该适用于可绘制对象、字符串、布尔值等。但是,我认为加载另一个 APK 的主题是不可能的,而且绝对不推荐。 虽然我可以从其他包资源中获得可绘制对象、字符串、布尔值,但我也希望获得主题。为什么不可能? 有可能(见我的回答)。您需要覆盖 getResrouces 以返回其他应用程序的资源,以便您可以应用主题。这也会返回来自其他应用程序的所有字符串、布局、可绘制对象和其他资源,因此不建议这样做。 【参考方案1】:

正如评论中已经提到的,您可以从其他应用程序访问资源,但使用其他应用程序主题将不起作用。


既然有证据总是一件好事,让我们看看源代码(我使用了 API 24 源代码)

Activity 上调用setTheme() 将调用ContextThemeWrapper 父类中的initializeTheme(),最终将调用onApplyThemeResource(..),而onApplyThemeResource(..) 又将尝试通过调用@987654327 从资源中加载实际的主题数据@

通过包装器Resources.Theme 跟踪链,我们可以在ResourcesImpl.ThemeImpl 中看到以下内容,其中调用AssetManager 将样式加载到主题中:

void applyStyle(int resId, boolean force) 
    synchronized (mKey) 
        AssetManager.applyThemeStyle(mTheme, resId, force);

        mThemeResId = resId;
        mKey.append(resId, force);
    

这是您尝试从其他应用加载 foreign 主题但失败的地方。


由于您需要使用的大多数方法都是静态调用或封装本地方法,因此似乎没有任何方法可以实现您想要的(例如应用或创建新主题)

即使您通过在上下文there is no accessible method to create or apply themes 上使用getAssets() 来获取其他应用程序的AssetManager

因此,使用其他应用资源的唯一方法是将资源添加到您的资源中。

【讨论】:

sharedUserId 怎么样?如果我将 sharedUserId 用于两个应用程序并使用相同的证书进行签名,会发生什么?我试过但没有运气。也许我在代码的某个地方做错了【参考方案2】:

那么,我应该怎么做才能使用来自不同包资源的主题?

出于多种原因,您不应该这样做。我写了一个简单的项目,表明只要包包含您的活动使用的资源,它确实是可能的。

见:https://github.com/jaredrummler/SO-41872033


基本上,您需要从活动中返回包的资源:

public class MainActivity extends AppCompatActivity 

  Resources resources;

  @Override protected void onCreate(Bundle savedInstanceState) 

    int themeResId = getResources().getIdentifier("AppTheme", "style", "com.example.theme");
    if (themeResId != 0) 
      setTheme(themeResId);
    

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
  

  @Override public Resources getResources() 
    if (resources == null) 
      try 
        resources = getPackageManager().getResourcesForApplication("com.example.theme");
       catch (PackageManager.NameNotFoundException e) 
        resources = super.getResources();
      
    
    return resources;
  


这只是为了表明它是可能的。同样,我建议您避免这种情况。

【讨论】:

是否可以通过这种方式在运行时更改资源?我的意思是,我想在从另一个资源设置主题后设置原始资源? 我不认为这是可能的,但我也不认为从另一个包中应用主题是可能的。这就是为什么我不推荐这个解决方案。您不想将另一个包的资源用于布局和活动中的任何可绘制对象。你到底想用这个实现什么? 我实际上尝试编写一个主题管理器和不同的 apk,其中包括一些主题(主题包括背景图像、默认声音、应用程序图标等)当我打开主题管理器应用程序时,我可以检索可绘制对象和列表他们从主题apk的相应。但是,当我更改主题时,我还想更改一些应用程序主题(如设置应用程序)。 (我想设置主色、强调色等)顺便说一句,我正在做 AOSP 项目,我可以轻松更改设置代码。我想要的只是从我的主题应用程序资源之一更改设置主题。【参考方案3】:

你看过这个演示吗:Multiple Theme Material Design

您可以查看此演示以了解运行时主题更改。

希望对你有帮助。

【讨论】:

该问题要求从设备上安装的不同软件包更改主题。这会将应用程序主题更改为当前包样式中预定义的内容。虽然这是应用主题的推荐方式,但它不能回答 OP 的问题。

以上是关于如何从android中的另一个应用程序资源更改主题?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Android 中更改 SettingsActivity 的主题?

如何从xml中的另一个包加载资源?

Android 如何从 Main Activity 中的另一个类调用 Activity 数据类型?

如何使用数据库将大量数据从一个活动传递到android中的另一个活动

如何将更改从主分支中的待处理更改列表迁移到 perforce 中的另一个分支

如何从主干中的另一个视图绑定元素上的事件