593

Right now, I'm storing every XML layout file inside the 'res/layout' folder, so it is feasible and simple to manage small projects, but when there is a case of large and heavy projects, then there should be a hierarchy and sub-folders needed inside the layout folder.

for e.g.

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

Like the same way, we would like to have sub-folders for the large application, so is there any way to do so inside the Android project?

I am able to create layout-personal and layout_address sub-folders inside the layout folder, but when the time comes to access the XML layout file using R.layout._______ , at that time there is no any XML layout pop-up inside the menu.

Braian Coronel
  • 17,823
  • 4
  • 30
  • 33
Paresh Mayani
  • 122,920
  • 69
  • 234
  • 290

20 Answers20

506

You CAN do this with gradle. I've made a demo project showing how.

The trick is to use gradle's ability to merge multiple resource folders, and set the res folder as well as the nested subfolders in the sourceSets block.

The quirk is that you can't declare a container resource folder before you declare that folder's child resource folders.

Below is the sourceSets block from the build.gradle file from the demo. Notice that the subfolders are declared first.

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

nested resources picture

Also, the direct parent of your actual resource files (pngs, xml layouts, etc..) does still need to correspond with the specification.

Tim
  • 38,263
  • 17
  • 115
  • 131
eski
  • 7,699
  • 1
  • 21
  • 34
  • 7
    Is it possible to do this with the drawable folders? I just tried with no luck, even taking into account the declaration ordering. – trevor-e Mar 31 '14 at 21:22
  • 1
    Yes, it works for any of the resource types. I use it for separating the tons of drawable files that the holo theme generator creates. What's your file structure and build.gradle file look like? – eski Apr 01 '14 at 13:02
  • 1
    I'm using the old project structure where `src` and `res` are at the project root level. I tried setting my `res.srcDirs` to `['res/drawable/test','res']` and it didn't work. After syncing my gradle structure AS still didn't recognize the drawable I put in the drawable/test folder. – trevor-e Apr 01 '14 at 15:48
  • you need to declare the parent directory of the drawable folder. This makes sense with how android decides which folder to use based on orientation, size, etc... – eski Apr 01 '14 at 15:58
  • 1
    notice in the picture that res/layouts is the declared resource folder, but the sub_layout.xml file is in res/layouts/layout – eski Apr 01 '14 at 15:59
  • 1
    Ah, my brain missed that. I tried restructuring it so that I have `res/test/drawable` and accordingly changed `srcDirs = ['res/test','res']` and it works! Thanks. – trevor-e Apr 01 '14 at 16:14
  • 8
    Nice! Just remember that you are really only exploiting gradle's ability to *merge* resource folders and it'll make sense. – eski Apr 01 '14 at 16:18
  • It should be noted that it should use .ext (it will be mandatory in future versions): `res.ext.srcDirs = [...]` as stated in [documentation](http://www.gradle.org/docs/current/javadoc/org/gradle/api/plugins/ExtraPropertiesExtension.html) – J. Costa Jun 05 '14 at 09:27
  • I'll test that out and update it. Gradle versions get so confusing. – eski Jun 05 '14 at 14:51
  • @J.Costa [gradle development forum](http://forums.gradle.org/gradle/topics/6caj90zrfqr0l) Looks like this means that it is only when you are dynamically adding properties to the closures. In this case res.srcDirs is an existing property that you are **setting.** Someone else will need to verify my interpretation though. – eski Jun 07 '14 at 00:27
  • What is the use of again putting a 'layout' folder in layouts_category2? Why doesnt it work if i simply put my layout files in layouts_category2? – Diffy Aug 04 '14 at 06:40
  • layouts_category2 is more like your "res" folder actually, so all the regular rules apply with folder naming conventions and qualifiers, but there are multiple resource folders being merged (and they just happen to be inside other resource folders). – eski Aug 10 '14 at 20:37
  • 1
    Shouldn't it use relative directories instead of absolute directories? For me it only works if I use relative directories (`src/main/res`) instead of absolute directories (`/src/main/res`) – rve Aug 26 '14 at 10:38
  • Yeah I think there is an issue here on mac and maybe linux, I'll try to update it tonight. – eski Aug 26 '14 at 17:44
  • @eski your solution is great, although I cannot use the "alt+Enter" on strings to extract static strings to resources - do you maybe know, if it's possible to get this working? – Damian Walczak Sep 30 '14 at 14:27
  • 6
    The file names in the folder still have to be unique and in code you have to know that e.g. `R.layout.list_item` is coming from the `movies` or the `cinemas` folder, so flat naming still applies. – TWiStErRob Oct 19 '14 at 22:32
  • Yes, true. They are still seen as a flat list of files after they get merged into a single resource directory. – eski Oct 20 '14 at 14:25
  • 5
    Unfortunately that does not work for me with `build:gradle:0.14.4` and `buildToolsVersion "21.1.1"`. – Kamil Lelonek Nov 21 '14 at 09:59
  • I tried using the plugin version 0.14.4 and buildToolsVersion 21.1.1, and have not seen issues. Have you tried cleaning your project first? – eski Dec 05 '14 at 16:59
  • Your layout xml file's parent folder still needs to be a layout directory, like res/sub/layout/main.xml – eski Jan 10 '15 at 20:38
  • 4
    Hey guys,I read all the comments above.However,I am facing another problem.I created a directory inside res/layout folder,and created a new layout inside the sub-directory.My new directory structure: -res/layout/fragment_layouts I also did the build.gradle stuff. I created a layout inside fragment_layouts folder called my_frag_layout.xml.However,I keep getting the message:URI not registered, when I am trying to declare the android namespace in the my_frag_layout.xml. Can someone please help me out with this? Please help! – user3509153 Jan 10 '15 at 20:43
  • 1
    The layout file's parent directory still needs to be a layout folder, like res/sub/layout/main.xml – eski Jan 10 '15 at 20:45
  • thanks. finally get that what you mean is the parent directory needs to be the folder "layout" ;) – manuzhang Feb 19 '15 at 06:58
  • roboelectric tests consideration: If using roboelectric tests, it's necessary to update the initialization of the RobolectricTestRunner. This linked solution make roboelectric work in conjunction with this answer: http://stackoverflow.com/a/29223625/3063884 – CJBS May 04 '15 at 19:34
  • I noticed that every time I made changes to a layout under this setting, I always had to do a `clean` to see it reflected on app – Pawan Kumar Jun 18 '15 at 09:00
  • Didn't seem to work for raw. Tried to do the following without success: res.srcDirs = ['src/main/res/raw-sounds', 'src/main/res'] – Ben Pearson Jul 17 '15 at 12:49
  • @Ben Pearson did you put a raw folder inside the raw-sounds folder? – eski Jul 17 '15 at 12:53
  • @eski I didn't originally, but just tried it; still didn't seem to work. I thought a clean/build might help, but no joy. – Ben Pearson Jul 17 '15 at 13:00
  • 3
    This is showing me `URI is not registered` in every xml of mine. – DroidDev Sep 02 '15 at 08:38
  • Have you looked through others' comments to see if they have already overcome the issue you are having? – eski Sep 02 '15 at 12:50
  • Sadly this isn't working for me I get `Error:(22, 32) error: cannot find symbol variable activity_home activity_home` is a layout inside `res/layout/layout_home` – CommonSenseCode Oct 22 '15 at 15:52
  • If you read the comments you should be able to figure out your mistake ;) – eski Oct 26 '15 at 19:48
  • 1
    please note that Instant Run will not work for layout changes in xml files within subfolders – Matias Elorriaga May 02 '16 at 00:05
  • 2
    as @user3509153 I also get errors "URI not registered" in each of my layouts at the line which contains "http://schemas.android.com/apk/res/android". Any idea to solve this? – Zach Jun 29 '16 at 19:28
  • 47
    This works, however the answer does a _woefully_ inadequate job of explaining that each subfolder that you add needs to have a _second_ subfolder inside of it, which can only be called "layout", and all of your layout files need to go inside of the _second_ subfolder(s). Reading through dozens of comments to work that out is far from ideal. And for anyone still looking, setting up the extra "layout" subfolders is the solution to the `URI is not registered` problem. – aroth Aug 18 '16 at 06:45
  • @aroth I figured the example, and knowledge of the way android resource folders work were enough to explain this, but since so many people are tripping up with this I made an edit to attempt to help. – eski Sep 16 '16 at 17:57
  • Actual structure : add a "layout" folder to "layouts_category2" folder and place your xml files in the "layout" folder. All xml files must be in a "layout" folder – ColinWa Nov 06 '16 at 09:35
  • super cool gradle trick! Also, you might consider to separate your codebase into different interrelated library projects - with their separate resources. At the end your app project will include the dependencies on some of them. I use this a lot to separate resource groups together with related classes – rupps May 22 '17 at 15:39
  • Show URI not registered Error in XML file. how may I configure that? please give some idea. – MohanRaj S Jun 27 '17 at 10:10
  • @MohanRajS: you need to move the file to **layout** directory. – ישו אוהב אותך Jun 28 '17 at 16:37
  • This does work, but the Android project view will no longer find the files. They do not appear in the list, and scrolling from source if the file is open does nothing. You have to press shift twice and search by name. An unfortunate side effect. – nasch May 22 '18 at 18:12
  • is it not possible in app level. when i switched project level to app level its gone. same as before.@eski – That's Enam Jun 22 '18 at 06:45
  • there is a great guild on how to preform this on https://medium.com/mindorks/how-to-put-android-layout-files-in-subfolders-1f7cf07ff48f – Omer Ben Haim Jan 13 '19 at 21:33
  • see [my answer](https://stackoverflow.com/a/45835698/1219956) below for a less manual way of achieving the same as above – Fonix Oct 22 '19 at 13:04
  • This no longer works - all the files are grouped together by Intellij and *appear* to be in the same directory, which has no value whatsoever. – That1Guy Feb 14 '20 at 19:26
  • @That1Guy it may depend on the "view" you are using in the project sidebar. – eski Feb 21 '20 at 01:44
  • @eski Good point - I was looking at the Android "View" but now I see in your screenshots, that you've selected "Project". I'll test this over the weekend. Thanks. – That1Guy Feb 21 '20 at 22:12
  • 1
    This solution will not work with view binding or data binding. – apsommer Sep 30 '20 at 22:50
241

The answer is no.

I would like to draw your attention towards this book Pro Android 2 that states:

It is also worth noting a few constraints regarding resources. First, Android supports only a linear list of files within the predefined folders under res. For example, it does not support nested folders under the layout folder (or the other folders under res).

Second, there are some similarities between the assets folder and the raw folder under res. Both folders can contain raw files, but the files within raw are considered resources and the files within assets are not.

Note that because the contents of the assets folder are not considered resources, you can put an arbitrary hierarchy of folders and files within it.

Gareth Latty
  • 77,968
  • 15
  • 168
  • 174
100rabh
  • 6,028
  • 5
  • 24
  • 40
  • 3
    I guest our next best hope would be an Eclipse or IntelliJ plugin that could collapse the files based on filename prefixes. – Jerry Brady Feb 05 '14 at 21:24
  • 17
    @Justin check out the solution I've posted for gradle. – eski Mar 16 '14 at 15:26
  • I guess one solution could be to name the files as you would like them categorized, so it looks like it's in subfolders. I add a "subfolder tag" to the beginning of the name. For example. all activities would be "activity_activityname.xml" and a child activity could be "activity_activityname_childactivity.xml" and so on. Any other file would be "other_filename.xml". That's what I do to avoid confusion. Damn, someone beat me to it. By three years >.< My bad, hadn't seen that. – Vedavyas Bhat Apr 19 '14 at 03:10
  • 1
    It's important to note here that by considering `raw` as resources you get density/orientation/version/etc dependent striping, while with `assets` you have to do that manually. – TWiStErRob Oct 19 '14 at 22:26
  • 1
    Chosen this answer as an argument against figting the Android mainstream. – Zon May 24 '17 at 03:41
  • The question wasn't "should he do this" the question was "could he do this", so your answer is not relevant to the question. It is just an opinion of process. Right, Wrong, or Indifferent, you are not supplying an answer to the question, so not sure how you got so many upvotes on this. – Sam Jul 19 '19 at 19:59
85

I just wanted to add onto eskis' fantastic answer for people having trouble. (Note: This will only work and look like separate directories inside the 'project' view, not the 'android' view unfortunately.)

Tested with the following. BuildToolsVersion = 23.0.0 gradle 1.2.3 & 1.3.0

This is how I got mine to work with an already built project.

  1. Copy all of the XML files out of your layout directory, and put them into a directory on the desktop or something for backup.
  2. Delete the entire layout directory (Make sure you backed everything up from step 1!!!)
  3. Right click the res directory and select new > directory.
  4. Name this new directory "layouts". (This can be whatever you want, but it will not be a 'fragment' directory or 'activity' directory, that comes later).
  5. Right click the new "layouts" directory and select new > directory. (This will be the name of the type of XML files you will have in it, for example, 'fragments' and 'activities').
  6. Right click the 'fragment' or 'activities' directory (Note: this doesn't have to be 'fragment' or 'activities' that's just what i'm using as an example) and select new > directory once again and name this directory "layout". (Note: This MUST be named 'layout'!!! very important).
  7. Put the XML files you want inside the new 'layout' directory from the backup you made on your desktop.
  8. Repeat steps 5 - 7 for as many custom directories as you desire.
  9. Once this is complete, go into your modules gradle.build file and create a sourceSets definition like this...(Make sure 'src/main/res/layouts' & 'src/main/res' are always the bottom two!!!! Like I am showing below).

    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. Profit $$$$

But seriously.. this is how I got it to work. Let me know if anyone has any questions.. I can try to help.

Pictures are worth more than words.

Directory Structure

hitch.united
  • 7,984
  • 7
  • 35
  • 55
  • 1
    What about layout directories such as `layout-v21`? – Akshay Chordiya Sep 07 '15 at 17:05
  • 5
    I havn't tested it, but I would imagine you just create a layout-v21 folder along side the layout folders. – hitch.united Sep 07 '15 at 17:07
  • Works on gradle 2.0.0 and buildToolsVersion 23.0.2! Note that the config value is `'src/main/res/layouts/content'` and the path of this config is `'src/main/res/layouts/content/layout'` – cwhsu Apr 14 '16 at 09:14
  • I was doing this mistake- I wasn't creating the "layout" directory in the subdirectories. Thanks. – h8pathak Jun 03 '16 at 10:01
  • 3
    I'm having problem with this http://stackoverflow.com/questions/41934004/android-studio-uri-is-not-registered-after-grouping-layouts-into-subdirectory – Petra Barus Jan 30 '17 at 10:49
  • 2
    Searched for awhile with no luck, this is the only solution that worked for me. Thanks for posting such a detailed answer with images! – Machine Tribe Mar 30 '17 at 14:54
  • How is this different from Eski's answer? Seems about the same to me. – AdamMc331 Sep 05 '17 at 19:11
  • @AdamMc331 it's really no different, I think you missed the first sentence "I just wanted to add onto eskis' fantastic answer for people having trouble" – hitch.united Sep 05 '17 at 20:20
  • Works fine and thank you for the clear steps. But the problems is, on the studio, it does not show sub directories and hence this does not solve my problem. My problem is to organize the android studio's res/layout folder under 'Android' view to have sub folders. The Android view just flats out the folders and show all the files under them directly under 'layout'. This is same as what was before this change. – PhantomReference Mar 24 '18 at 09:23
  • Thanks, it works. See http://alexzh.com/tutorials/how-to-store-layouts-in-different-folders-in-android-project/ for similar article. If you get error: `Could not get unknown property 'res' for source set 'main' of type org.gradle.api.internal.tasks.DefaultSourceSet.`, make sure you have only one block `sourceSets {` in build.gradle and `main { res.srcDirs = ` is inside it. – CoolMind Aug 01 '18 at 09:49
  • After recompile a structure of `layout` reverted back to list. I have several subfolders on disk. First AS showed it as a tree, but after recompile it returned to a list. AS shows a `layouts` directory structure in `Project` mode, not in `Android` (as said in the beginning of the answer). – CoolMind Aug 01 '18 at 10:17
  • Another big disadvantage consists in placing resources in wrong folders. When adding new images, strings, dimensions, AS places new files not to `res/drawable`, `res/values`, but to `res/layouts` subfolders! – CoolMind Aug 16 '18 at 11:30
  • @hitch.united : What if I want to move the entire res-directory to another module, how do I do then? I can't seem to find a good guide on the web regarding this. My project is screaming right now since the manifest, among other files, can not find the stored resources... – goldenmaza Jan 17 '19 at 09:45
54

Not possible, but the layout folder is sorted by name. So, I prepend the layout file names with my package names. E.g. for the two packages "buying" and "playing":

buying_bought_tracks.xml
buying_buy_tracks.xml
playing_edit_playlist.xml
playing_play_playlist.xml
playing_show_playlists.xml
delformo
  • 971
  • 9
  • 7
22

I use Android File Grouping plugin for Android Studio.It doesn't really allows you to create sub-folders, but it can DISPLAY your files and resources AS they are in different folders. And this is exactly what I wanted.

You can install "Android File Grouping" plugin by

Windows:

Android Studio -> File -> Settings -> Plugins.

Mac:

Android Studio -> Android Studio Tab (Top Left) -> Preferences -> Plugins -> Install JetBrains Plugin..

For Mac, I was able to test it and was not able to search for the plugin. So I downloaded the plugin from here and used the Install plugin from disk option from the above setting.

ᴛʜᴇᴘᴀᴛᴇʟ
  • 4,057
  • 5
  • 33
  • 62
Sharpe
  • 386
  • 3
  • 8
19

I think the most elegant solution to this problem (given that subfolders are not allowed) is to prepend the file names with the name of the folder you would have placed it inside of. For example, if you have a bunch of layouts for an Activity, Fragment, or just general view called "places" then you should just prepend it with places_my_layout_name. At least this solves the problem of organizing them in a way that they are easier to find within the IDE. It's not the most awesome solution, but it's better than nothing.

botbot
  • 6,848
  • 12
  • 53
  • 93
18

Now with Android Studio and Gradle, you can have multiple resource folders in your project. Allowing to organize not only your layout files but any kind of resources.

It's not exactly a sub-folder, but may separte parts of your application.

The configuration is like this:

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

Check the documentation.

Androiderson
  • 15,139
  • 5
  • 60
  • 71
  • 2
    I noticed that every time I made changes to a layout under this setting, I always had to do a `clean` to see it reflected on app. – Pawan Kumar Jun 18 '15 at 08:50
18

Small Problem

I am able to achieve subfolders by following the top answer to this question.

However, as the project grows bigger, you will have many sub-folders:

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

Not a big problem, but:

  • it's not pretty as the list become very long.
  • you have to change your app/build.gradle everytime you add a new folder.

Improvement

So I wrote a simple Groovy method to grab all nested folders:

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
}

Paste this method outside of the android {...} block in your app/build.gradle.


How to use

For a structure like this:

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

Use it like this:

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

If you have a structure like this:

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

You will use it like this:

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

Explanation

getLayoutList() takes a relative path as an argument. The relative path is relative to the root of the project. So when we input "app/src/main/res/layouts/", it will return all the subfolders' name as an array, which will be exactly the same as:

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

Here's the script with comments for understanding:

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
}

