ChatGPT解决这个技术问题 Extra ChatGPT

Android布局文件夹可以包含子文件夹吗?

现在,我将每个 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 布局弹出窗口菜单。

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

T
Tim

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

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

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

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

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

https://i.stack.imgur.com/yzj01.png

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


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

答案是不。

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

还值得注意的是有关资源的一些限制。首先,Android 仅支持 res 下预定义文件夹中的文件的线性列表。例如,它不支持 layout 文件夹下的嵌套文件夹(或 res 下的其他文件夹)。其次,assets文件夹和res下的raw文件夹有一些相似之处。两个文件夹都可以包含原始文件,但原始文件中的文件被视为资源,资产中的文件不是。请注意,由于 assets 文件夹的内容不被视为资源,因此您可以在其中放置任意层次的文件夹和文件。


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

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

用以下方法测试。 BuildToolsVersion = 23.0.0 gradle 1.2.3 & 1.3.0

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

将所有 XML 文件从布局目录中复制出来,并将它们放入桌面上的目录或其他地方进行备份。删除整个布局目录(确保备份了步骤 1 中的所有内容!!!)右键单击 res 目录并选择新建 > 目录。将此新目录命名为“布局”。 (这可以是您想要的任何内容,但它不会是稍后出现的“片段”目录或“活动”目录)。右键单击新的“布局”目录并选择新建 > 目录。 (这将是您将在其中拥有的 XML 文件类型的名称,例如“片段”和“活动”)。右键单击“片段”或“活动”目录(注意:这不一定是“片段”或“活动”,这只是我用作示例的内容)并再次选择新>目录并命名此目录“布局”。 (注意:这必须命名为“布局”!!!非常重要)。将所需的 XML 文件从您在桌面上创建的备份中放入新的“布局”目录中。对任意数量的自定义目录重复步骤 5 - 7。完成后,进入您的模块 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' ] } } 利润 $$$$

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

图片比文字更有价值。

https://i.stack.imgur.com/C1c9B.png


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/…的问题
搜索了一段时间没有运气,这是唯一对我有用的解决方案。感谢您用图片发布如此详细的答案!
d
delformo

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

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

ᴛʜᴇᴘᴀᴛᴇʟ

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

您可以通过以下方式安装“Android File Grouping”插件

视窗:

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

苹果电脑:

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

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


也分享那个插件的链接!
完毕。无论如何,您始终可以在 Android Studio -> 文件 -> 设置 -> 插件中搜索“Android 文件分组”
该插件不适用于“Android”视图,仅适用于“项目”视图:/
b
botbot

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


C
Community

小问题

我可以按照这个问题的 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'
            ]

这是带有注释以供理解的脚本:

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 想要 Gstring 参数而不是 String one。如果有人而不是我发现了这个错误,你可以编辑它
子文件夹中的布局无法解析 android 命名空间。有任何想法吗 ?
您可以用 // uncomment for debugging 取消注释这些行,然后检查输出 src/main/res/layouts/<your_layout_files> 是否被正确检测到?
A
Androiderson

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

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

配置是这样的:

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

检查 documentation


我注意到每次在此设置下更改布局时,我总是必须执行 clean 才能看到它反映在应用程序上。
S
Santhi Bharath

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

看看这个链接

Android File Grouping

https://i.stack.imgur.com/nSdnQ.png


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

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

android {
    //other stuff

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

https://i.stack.imgur.com/zLGhp.png

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


S
Sneg

选中将文件夹层次结构转换为单个文件夹的 Bash Flatten Folder script


很快就会检查出来。谢谢
该脚本将更改文件名以在文件名中包含目录层次结构。这将如何与您的 Android IDE 一起使用,文件名随处可见?
D
Drusantia

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

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

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


D
Darokthar

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

ln -s PATH_TO_YOUR_FILE

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

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

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


放入符号链接破坏了通过将组件分离到不同的子目录来模块化存储的目的:有效地使其更复杂而不是更少。 Gradle 允许指定额外的资源目录。
r
rogerhu

虽然针对多个资源集的所有建议都可能有效,但问题是 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/…
R
Riki

@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,那么可绘制的 xml 文件应该放在 res\drawables\selectors\drawable\xxx.xml 中。

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


这一个应该被认为是正确的答案!简单而完美地工作!
j
jwpfox

第 1 步:右键单击布局 - 在资源管理器中显示

第二步:打开layout文件夹,直接创建子文件夹:layout_1, layout_2 ...

第三步:打开layout_1创建文件夹布局(注:必填名称为layout),打开layout_2文件夹创建布局子目录(注:必填名称为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 模式下)。
s
sravan953

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


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

最佳答案有几个缺点:您必须添加新的布局路径,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 >文件夹 >水库文件夹。

更新

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


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

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

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

B
Braian Coronel

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

在 res.srcDirs 中添加资源目录进行风味时,请记住,在其他模块中,甚至在同一模块的 src/main/res 中,也会添加资源目录。因此,使用附加分配 (+=) 的重要性,以免用新分配覆盖所有现有资源。声明为数组元素的路径是包含资源类型的路径,即资源类型是一个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 目录视为包含资源类型的自定义资源。

总帐