【问题标题】:Can the Android layout folder contain subfolders?Android布局文件夹可以包含子文件夹吗?
【发布时间】:2022-01-05 12:35:29
【问题描述】:

现在,我将每个 XML 布局文件都存储在 'res/layout' 文件夹中,因此管理小型项目是可行且简单的,但是当有大型和繁重项目的情况下,则应该有布局文件夹内需要的层次结构和子文件夹。

例如

layout
-- layout_personal
   -- personal_detail.xml
   -- personal_other.xml
--layout_address
  -- address1.xml
  -- address2.xml

同理,我们希望为大型应用程序设置子文件夹,那么在 Android 项目中有没有办法做到这一点?

我可以在 layout 文件夹中创建 layout-personal 和 layout_address 子文件夹,但是当需要使用 R.layout._______ 访问 XML 布局文件时,当时没有任何 XML 布局弹出-在菜单中。

【问题讨论】:

标签: android xml android-layout gradle build.gradle


【解决方案1】:

你可以用 gradle 做到这一点。我制作了一个 demo project 来展示如何操作。

诀窍是利用 gradle 的能力merge multiple resource folders,并在 sourceSets 块中设置 res 文件夹以及嵌套的子文件夹。

奇怪的是,您不能在声明容器资源文件夹之前声明该文件夹的子资源文件夹。

以下是演示中 build.gradle 文件中的 sourceSets 块。请注意,首先声明子文件夹。

sourceSets {
    main {
        res.srcDirs =
        [
                'src/main/res/layouts/layouts_category2',
                'src/main/res/layouts',
                'src/main/res'
        ]
    }
}

此外,您的实际资源文件(png、xml 布局等)的直接父级仍然需要与 specification 相对应。

【讨论】:

  • 是否可以使用可绘制文件夹执行此操作?我只是尝试了没有运气,即使考虑到声明顺序。
  • 不错!请记住,您实际上只是在利用 gradle 的合并资源文件夹的能力,这很有意义。
  • 文件夹中的文件名仍然必须是唯一的,并且在代码中您必须知道这一点,例如R.layout.list_item 来自 moviescinemas 文件夹,因此仍然适用平面命名。
  • 不幸的是,build:gradle:0.14.4buildToolsVersion "21.1.1" 对我不起作用。
  • 这行得通,但是答案在解释您添加的每个子文件夹中都需要有一个 second 子文件夹方面做得不够,只能称为“布局”,并且所有布局文件都需要放在 second 子文件夹中。阅读数十个 cmets 来解决这个问题远非理想。对于仍在寻找的人来说,设置额外的“布局”子文件夹是URI is not registered 问题的解决方案。
【解决方案2】:

答案是否定的。

我想提请您注意这本书Pro Android 2,它指出:

还有一些值得注意的 关于资源的限制。 一、Android只支持线性 预定义中的文件列表 res下的文件夹。例如,它 不支持嵌套文件夹 布局文件夹(或其他 res 下的文件夹)。

第二,有一些相似之处 在资产文件夹和原始文件夹之间 res下的文件夹。两个文件夹都可以 包含原始文件,但文件 在 raw 中被认为是资源 资产中的文件不是。

请注意,因为 不考虑 assets 文件夹 资源,你可以放一个任意 文件夹和文件的层次结构 它。

【讨论】:

  • 我希望我们的下一个最大希望是 Eclipse 或 IntelliJ 插件,它可以根据文件名前缀折叠文件。
  • @Justin 查看我为 gradle 发布的解决方案。
  • 我想一种解决方案可能是按照您希望它们分类的方式命名文件,因此它看起来像是在子文件夹中。我在名称的开头添加了一个“子文件夹标签”。例如。所有活动都是“activity_activityname.xml”,子活动可以是“activity_activityname_childactivity.xml”等等。任何其他文件都是“other_filename.xml”。我这样做是为了避免混淆。妈的,有人打我。三年前>。
  • 这里需要注意的是,通过将raw 视为资源,您可以获得依赖于密度/方向/版本/等的条带化,而使用assets,您必须手动执行此操作。
  • 选择这个答案作为反对安卓主流的论据。
