Android版式文件夹可以包含子文件夹吗?


551

现在,我将每个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目录中没有任何xml布局弹出窗口。菜单。



我认为此链接可能会对您有所帮助。blog.mindorks.com/...
迪利普Poudel

Answers:


475

你可以用gradle做到这一点。我做了一个演示项目,展示了如何。

诀窍是利用gradle 合并多个资源文件夹的能力,并在sourceSets块中设置res文件夹以及嵌套的子文件夹。

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

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

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

nested resources picture

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


6
是否可以使用可绘制文件夹执行此操作?我只是没有运气就尝试了,甚至考虑了声明顺序。
trevor-e

7
真好!请记住,您实际上只是在利用gradle 合并资源文件夹的功能,这才有意义。
eski 2014年

5
文件夹中的文件名仍然必须是唯一的,并且在代码中您必须知道例如R.layout.list_item来自moviescinemas文件夹,因此仍然使用平面命名。
TWiStErRob 2014年

5
不幸的是,这build:gradle:0.14.4和and 不适用于我buildToolsVersion "21.1.1"
卡米尔·莱洛尼克

32
此方法有效,但是答案做得很糟糕,无法解释所添加的每个子文件夹都需要在其中包含第二个子文件夹,该子文件夹只能被称为“布局”,并且所有布局文件都需要放在第二个子文件夹。阅读数十条评论以解决问题远非理想。对于仍在寻找的任何人,设置额外的“布局”子文件夹是解决此URI is not registered问题的方法。
aroth

233

答案是不。

我想引起您对这本书Pro Android 2的关注,该书指出:

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

其次,res下的资产文件夹和原始文件夹之间存在一些相似之处。这两个文件夹都可以包含原始文件,但是原始文件中的文件被视为资源,资产中的文件则不被视为资源。

请注意,由于资产文件夹的内容 不被视为资源,因此可以在其中放置任意层次的文件夹和文件


3
我对我们的下一个最大希望是,Eclipse或IntelliJ插件可以根据文件名前缀折叠文件。
杰里·布雷迪

15
@Justin签​​出我发布的解决方案。
eski 2014年

我猜一种解决方案可能是按照文件的分类来命名文件,因此看起来就在子文件夹中。我在名称的开头添加了“子文件夹标签”。例如。所有活动将是“ activity_activityname.xml”,子活动将是“ activity_activityname_childactivity.xml”,依此类推。任何其他文件将是“ other_filename.xml”。这就是我为避免混乱所做的。该死的,有人击败了我。到三年了。<我的糟糕,还没看到。
Vedavyas Bhat 2014年

1
重要的是要在此处注意,通过考虑raw将资源作为密度/方向/版本/等相关的条带,而assets必须手动进行。
TWiStErRob 2014年

1
选择此答案作为反对使用Android主流的论点。
Zon

77

我只是想补充一下爱斯基摩人对遇到麻烦的人的绝妙答案。(注意:这仅适用于“项​​目”视图中的单独目录,但不幸的是,“ android”视图中的目录看起来不同。)

经过以下测试。BuildToolsVersion = 23.0.0 gradle 1.2.3和1.3.0

这就是我让我与已经建立的项目一起工作的方式。

  1. 将所有XML文件复制到布局目录外,然后将其放在桌面上的某个目录中或进行备份。
  2. 删除整个布局目录(确保备份了步骤1中的所有内容!!!)
  3. 右键单击res目录,然后选择新建>目录。
  4. 将此新目录命名为“ layouts”。(这可以是您想要的任何内容,但不会是稍后出现的“片段”目录或“活动”目录)。
  5. 右键单击新的“布局”目录,然后选择“新建”>“目录”。(这将是您将在其中拥有的XML文件类型的名称,例如,“片段”和“活动”)。
  6. 右键单击“碎片”或“活动”目录(注意:这不必是“碎片”或“活动”,这只是我作为示例使用的内容),然后再次选择新>目录并命名该目录“布局”。(注意:必须将其命名为“ layout” !!!!!非常重要)。
  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. 利润$$$$

但是说真的..这就是我如何使它工作。让我知道是否有人有任何问题。

图片比文字更有价值。

目录结构


1
诸如这样的布局目录layout-v21呢?
Akshay Chordiya

