Может ли папка Android Layout содержать подпапки?


551

Прямо сейчас я храню каждый XML-файл макета внутри res/layoutпапки, так что выполнимо и просто управлять небольшими проектами, но когда речь идет о больших и тяжелых проектах, тогда должна быть иерархия и подпапки, необходимые внутри папка макета.

например

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

Точно так же мы хотели бы иметь подпапки для большого приложения, так есть ли способ сделать это внутри проекта Android?

Я могу создавать вложенные папки layout-personal и layout_address внутри папки layout, но когда приходит время доступа к файлу макета xml с помощью R.layout ._______, в это время не появляется всплывающее окно макета xml внутри меню.



Я думаю, что эта ссылка может помочь вам в деталях. blog.mindorks.com/…
Дилип

Ответы:


475

Вы можете сделать это с Gradle. Я сделал демонстрационный проект, показывающий как.

Хитрость заключается в том, чтобы использовать возможность Gradle для объединения нескольких папок ресурсов и установить папку res, а также вложенные подпапки в блоке sourceSets.

Суть в том, что вы не можете объявить папку ресурса контейнера до того, как объявите папки дочерних ресурсов этой папки.

Ниже приведен блок sourceSets из файла build.gradle из демоверсии. Обратите внимание, что подпапки объявляются первыми.

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

изображение вложенных ресурсов

Кроме того, непосредственный родительский элемент ваших фактических файлов ресурсов (pngs, xml-макеты и т. Д.) Все еще должен соответствовать спецификации .


6
Возможно ли это сделать с помощью открываемых папок? Я просто безуспешно пытался, даже с учетом декларации заказа.
Тревор-е

7
Ницца! Просто помните, что вы действительно используете способность gradle объединять папки ресурсов, и это будет иметь смысл.
Eski

5
Имена файлов в папке по-прежнему должны быть уникальными, и в коде вы должны знать, что, например, R.layout.list_itemон исходит из папки moviesили cinemas, поэтому по-прежнему применяется плоское именование.
TWiStErRob

5
К сожалению, это не работает для меня с build:gradle:0.14.4и buildToolsVersion "21.1.1".
Камил Лелонек

32
Это работает, однако ответ делает крайне неадекватную работу по объяснению того, что каждая добавляемая вами подпапка должна иметь вторую подпапку, которую можно назвать только «макетом», и все ваши файлы макета должны находиться внутри вторая подпапка (и). Прочитать десятки комментариев, чтобы решить это далеко от идеала. И для тех, кто все еще ищет, настройка дополнительных подпапок «макет» является решением URI is not registeredпроблемы.
aroth

233

Ответ - нет.

Я хотел бы обратить ваше внимание на эту книгу Pro Android 2, в которой говорится:

Стоит также отметить несколько ограничений в отношении ресурсов. Во-первых, Android поддерживает только линейный список файлов в предопределенных папках под res. Например, он не поддерживает вложенные папки в папке макета (или другие папки в res).

Во-вторых, есть некоторые сходства между папкой assets и папкой raw в res. Обе папки могут содержать сырые файлы, но файлы в сырых считаются ресурсами, а файлы в активах - нет.

Обратите внимание , что из - за содержимые активы папки не считаются ресурсами, вы можете положить произвольную иерархию папок и файлов в нем.


3
Следующей нашей лучшей надеждой для меня будет плагин Eclipse или IntelliJ, который может сворачивать файлы на основе префиксов имен файлов.
Джерри Брэди

15
@Justin, ознакомьтесь с решением, которое я написал для gradle.
Eski

Я полагаю, что одним из решений может быть присвоение имен файлам, как вы бы хотели, чтобы они были распределены по категориям, чтобы они выглядели так, как будто они находятся в подпапках. Я добавляю «тег подпапки» в начало названия. Например. все действия будут называться «activity_activityname.xml», а дочерние действия могут быть «activity_activityname_childactivity.xml» и так далее. Любой другой файл будет "other_filename.xml". Вот что я делаю, чтобы избежать путаницы. Блин, кто-то бил меня в это. К трем годам>. <Мой плохой, такого не видел.
Ведавиас Бхат

1
Здесь важно отметить, что, рассматривая в rawкачестве ресурсов, вы получаете чередование, зависящее от плотности / ориентации / версии / и т. Д., В то время как с этим assetsвы должны делать это вручную.
TWiStErRob

1
Выбрал этот ответ в качестве аргумента против создания мейнстрима Android.
Зон

77

Я просто хотел добавить к фантастическому ответу eskis для людей, имеющих проблемы. (Примечание: это будет работать и выглядеть как отдельные каталоги внутри представления проекта, к сожалению, не представления андроида.)

Проверено со следующим. BuildToolsVersion = 23.0.0 gradle 1.2.3 и 1.3.0