【解决方案3】:

我只是想为遇到麻烦的人添加 eskis 的绝妙答案。 (注意:这只会在“项目”视图中起作用并且看起来像单独的目录,而不是“安卓”视图。)

经过以下测试。 构建工具版本 = 23.0.0 分级 1.2.3 & 1.3.0

这就是我如何让我的项目与一个已经构建的项目一起工作。

  1. 将所有 XML 文件从布局目录中复制出来,并将它们放入桌面上的目录或其他地方进行备份。
  2. 删除整个布局目录(确保从第 1 步开始备份所有内容!!!)
  3. 右键res目录,选择新建>目录。
  4. 将此新目录命名为“布局”。 (这可以是您想要的任何内容,但它不会是稍后出现的“片段”目录或“活动”目录)。
  5. 右键单击新的“布局”目录并选择新建 > 目录。 (这将是您将在其中拥有的 XML 文件类型的名称,例如“片段”和“活动”)。
  6. 右键单击“片段”或“活动”目录(注意:这不一定是“片段”或“活动”,这只是我使用的示例)并再次选择新建 > 目录并将此目录命名为“布局”。 (注意:这必须命名为“布局”!!!非常重要)。
  7. 将所需的 XML 文件放入桌面上备份的新“布局”目录中。
  8. 对任意数量的自定义目录重复步骤 5 到 7。
  9. 一旦完成,进入你的模块 gradle.build 文件并像这样创建一个 sourceSets 定义......(确保 'src/main/res/layouts' 和 'src/main/res' 始终是底部两个!!!!就像我在下面展示的那样)。

    sourceSets {
        main {
            res.srcDirs =
                    [
                            'src/main/res/layouts/activities',
                            'src/main/res/layouts/fragments',
                            'src/main/res/layouts/content',
                            'src/main/res/layouts',
                            'src/main/res'
                    ]
        }
    }
    
  10. 利润 $$$$

但是说真的.. 这就是我让它工作的方式。如果有人有任何问题,请告诉我。我可以提供帮助。

图片比文字更有价值。

【讨论】:

  • layout-v21等布局目录呢?
  • 我没有测试过,但我想你只是在布局文件夹旁边创建一个 layout-v21 文件夹。
  • 适用于 gradle 2.0.0 和 buildToolsVersion 23.0.2!注意配置值是'src/main/res/layouts/content',这个配置的路径是'src/main/res/layouts/content/layout'
  • 我遇到了这个stackoverflow.com/questions/41934004/…的问题
  • 搜索了一段时间没有运气,这是唯一对我有用的解决方案。感谢您用图片发布如此详细的答案!
【解决方案4】:

不可能,但布局文件夹按名称排序。所以,我在布局文件名前加上我的包名。例如。对于“买”和“玩”两个包:

buying_bought_tracks.xml
buying_buy_tracks.xml
playing_edit_playlist.xml
playing_play_playlist.xml
playing_show_playlists.xml