4
我没有测试过,但是我可以想象您只是在布局文件夹旁边创建一个layout-v21文件夹。
hitch.united 2015年

适用于gradle 2.0.0和buildToolsVersion 23.0.2!请注意,配置值为'src/main/res/layouts/content'且此配置的路径为'src/main/res/layouts/content/layout'
cwhsu


2
搜索了一会儿没有运气,这是唯一对我有用的解决方案。感谢您发布带有图像的详细答案!
Machine Tribe'Mar

52

不可能,但是布局文件夹按名称排序。因此,我在布局文件名前加上包名。例如,两个包“购买”和“播放”:

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

20

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

您可以通过以下方式安装“ Android文件分组”插件:

视窗:

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

苹果电脑:

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

对于Mac,我可以对其进行测试,而无法搜索该插件。因此,我从这里下载了插件,并使用Install plugin from disk了上述设置中的选项。


还共享该插件的链接!
Paresh Mayani 2015年

做完了 无论如何,您始终可以在Android Studio中搜索“ Android文件分组”->文件->设置->插件
Sharpe

3
该插件在“ Android”视图上不起作用,仅在“ Project”视图上起作用:/
Hugo Gresse

18

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

它不完全是子文件夹,但可以分离应用程序的各个部分。

配置如下:

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

检查文档


2
我注意到,每次在此设置下更改布局时,我总是必须做一次clean才能看到它反映在应用程序上。
Pawan Kumar 2015年

16

我认为解决此问题的最佳方法(假设不允许使用子文件夹)是在文件名前加上您要放置在其中的文件夹的名称。例如,如果您有许多用于“活动”,“片段”或仅称为“地方”的普通视图的布局,则应在其前面放置places_my_layout_name。至少这解决了以在IDE中更容易找到它们的方式组织它们的问题。这不是最棒的解决方案,但总比没有好。


16

小问题

我可以通过遵循最佳答案来实现子文件夹此问题,。

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

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
}

将此方法粘贴android {...}到您的代码块中app/build.gradle


如何使用

对于这样的结构:

<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'
            ]

这是带有注释的脚本,可以帮助您理解:

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,它有admin_home布局时,我在活动,并使用setContentView(R.layout.Admin.admin_home .)Admin.admin_home没有解决
ASMAA拉沙德-

您应该可以正常使用它,例如R.layout.admin_home。该脚本应该能够自动为您选择名称。让我知道您是否可以使用它。
我是青蛙龙

它工作正常,但更改此行后array.push("src/main/res");def res="src/main/res"; array.push(res);因为我有一个错误,因为推送请求Gstring参数不是String one。如果有人而不是我发现此错误,则可以对其进行编辑
Asmaa Rashad

1
子文件夹中的布局无法解析android名称空间。有任何想法吗 ?
美洲虎

您能否用取消注释这些行,// uncomment for debugging然后检查是否src/main/res/layouts/<your_layout_files>正确检测到输出?
我是青蛙龙

15

现在,我们可以轻松地使用名为“ Android File Grouping ”的JetBrains插件

查看此链接

Android文件分组

在此处输入图片说明


必须标记为正确答案,因为仅使用简单名称约定来组织分组。无需更改gradle,甚至无需更改应用程序的默认目录结构。所有布局仍保留在布局文件夹中,但归类于Android Studio
Alireza Fattahi,2017年

1
不应该接受的答案,因为它没有在Android视图中工作......(但我喜欢这个主意^^)
Appyx

这不适用于android的最新版本...是任何更新版本吗?
user3053247

7

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