Вот так я и начал работать с уже построенным проектом.

  1. Скопируйте все XML-файлы из своего каталога макетов и поместите их в каталог на рабочем столе или что-то для резервного копирования.
  2. Удалите весь каталог макета (убедитесь, что вы сделали резервную копию всего, начиная с шага 1 !!!)
  3. Щелкните правой кнопкой мыши по каталогу res и выберите новый> каталог.
  4. Назовите этот новый каталог "раскладки". (Это может быть все, что вы хотите, но это не будет каталог 'фрагмент' или каталог 'деятельность', который появится позже).
  5. Щелкните правой кнопкой мыши новый каталог "layouts" и выберите новый> каталог. (Это будет имя типа XML-файлов, которые вы будете иметь в нем, например, «фрагменты» и «действия»).
  6. Щелкните правой кнопкой мыши по каталогу «фрагмент» или «деятельность» (Примечание: это не обязательно должен быть «фрагмент» или «деятельность», это просто то, что я использую в качестве примера), выберите новый> каталог еще раз и назовите этот каталог «макет». (Примечание: это ДОЛЖНО быть названо 'layout' !!! очень важно).
  7. Поместите нужные вам XML-файлы в новый каталог «layout» из резервной копии, которую вы сделали на рабочем столе.
  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?
Акшай Чордия

4
Я не проверял это, но я хотел бы представить, что вы просто создаете папку layout-v21 рядом с папками макета.
Hitch.united

Работает на gradle 2.0.0 и buildToolsVersion 23.0.2! Обратите внимание, что значение конфигурации 'src/main/res/layouts/content'и путь к этой конфигурации'src/main/res/layouts/content/layout'
cwhsu

3
У меня проблема с этим stackoverflow.com/questions/41934004/…
Петра Барус

2
Искал какое-то время без удачи, это единственное решение, которое сработало для меня. Спасибо за размещение такого подробного ответа с изображениями!
Machine Tribe

53

Не возможно, но папка макета отсортирована по имени. Итак, я добавляю имена файлов макета с именами моих пакетов. Например, для двух пакетов «покупка» и «игра»:

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

20

Я использую Android File Группирование плагин для Android Studio.It на самом деле не позволяет создавать вложенные папки, но он может отображать файлы и ресурсы , поскольку они находятся в разных папках. И это именно то, что я хотел.

Вы можете установить плагин "Android File Grouping"

Окна:

Android Studio -> Файл -> Настройки -> Плагины.

Mac:

Android Studio -> вкладка Android Studio (вверху слева) -> Настройки -> Плагины -> Установить плагин JetBrains.

Для Mac я смог протестировать его и не смог найти плагин. Поэтому я скачал плагин отсюда и использовал Install plugin from diskопцию из вышеупомянутой настройки.


Также поделитесь ссылкой на этот плагин!
Пареш Майани

Выполнено. В любом случае вы всегда можете найти «Группировка файлов Android» в Android Studio -> Файл -> Настройки -> Плагины
Sharpe

3
плагин не работал в представлении «Android», только в представлении «Project»: /
Hugo Gresse

18

Теперь с Android Studio и Gradle вы можете иметь несколько папок ресурсов в вашем проекте. Позволяет организовать не только ваши файлы макетов, но и любые виды ресурсов.

Это не совсем подпапка, но может разделять части вашего приложения.

Конфигурация такая:

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

Проверьте документацию .


2
Я заметил, что каждый раз, когда я вносил изменения в макет под этим параметром, мне всегда приходилось делать, cleanчтобы это отразилось на приложении.
Паван Кумар

16

Я думаю, что наиболее элегантное решение этой проблемы (учитывая, что подпапки не допускаются) состоит в добавлении имени файла к имени папки, в которую вы бы поместили его. Например, если у вас есть несколько макетов для вида деятельности, фрагмента или просто общего вида, называемых «местами», то вам просто нужно добавить к нему местами местный_имя_слова_имя. По крайней мере, это решает проблему их организации таким образом, чтобы их было легче найти в среде 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 Rashad

Вы должны быть в состоянии использовать его как обычно, как R.layout.admin_home. Сценарий должен автоматически подбирать имена для вас. Дайте мне знать, если вы можете заставить его работать.
Я лягушачий дракон

это работает хорошо, но после изменения этой строки array.push("src/main/res");на, def res="src/main/res"; array.push(res);потому что у меня есть ошибка, так как push хочет, чтобы параметр Gstring не String one. Если любой, а не я обнаружил эту ошибку, вы можете ее отредактировать
Asmaa Rashad

1
Макеты в подпапках не могут разрешить пространство имен Android. Любые идеи ?
Ягуар

Можете ли вы раскомментировать эти строки, // uncomment for debuggingа затем проверить, правильно ли определены выходы src/main/res/layouts/<your_layout_files>?
Я лягушачий дракон

15

Теперь мы можем легко сделать с плагином JetBrains под названием « Android File Grouping »

проверить эту ссылку

Группировка файлов Android

введите описание изображения здесь


Должен быть отмечен как правильный ответ, так как организуйте только группирование с простым соглашением имен. Нет необходимости изменять gradle или даже структуру каталогов по умолчанию приложения. Все макеты по-прежнему остаются в папке макетов, но сгруппированы вAndroid Studio
Alireza Fattahi

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/ valuesetc, упорядочивающий вещи, и вам не придется обновлять файл gradle вручную, как некоторые из этих других требуются ответы (просто синхронизируйте ваш gradle каждый раз, когда вы добавляете новую папку ресурсов, чтобы она знала об этом, и обязательно добавляйте соответствующие подпапки перед добавлением ваших XML-файлов).