Hope it helps!

Community
  • 1
  • 1
I'm a frog dragon
  • 6,717
  • 5
  • 38
  • 43
  • I used your approach but how to use it in activity ? i created subfolder in layouts folder called admin and it has admin_home layout when i am in activity and use `setContentView(R.layout.Admin.admin_home .)` Admin.admin_home isn't resolved – Asmaa Rashad May 23 '16 at 11:17
  • You should be able to use it normally, like `R.layout.admin_home`. The script should be able to automatically pick up the names for you. Let me know if you can make it work. – I'm a frog dragon May 23 '16 at 13:11
  • it works well but after changing this line `array.push("src/main/res");` to `def res="src/main/res";` `array.push(res);` because i have an error as push want Gstring parameter not String one .if any one rather than me found this error you can edit it – Asmaa Rashad May 23 '16 at 13:51
  • 1
    Layouts in the subfolders are not able to resolve android namespace. Any ideas ? – Jaguar Jul 06 '16 at 23:11
  • Can you uncomment those lines with `// uncomment for debugging` and then check if the outputs `src/main/res/layouts/` are properly detected? – I'm a frog dragon Jul 07 '16 at 01:21
17

Now we can easily do with JetBrains plugin called "Android File Grouping"

check out this link

Android File Grouping

enter image description here