android {
    //other stuff

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

示例文件夹结构

那么新res文件夹的每个子文件夹都可以是与每个特定屏幕或应用程序中的某个东西相关的文件,并且每个文件夹都有自己的layout/ drawable/ values等,使文件井井有条,而您不必像其他一些文件一样手动更新gradle文件答案是必需的(每次添加新的资源文件夹时,请使其同步,这样它就知道了,并确保在添加xml文件之前添加相关的子文件夹)。



3

如果您在Linux或Mac Box上进行开发,则一种变通办法是创建子文件夹,其中包括指向您的layoutfiles的符号链接。只需将ln命令与-s一起使用

ln -s PATH_TO_YOUR_FILE

问题是您的布局文件夹仍然包含所有.xml文件。但是,尽管可以使用子文件夹选择它们。这是最接近您想要的东西。

我刚刚读到,如果您使用的是Vista或更高版本,它也可能适用于Windows。有这个mklink命令。只是谷歌它,从来没有使用过它自己。

另一个问题是,如果您打开了文件并尝试再次打开它,则插件将引发NULL指针异常。但是它不会挂断。


1
将符号链接置于首位无法实现通过将组件分成不同的子目录来实现模块化存储的目的:有效地使其变得更复杂而不是更少。Gradle允许指定其他资源目录。
RichieHH 2014年

3

尽管针对多个资源集的所有建议都可以使用,但问题是,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/提交了一个补丁,以尝试帮助解决问题。


我同意您的意见,AS不记录XML文件中的更改。但是,解决方案是什么?我将尝试stackoverflow.com/questions/32317822/…
CoolMind

3

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

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]
}

在实践中该怎么做?

例如,我要创建一个名为activity的子文件夹layout,在resDirs变量中添加任何名称的字符串,例如layouts,然后将布局xml文件放入res\layouts\activity\layout\xxx.xml

如果我要创建命名selectors为的子文件夹drawable,请在resDirs变量(如)中添加任何名称的字符串drawables,然后将可绘制的xml文件放入res\drawables\selectors\drawable\xxx.xml

文件夹名称(例如layoutsdrawables)在resDirs变量中定义,可以是任何字符串。您创建的所有子文件夹(例如activity或)selectors都与res文件夹相同。因此,在selectors文件夹中,我们必须drawable另外创建文件夹并将xml文件放入文件drawable夹中,然后gradle才能将xml文件识别为正常可绘制的。


这应该被认为是正确的答案!简单且完美地工作!
Gláucio莱昂纳多Sant'ana

2

如果您在批准的答案中使用该方法,并且想对它进行一点改进,则可以像下面这样更改gradle设置:

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

因此,如果您添加更多文件夹和布局,则无需回到此处并添加一长串源文件夹,让gradle为您获取所有文件夹。


1
  • 第1步:右键单击布局-在资源管理器中显示
  • 步骤2:打开布局文件夹并直接创建子文件夹:layout_1,layout_2 ...
  • 步骤3:打开layout_1创建文件夹布局(注意:强制名称是layout),打开layout_2文件夹创建布局子目录(注意:强制名称是layout)...
  • 步骤4:将xml文件复制到layout_1和layout_2中的布局子目录中
  • 步骤5:在buid.grade(模块应用)中运行代码,然后立即同步:

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

感谢您的Project模式。我不明白子文件夹在哪里消失(在Android模式下)。
CoolMind

0

好吧,简短的答案是没有。但是您绝对可以有多个res文件夹。我认为,这与该文件夹的子文件夹非常接近layout这是您的操作方式。


1
检查接受的答案。我没有亲自尝试过,但是如果您可以检查和更新,那将很有帮助!
Paresh Mayani

0

最佳答案有几个缺点:您必须添加新的布局路径,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 > Clean ProjectBuild > Rebuild Project在Android Studio中更改xml布局文件后,请阅读“ 需要重建”


您的评论“我在本文中创建了文件夹(附加链接)”不是在此处提供解决方案的可接受方式。您的答案含糊不清。您可以留下说明,并通过链接向作者致谢。否则,这只是懒惰。
jungledev

@jungledev,好吧,最后我拒绝了这个想法,回到了传统的res文件夹中,因为AS不完全支持将资源划分为文件夹。我可能会更改此答案,甚至删除。但是,当您说这不是在SO上提供解决方案的可接受方式时,您是什么意思?
CoolMind

我是说我的意思。不要说“我在本教程中做到了...。(插入链接)。” 您应该说的是:“我遵循了本教程(插入链接),这是对我有用的步骤。第1步,第2步,第3步...等”,您需要在答案中包括此处的步骤。链接到教程是不可接受的
jungledev

0

无法轻松拥有子目录,但可以拥有其他资源文件夹。谁都没有提到它而感到惊讶,而是保留了默认资源文件夹,并添加了更多内容:

    sourceSets {
        main.res.srcDirs += ['src/main/java/XYZ/ABC']
    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.