【讨论】:

    【解决方案5】:

    我为 Android Studio 使用Android File Grouping 插件。它实际上并不允许您创建子文件夹,但它可以显示您的文件和资源,因为它们位于不同的文件夹中。而这正是我想要的。

    您可以通过

    安装“Android文件分组”插件

    窗户:

    Android Studio -> 文件 -> 设置 -> 插件。

    苹果机:

    Android Studio -> Android Studio 选项卡(左上角)-> 首选项 -> 插件 -> 安装 JetBrains 插件..

    对于 Mac,我能够对其进行测试,但无法搜索插件。所以我从here 下载了插件,并使用了上述设置中的Install plugin from disk 选项。

    【讨论】:

    • 也分享那个插件的链接!
    • 完成。无论如何,您始终可以在 Android Studio -> 文件 -> 设置 -> 插件中搜索“Android 文件分组”
    • 插件在“Android”视图上不起作用,只能在“项目”视图上运行:/
    【解决方案6】:

    我认为解决这个问题的最优雅的解决方案(鉴于不允许使用子文件夹)是在文件名前加上您将放置它的文件夹的名称。例如,如果您有一堆 Activity、Fragment 或只是名为“places”的一般视图的布局,那么您应该在其前面加上 places_my_layout_name。至少这解决了以更容易在 IDE 中找到它们的方式组织它们的问题。这不是最棒的解决方案,但总比没有好。

    【讨论】:

      【解决方案7】:

      现在使用 Android Studio 和 Gradle,您可以在项目中拥有多个资源文件夹。不仅可以组织您的布局文件,还可以组织任何类型的资源。

      它不完全是一个子文件夹,但可能会分隔您的应用程序的各个部分。

      配置是这样的:

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

      检查documentation

      【讨论】:

      • 我注意到每次在此设置下对布局进行更改时,我总是需要clean 才能看到它反映在应用程序上。
      【解决方案8】:

      小问题

      我可以通过关注top answer 这个问题来实现子文件夹。

      但是,随着项目越来越大,您将拥有许多子文件夹:

      sourceSets {
          main {
              res.srcDirs =
                  [
                          'src/main/res/layouts/somethingA',
                          'src/main/res/layouts/somethingB',
                          'src/main/res/layouts/somethingC',
                          'src/main/res/layouts/somethingD',
                          'src/main/res/layouts/somethingE',
                          'src/main/res/layouts/somethingF',
                          'src/main/res/layouts/somethingG',
                          'src/main/res/layouts/somethingH',
                          'src/main/res/layouts/...many more',
                          'src/main/res'
                  ]
          }
      }
      

      问题不大,但是:

      • 这不是很漂亮,因为列表变得很长。
      • 每次添加新文件夹时,您都必须更改 app/build.gradle

      改进

      所以我写了一个简单的 Groovy 方法来抓取所有嵌套的文件夹:

      def getLayoutList(path) {
          File file = new File(path)
          def throwAway = file.path.split("/")[0]
          def newPath = file.path.substring(throwAway.length() + 1)
          def array = file.list().collect {
              "${newPath}/${it}"
          }
          array.push("src/main/res");
          return array
      }
      

      将此方法粘贴到您的app/build.gradle 中的android {...} 块之外。


      如何使用

      对于这样的结构:

      <project root>
      ├── app <---------- TAKE NOTE
      ├── build
      ├── build.gradle
      ├── gradle
      ├── gradle.properties
      ├── gradlew
      ├── gradlew.bat
      ├── local.properties
      └── settings.gradle
      

      像这样使用它:

      android {
          sourceSets {
              main {
                  res.srcDirs = getLayoutList("app/src/main/res/layouts/")
              }
          }
      }
      

      如果你有这样的结构:

      <project root>
      ├── my_special_app_name <---------- TAKE NOTE
      ├── build
      ├── build.gradle
      ├── gradle
      ├── gradle.properties
      ├── gradlew
      ├── gradlew.bat
      ├── local.properties
      └── settings.gradle
      

      你会这样使用它:

      android {
          sourceSets {
              main {
                  res.srcDirs = getLayoutList("my_special_app_name/src/main/res/layouts/")
              }
          }
      }
      

      说明

      getLayoutList()a relative path 作为参数。 relative path 相对于项目的根目录。所以当我们输入"app/src/main/res/layouts/"时,它会将所有子文件夹的名称作为一个数组返回,这将与:

                  [
                          'src/main/res/layouts/somethingA',
                          'src/main/res/layouts/somethingB',
                          'src/main/res/layouts/somethingC',
                          'src/main/res/layouts/somethingD',
                          'src/main/res/layouts/somethingE',
                          'src/main/res/layouts/somethingF',
                          'src/main/res/layouts/somethingG',
                          'src/main/res/layouts/somethingH',
                          'src/main/res/layouts/...many more',
                          'src/main/res'
                  ]
      

      这是带有 cmets 的脚本,便于理解:

      def getLayoutList(path) {
          // let's say path = "app/src/main/res/layouts/
          File file = new File(path)
      
          def throwAway = file.path.split("/")[0]
          // throwAway = 'app'
      
          def newPath = file.path.substring(throwAway.length() + 1) // +1 is for '/'
          // newPath = src/main/res/layouts/
      
          def array = file.list().collect {
              // println "filename: ${it}" // uncomment for debugging
              "${newPath}/${it}"
          }
      
          array.push("src/main/res");
          // println "result: ${array}" // uncomment for debugging
      
          return array
      }
      

      希望对你有帮助!

      【讨论】:

      • 我使用了您的方法,但如何在活动中使用它?我在名为 admin 的布局文件夹中创建了子文件夹,当我处于活动状态并使用 setContentView(R.layout.Admin.admin_home .) Admin.admin_home 时,它​​具有 admin_home 布局
      • 应该可以正常使用了,比如R.layout.admin_home。该脚本应该能够自动为您选择名称。让我知道你是否可以让它工作。
      • 它运行良好,但在将这一行 array.push("src/main/res"); 更改为 def res="src/main/res"; array.push(res); 后,因为我有一个错误,因为 push want Gstring parameter not String one .if 任何人而不是我发现这个错误你可以编辑
      • 子文件夹中的布局无法解析 android 命名空间。有什么想法吗?
      • 你能用// uncomment for debugging取消注释这些行,然后检查输出src/main/res/layouts/&lt;your_layout_files&gt;是否被正确检测到?
      【解决方案9】:

      现在我们可以使用名为“Android File Grouping”的 JetBrains 插件轻松完成操作

      查看此链接

      Android File Grouping

      【讨论】:

      • 必须标记为正确答案,因为仅使用简单的名称约定组织分组。无需更改 gradle,甚至无需更改应用程序的默认目录结构。所有布局仍保留在布局文件夹中,但分组在Android Studio
      • 不应该是公认的答案,因为它在 Android 视图中不起作用......(但我喜欢这个想法^^)
      • 这不适用于最后一个版本的android...有更新版本吗?
      【解决方案10】:

      我这样做的一种方法是在项目中与实际 res 文件夹处于同一级别创建一个单独的 res 文件夹,然后你可以在你的应用程序 build.gradle 中使用它

      android {
          //other stuff
      
          sourceSets {
              main.res.srcDirs = ['src/main/res', file('src/main/layouts').listFiles()]
          }
      }
      

      然后,您的新 res 文件夹的每个子文件夹都可以与每个特定屏幕或应用程序中的某些内容相关,并且每个文件夹将有自己的 layout / drawable / values 等,让事情井井有条,而你不需要必须像其他一些答案一样手动更新 gradle 文件(只需在每次添加新资源文件夹时同步您的 gradle 以便它知道它,并确保在添加您的 xml 文件之前添加相关的子文件夹)。

      【讨论】:

        【解决方案11】:

        检查Bash Flatten Folder script 将文件夹层次结构转换为单个文件夹

        【讨论】:

        • 很快就会去看看。谢谢
        • 该脚本将更改文件名以在文件名中包含目录层次结构。这将如何与您的 Android IDE 一起使用,文件名随处可见?
        【解决方案12】:

        如果您使用已批准答案中的方法,并想稍微改进一下,请像这样更改 gradle 设置:

            sourceSets {
            main {
                res.srcDirs = [
                    file("src/main/res/layouts/").listFiles(),
                    "src/main/res/layouts",
                    "src/main/res"
                ]
            }
        }
        

        所以如果你添加更多的文件夹和布局,你不需要回到这里附加一长串的源文件夹,让 gradle 为你获取所有的文件夹。

        【讨论】:

          【解决方案13】:

          如果您在 linux 或 mac 机器上进行开发,一种解决方法是创建包含指向您的布局文件的符号链接的子文件夹。只需使用带有 -s 的 ln 命令

          ln -s PATH_TO_YOUR_FILE

          问题在于,您的 Layout 文件夹仍然包含所有 .xml 文件。但是您可以通过使用子文件夹来选择它们。这是最接近您想要拥有的东西。

          我刚刚读到,如果您使用 Vista 或更高版本,这也可能适用于 Windows。有这个mklink 命令。自己google一下,没用过。

          另一个问题是,如果您打开文件并尝试再次打开它,插件会抛出 NULL 指针异常。但它并没有挂断。

          【讨论】:

          • 放入符号链接违背了通过将组件分离到不同的子目录来模块化存储的目的:有效地使其更复杂而不是更少。 Gradle 允许指定额外的资源目录。
          【解决方案14】:

          虽然针对多个资源集的所有建议都可能有效,但问题是 Android Studio Gradle 插件的当前逻辑不会在资源文件针对嵌套资源集进行更改后更新它们。当前实现尝试使用startsWith()检查资源目录,因此嵌套的目录结构(即src/main/res/layout/layouts和src/main/res/layout/layouts_category2)将选择src/main/res /layout/layouts 始终如一,从不真正更新更改。最终结果是您每次都必须重新构建/清理项目。

          我在https://android-review.googlesource.com/#/c/157971/ 提交了一个补丁,试图帮助解决问题。

          【讨论】:

          【解决方案15】:

          @eski 的最佳答案很好,但是代码使用起来并不优雅,所以我在 gradle 中编写了一个 groovy 脚本以供一般使用。它适用于所有构建类型和产品风格,不仅可用于布局,还可以为任何其他资源类型(如可绘制)添加子文件夹。代码如下(放在项目级gradle文件android块中):

          sourceSets.each {
              def rootResDir = it.res.srcDirs[0]
              def getSubDirs = { dirName ->
                  def layoutsDir = new File(rootResDir, dirName)
                  def subLayoutDirs = []
                  if (layoutsDir.exists()) {
                      layoutsDir.eachDir {
                          subLayoutDirs.add it
                      }
                  }
                  return subLayoutDirs
              }
              def resDirs = [
                      "anims",
                      "colors",
                      "drawables",
                      "drawables-hdpi",
                      "drawables-mdpi",
                      "drawables-xhdpi",
                      "drawables-xxhdpi",
                      "layouts",
                      "valuess",
              ]
              def srcDirs = resDirs.collect {
                  getSubDirs(it)
              }
              it.res.srcDirs = [srcDirs, rootResDir]
          }
          

          实践中怎么做?

          例如,我想为layout创建名为activity的子文件夹,在resDirs变量中添加一个任意名称的字符串,例如layouts,那么布局xml文件应该放在res\layouts\activity\layout\xxx.xml中。

          如果我想为drawable创建名为selectors的子文件夹,在resDirs变量中添加一个任意名称的字符串,例如drawables,那么drawable xml文件应该放在res\drawables\selectors\drawable\xxx.xml中。

          layoutsdrawables等文件夹名称定义在resDirs变量中,可以是任意字符串。 您创建的所有子文件夹,例如activityselectors,都被视为与res 文件夹相同。所以在selectors文件夹中,我们必须另外创建drawable文件夹,并将xml文件放入drawable文件夹,之后gradle才能正常识别xml文件为drawable。

          【讨论】:

          • 这个应该算是正确答案吧!简单而完美!
          【解决方案16】:
          • 第 1 步:右键单击布局 - 在资源管理器中显示
          • 第二步:打开layout文件夹,直接创建子文件夹:layout_1, layout_2 ...
          • 第三步:打开layout_1创建文件夹layout(注意:必填名称为layout),打开layout_2文件夹创建layout子目录(注意:必填名称为layout)...
          • 第四步:将xml文件复制到layout_1和layout_2的layout子目录中
          • 第 5 步:运行 buid.grade(模块应用)中的代码并立即点击同步:

          sourceSets {
              main {
                  res.srcDirs =
                      [
                          'src / main / res / layout / layout_1'
                          'src / main / res / layout / layout_2',
                          'src / main / res'
                      ]
              }
          }
          
          • 第 6 步:总结:以上所有步骤仅有助于集群文件夹并以“项目”模式显示,而“安卓”模式将正常显示。
          • 所以我认为命名前缀可能与集群文件夹一样有效。

          【讨论】:

          • 感谢Project 模式。我不明白子文件夹在哪里消失(Android 模式)。
          【解决方案17】:

          嗯,简短的回答是否定的。但是您绝对可以拥有多个res 文件夹。我认为,这与拥有layout 文件夹的子文件夹一样接近。 Here's 你是怎么做到的。

          【讨论】:

          • 检查接受的答案。我没有亲自尝试过,但如果您可以检查和更新,那将会很有帮助!
          【解决方案18】:

          热门答案有几个缺点:您必须添加新的布局路径,AS 将新资源放置到 res\layouts 文件夹而不是 res\values

          结合我写的几个类似的答案:

          sourceSets {
              main {
                  res.srcDirs =
                          [
                                  'src/main/res',
                                  file("src/main/res/layouts/").listFiles(),
                                  'src/main/res/layouts'
                          ]
              }
          }
          

          我用这篇文章制作了文件夹:http://alexzh.com/tutorials/how-to-store-layouts-in-different-folders-in-android-project/。为了创建子文件夹,你应该使用这个菜单:New > Folder > Res Folder。

          更新

          几周后,我发现 Android Studio 没有注意到资源的变化。因此,出现了一些奇怪的错误。例如,布局继续显示旧尺寸、边距。有时 AS 找不到新的 XML 文件(尤其是在运行时)。有时它会混合view ids(对另一个 XML 文件的引用)。通常需要按Build &gt; Clean ProjectBuild &gt; Rebuild Project。阅读Rebuild required after changing xml layout files in Android Studio

          【讨论】:

          • 您的评论“我用这篇文章制作了文件夹(附加链接)”不是在 S.O 上提供解决方案的可接受方式。你的回答很模糊。您可以留下说明并向作者提供链接。否则,这只是懒惰。
          • @jungledev,好吧,最后我拒绝了这个想法,回到了传统的res文件夹,因为AS不完全支持将资源分成文件夹。可能我会更改此答案甚至删除。但是,当您说在 S.O. 上提供解决方案不是一种可接受的方式时,您是什么意思?
          • 我的意思就是我所说的。不要说“我是通过本教程完成的……(插入链接)。”您应该说的是:“我按照本教程(插入链接)进行操作,以下是对我有用的步骤。第 1 步、第 2 步、第 3 步……等等”您需要在答案中包含这些步骤。只需链接到教程是不可接受的。
          【解决方案19】:

          不能有子目录(很容易),但你可以有附加资源文件夹。很惊讶没有人提到它,但是保留默认资源文件夹,并添加更多:

              sourceSets {
                  main.res.srcDirs += ['src/main/java/XYZ/ABC']
              }
          

          【讨论】:

            【解决方案20】:

            在一个模块中,要拥有风味、风味资源(布局、值)和风味资源资源的组合,需要牢记的主要有两点:

            1. res.srcDirs 中添加资源目录时,请记住,在其他模块中,甚至在同一模块的src/main/res 中,也会添加资源目录。因此,使用附加分配 (+=) 的重要性,以免用新分配覆盖所有现有资源。

            2. 声明为数组元素的路径是包含资源类型的路径,即资源类型是一个res文件夹通常包含的所有子目录如color、drawable、layout、values等,res文件夹的名字可以改。

            一个示例是使用路径"src/flavor/res/values/strings-ES",但注意实践层次结构必须具有子目录values

            ├── module 
               ├── flavor
                  ├── res
                     ├── values
                        ├── strings-ES
                           ├── values
                              ├── strings.xml
                           ├── strings.xml
             
            

            框架根据类型精确识别资源,这就是为什么通常已知的子目录不能省略的原因。

            还请记住,风味中的所有strings.xml 文件将形成一个联合,因此资源不能被复制。反过来,这个形成风味文件的联合在模块的主模块之前具有更高的优先级。

            flavor {
                    res.srcDirs += [
                        "src/flavor/res/values/strings-ES"
                    ]
            }
            

            strings-ES 目录视为包含资源类型的自定义资源。

            GL

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-09-14
              • 1970-01-01
              • 2016-04-30
              • 2012-02-15
              • 2011-02-02
              相关资源
              最近更新 更多