Bharath Kumar Bachina
  • 2,628
  • 3
  • 23
  • 38
  • Must be marked as correct answer, as only organize the grouping with simple name convention. No need to change gradle, or even default directory structure of an app. All layout still remains in the layout folder but are grouped in `Android Studio` – Alireza Fattahi Dec 11 '17 at 07:50
  • 3
    Should not be the accepted answer because it's not working in the Android view....(but i love the idea^^) – Appyx Feb 22 '18 at 20:20
  • 2
    This does not work for the last last version of android... is any updated version? –  Oct 06 '18 at 17:04
12

A way i did it was to create a separate res folder at the same level as the actual res folder in your project, then you can use this in your apps build.gradle

android {
    //other stuff

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

example folder structure

then each subfolder of your new res folder can be something relating to each particular screen or something in your app, and each folder will have their own layout / drawable / values etc keeping things organised and you dont have to update the gradle file manually like some of these other answers require (Just sync your gradle each time you add a new resource folder so it knows about it, and make sure to add the relevant subfolders before adding your xml files).

Fonix
  • 10,588
  • 2
  • 41
  • 67
4

Check Bash Flatten Folder script that converts folder hierarchy to a single folder

Sneg
  • 972
  • 1
  • 10
  • 13
4

If you use the method in the approved answer, and want to improve it on a bit, then change the gradle setting like this:

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

So if you add more folders and layouts, you don't need to come back here and append a long list of source folders, let gradle get all the folders for you.

Drusantia
  • 317
  • 1
  • 8
3

While all the proposals for multiple resource sets may work, the problem is that the current logic for the Android Studio Gradle plug-in will not update the resource files after they have changed for nested resource sets. The current implementation attempts to check the resource directories using startsWith(), so a directory structure that is nested (i.e. src/main/res/layout/layouts and src/main/res/layout/layouts_category2) will choose src/main/res/layout/layouts consistently and never actually update the changes. The end result is that you have to rebuild/clean the project each time.

I submitted a patch at https://android-review.googlesource.com/#/c/157971/ to try to help resolve things.

rogerhu
  • 101
  • 4
  • I agree with you that AS doesn't note changes in XML files. But what is the solution? I will try https://stackoverflow.com/questions/32317822/rebuild-required-after-changing-xml-layout-files-in-android-studio. – CoolMind Aug 28 '18 at 13:47
3

The top answer by @eski is good, but the code is not elegant to use, so I wrote a groovy script in gradle for general use. It's applied to all build type and product flavor and not only can be use for layout, you can also add subfolder for any other resources type such as drawable. Here is the code(put it in android block of project-level gradle file):

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

How to do in practice?

For example, I want to create subfolder named activity for layout, add a string by any name in resDirs variable such as layouts, then the layout xml file should be put in res\layouts\activity\layout\xxx.xml.

If I want to create subfolder named selectors for drawable, add a string by any name in resDirs variable such as drawables, then the drawable xml file should be put in res\drawables\selectors\drawable\xxx.xml.

The folder name such as layouts and drawables is defined in resDirs variable, it can be any string. All subfolder created by you such as activity or selectors are regarded as the same as res folder. So in selectors folder, we must create drawable folder additionally and put xml files in drawable folder, after that gradle can recognize the xml files as drawable normally.

AvatarQing
  • 2,557
  • 1
  • 22
  • 38
3

If you are developing on a linux or a mac box, a workaround would be, to create subfolders which include symbolic links to your layoutfiles. Just use the ln command with -s

ln -s PATH_TO_YOUR_FILE

The Problem with this is, that your Layout folder still contains all the .xml files. But you could although select them by using the sub-folders. It's the closest thing, to what you would like to have.

I just read, that this might work with Windows, too if you are using Vista or later. There is this mklink command. Just google it, have never used it myself.

Another problem is, if you have the file opened and try to open it again out the plugin throws a NULL Pointer Exception. But it does not hang up.

Darokthar
  • 943
  • 11
  • 18
  • 1
    Putting a symlink in defeats the purpose of wanting to modularise the storage by seperating components into different subdirectories : effectively making it more complex instead of less. Gradle allows the specifications of additional resource directories. – RichieHH Mar 16 '14 at 00:56
1
  • Step 1: Right click on layout - show in explorer
  • Step 2: Open the layout folder and create the subfolders directly: layout_1, layout_2 ...
  • Step 3: open layout_1 create folder layout (note: mandatory name is layout), open layout_2 folder create layout subdirectory (note: mandatory name is layout) ...
  • Step 4: Copy the xml files into the layout subdirectories in layout_1 and layout_2
  • Step 5: Run the code in buid.grade (module app) and hit sync now:

sourceSets {
    main {
        res.srcDirs =
            [
                'src / main / res / layout / layout_1'
                'src / main / res / layout / layout_2',
                'src / main / res'
            ]
    }
}
  • Step 6: Summary: All the steps above will only help clustering folders and display in 'project' mode, while 'android' mode will display as normal.
  • So I draw that maybe naming prefixes is as effective as clustering folders.
jwpfox
  • 4,786
  • 11
  • 41
  • 42
O Thạnh Ldt
  • 593
  • 4
  • 10
0

Well, the short answer is no. But you definitely can have multiple res folders. That, I think, is as close as you can get to having subfolders for the layout folder. Here's how you do it.

sravan953
  • 161
  • 3
  • 13
0

Top answers have several disadvantages: you have to add new layout paths, AS places new resources to res\layouts folder instead of res\values.

Combining several answers I wrote similar:

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

I made folders with this article: http://alexzh.com/tutorials/how-to-store-layouts-in-different-folders-in-android-project/. In order to create subfolders you should use this menu: New > Folder > Res Folder.

UPDATE

After a couple of weeks I found that changes in resources are not noticed by Android Studio. So, some weird bugs appear. For instance, layouts continue to show old sizes, margins. Sometimes AS doesn't find new XML-files (especially during run-time). Sometimes it mixes view ids (references to another XML-file). It's often required to press Build > Clean Project or Build > Rebuild Project. Read Rebuild required after changing xml layout files in Android Studio.

CoolMind
  • 16,738
  • 10
  • 131
  • 165
  • Your comment 'I made folders with this article (attached link)' is not an acceptable way to provide solutions here on S.O. Your answer is vague. You may leave instructions and credit the author with a link. Otherwise, this is just lazy. – jungledev Dec 03 '18 at 12:24
  • @jungledev, well, finally I refused this idea and returned to a traditional `res` folder, because AS doesn't fully support dividing resources into folders. Probably I will change this answer or even delete. But what do you mean when saying that it is not an acceptable way to provide solutions here on S.O.? – CoolMind Dec 03 '18 at 12:31
  • I mean exactly what I said. Don't say 'I did it with this tutorial.... (insert link).' What you should say is: "I followed this tutorial (insert link) and here are the steps that worked for me. Step 1, Step 2, Step 3... etc' You need to include the steps HERE in the answer. Simply linking to a tutorial is unacceptable. – jungledev Dec 03 '18 at 12:41
0

Cannot have subdirectories (easily) but you can have additional resource folders. Surprised no one mentioned it already, but to keep the default resource folders, and add some more:

    sourceSets {
        main.res.srcDirs += ['src/main/java/XYZ/ABC']
    }
netcyrax
  • 1,009
  • 12
  • 22
0

Within a module, to have a combination of flavors, flavor resources (layout, values) and flavors resource resources, the main thing to keep in mind are two things:

  1. When adding resource directories in res.srcDirs for flavor, keep in mind that in other modules and even in src/main/res of the same module, resource directories are also added. Hence, the importance of using an add-on assignment (+=) so as not to overwrite all existing resources with the new assignment.

  2. The path that is declared as an element of the array is the one that contains the resource types, that is, the resource types are all the subdirectories that a res folder contains normally such as color, drawable, layout, values, etc. The name of the res folder can be changed.

An example would be to use the path "src/flavor/res/values/strings-ES" but observe that the practice hierarchy has to have the subdirectory values:

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

The framework recognizes resources precisely by type, that is why normally known subdirectories cannot be omitted.

Also keep in mind that all the strings.xml files that are inside the flavor would form a union so that resources cannot be duplicated. And in turn this union that forms a file in the flavor has a higher order of precedence before the main of the module.

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

Consider the strings-ES directory as a custom-res which contains the resource types.

GL

Braian Coronel
  • 17,823
  • 4
  • 30
  • 33