4

Проверьте скрипт Bash Flatten Folder, который преобразует иерархию папок в одну папку


Скоро проверим. Спасибо
Пареш Майани

1
Этот сценарий вносит изменения в имя файла, чтобы включить иерархию каталогов в имя файла. Как это будет работать с вашей Android IDE, когда имена файлов меняются повсюду?
Раввин Стелс

3

Если вы разрабатываете на Linux или Mac Box, обходной путь, чтобы создать подпапки, которые содержат символические ссылки на ваши файлы макета. Просто используйте команду ln с -s

ln -s PATH_TO_YOUR_FILE

Проблема в том, что ваша папка Layout по-прежнему содержит все XML-файлы. Но вы можете выбрать их, используя подпапки. Это самое близкое к тому, что вы хотели бы иметь.

Я только что прочитал, что это может работать и с Windows, если вы используете Vista или более позднюю версию. Есть эта mklinkкоманда. Просто погуглите, никогда не использовал это сам.

Другая проблема заключается в том, что если файл открыт и вы пытаетесь открыть его снова, плагин генерирует исключение NULL Pointer Exception. Но это не вешает трубку.


1
Использование символической ссылки лишает смысла желание модульного хранилища, разделяя компоненты на разные подкаталоги: эффективно делая его более сложным, а не меньшим. Gradle позволяет спецификации дополнительных каталогов ресурсов.
RichieHH

3

Хотя все предложения для нескольких наборов ресурсов могут работать, проблема в том, что текущая логика для плагина Android Studio Gradle не будет обновлять файлы ресурсов после их изменения для вложенных наборов ресурсов. Текущая реализация пытается проверить каталоги ресурсов с помощью startWith (), поэтому вложенная структура каталогов (т. Е. Src / main / res / layout / layouts и src / main / res / layout / layouts_category2) выберет src / main / res / layout / layout постоянно и никогда не обновляет изменения. Конечным результатом является то, что вам приходится каждый раз перестраивать / очищать проект.

Я отправил патч на https://android-review.googlesource.com/#/c/157971/, чтобы попытаться помочь решить проблемы.


Я согласен с вами, что AS не замечает изменений в файлах XML. Но каково решение? Я попробую stackoverflow.com/questions/32317822/… .
CoolMind

3

Верхний ответ @eski хорош, но код не изящен в использовании, поэтому я написал отличный скрипт в gradle для общего пользования. Он применяется ко всем типам сборки и вкусу продукта, и его можно использовать не только для макета, но также можно добавить подпапку для любого другого типа ресурсов, например, для рисования. Вот код (поместите его в 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.

Имя папки, такое как layoutsи drawablesопределяется в resDirsпеременной, это может быть любая строка. Все созданные вами подпапки, такие как activityили selectorsрасцениваются как resпапки. Таким образом, в selectorsпапке мы должны drawableдополнительно создать папку и поместить в drawableнее файлы XML , после чего Gradle сможет распознавать файлы XML как обычно доступные для рисования.


Этот должен считаться правильным ответом! Просто и отлично работает!
Глаусио Леонардо Сантана

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_2, создайте макет папки (примечание: обязательное имя - макет) ...
  • Шаг 4. Скопируйте файлы xml в подкаталоги layout в 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
Проверьте принятый ответ. Я не пробовал лично, но если вы можете проверить и обновить, это было бы полезно!
Пареш Майани

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 Projectили Build > Rebuild Project. Чтение Требуется восстановление после изменения файлов макета XML в Android Studio .


Ваш комментарий «Я создал папки с этой статьей (прикрепленная ссылка)» не является приемлемым способом предоставления решений здесь, на SO. Ваш ответ неопределенный. Вы можете оставить инструкции и указать автора ссылки. Иначе это просто лень.
jungledev

@jungledev, наконец, я отказался от этой идеи и вернулся к традиционной resпапке, потому что AS не поддерживает разделение ресурсов на папки. Возможно, я изменю этот ответ или даже удалю. Но что вы имеете в виду, когда говорите, что это неприемлемый способ предоставления решений здесь на SO?
CoolMind

Я имею в виду именно то, что я сказал. Не говорите: «Я сделал это с этим руководством ... (вставьте ссылку)». Что вы должны сказать: «Я следовал этому руководству (вставьте ссылку), и вот шаги, которые работали для меня. Шаг 1, Шаг 2, Шаг 3 ... и т. Д.» Вы должны включить шаги в ответ. Просто
ссылка

0

Не может быть подкаталогов (легко), но вы можете иметь дополнительные папки ресурсов. Удивило, что никто уже не упомянул об этом, но сохранил папки ресурсов по умолчанию и добавил еще несколько:

    sourceSets {
        main.res.srcDirs += ['src/main/java/XYZ/ABC']
    }
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.