如何使用多个res.srcDirs并通过gradle覆盖某些资源

19

我想基于不同的productFlavors构建我的应用程序的不同版本,但我需要一些灵活性,目前还无法实现。

这是我的文件夹结构:

+src
  +main
    +java
    +res
  +base
    +java
    +res
  +custom1
    +java
    +res
  +custom2
    +res

通用代码位于 main(一个服务)上,基本 UI 位于 base(一个活动)上。然后定义了一个新的 UI 于自定义版本的应用程序中,该版本位于 custom1 中(新活动)。目前这个运行得很好。但是我需要另一个版本的应用程序(custom2),它使用 base UI,但更改了一些资源(图标、字符串或颜色)。

我在 build.gradle 文件中尝试的方法是:

android{
    ...
  productFlavors {
    base {
      ...
    }
    custom1 {
      ...
    }
    custom2 {
      ...
    }
  }

  sourceSets{
    custom2{
      java.srcDirs = ['src/base/java']
      res.srcDirs = ['src/custom2/res', 'src/base/res']
    }
  }
}

为了指定custom2将使用base的源代码和资源以及custom2自己的资源。

问题是我得到了一个:

Error: Duplicate resources: <project_path>/src/base/res/values-es/strings.xml:string-en/app_name, <project_path>/src/custom2/res/values-es/strings.xml:string-en/app_name
因为app_namebasecustom2上都有定义,但我的目标是用custom2中的资源覆盖base中的资源定义。
我知道如果我在res.srcDirs中不指定任何内容来代替custom2app_name将在main中被覆盖,但同时base中的所有资源都将无法使用。
这种方法正确吗?或者我正在滥用Gradle所提供的灵活性?或者有一种方法可以实现我想要做的事情吗?
提前感谢!
3个回答

15

以这种方式是无法实现的。

资源合并有优先级顺序:

合并的资源来源于三种不同类型的源:

  • 主要的资源,与主要的源集相关联,通常位于src/main/res
  • 来自构建类型和口味的变体覆盖层。
  • 库项目依赖项通过其aar捆绑包中的res条目提供资源。

详情请参见此处

如上所述,每个sourceSet可以定义多个资源文件夹。例如:

android.sourceSets {
   main.res.srcDirs = ['src/main/res', 'src/main/res2']
}

在这种情况下,两个资源文件夹具有相同的优先级。这意味着如果一个资源在两个文件夹中都声明了,合并将失败并报告错误

你的情况也是一样的。不能以这种方式重复使用相同的资源。


感谢您的评论。我之前已经阅读了那篇链接,但是漏掉了最后一段。不过,这是否意味着我不能做我想要的事情呢?或者也许另一种方法可以解决我的问题? - Parmaia
说实话,根据我的经验,你不能用这种方式来做。你可以在build.gradle中使用buildConfigField和resValue来设置图标和资源。 - Gabriele Mariotti
1
经过许多测试,我很遗憾地得出结论,这是不可能完成的。我认为可以通过排除我想要覆盖的资源来实现,但是res.exclude目前还不支持(java.exclude也不支持)。所以现在只能等待并找到一个不太优雅的解决方法...无论如何,感谢您的评论。 - Parmaia
我事先不知道。我在为即将到来的未来情况做计划。我们正在开发一个应用程序,由各种运营商使用,并且每个运营商都希望拥有自己的外观和感觉,从完全新的活动集合到简单的图标和/或颜色更改不等。因此需要这种灵活性程度。 - Parmaia
答案中的链接已损坏。截至今天的有效链接为 - https://developer.android.com/studio/write/add-resources - Sri Harsha Arangi

1
我能够通过在src的兄弟节点添加另一层名为shared的文件夹来实现类似以下这样的效果,然后手动检查和组装必要的资源到一个额外的脚本中以避免重复。脚本在此处可用:https://github.com/smbgood/examples/blob/master/family-resources.gradle 资源将放置在如下的文件夹中(我们有三个应用程序“家族”,IG、JB和CB):
/shared/JB/res/values/ids.xml
对于我们现在拥有的每个构建变量,ids.xml中都有一个“family”属性,确定要使用哪个应用程序家族。在构建时,脚本会检查在shared中找到的drawables和xml文件,并将它们与变量中存在的项目进行比较,最后删除重叠的项目,最终将所需项目写入/assembled/文件夹。

1
如果您能分享带有示例项目的解决方案,那将是非常有帮助的。我非常想实现这个解决方案。谢谢! - Farhan

0

你可以设置一个路径来覆盖res文件,而在custom2/res中没有基础的res文件。

custom2.res.srcDir=['src/custom2/res']

首先它会在src/custom2/res路径下搜索文件,如果文件不存在,则在编译时自动查找src/main/res。


这并没有回答问题。一旦您拥有足够的声望,您将能够评论任何帖子;相反,提供不需要询问者澄清的答案。- 来自审核 - user1773603

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接