Как заставить RelativeLayout работать с объединением и включением?

114

В течение нескольких дней я пытался сделать свои макеты более эффективными, преобразовав использование нескольких уровней вложенности LinearLayoutsв один, RelativeLayoutи столкнулся с несколькими проблемами, для которых я не смог найти обходной путь ...

Я искал группу для начинающих Android и этот сайт и не смог найти ничего, что помогло бы мне решить проблему.

Я прочитал в одном из блогов, что вы можете комбинировать макеты с помощью тегов слияния и включения. Итак, у меня есть основной файл макета с RelativeLayoutкорневым элементом. Внутри у меня есть 5 тегов include, которые ссылаются на 5 разных файлов макета xml, каждый из которых имеет элемент слияния для корня (все мои файлы слияния одинаковы, за исключением идентификаторов в них).

Я столкнулся с двумя проблемами, которые я объясню после публикации упрощенной версии моего кода макета:

Пример основного файла макета:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@drawable/translucent_gray" >

    <include 
        android:id="@+id/running_gallery_layout_id"
        layout="@layout/running_gallery_layout" />

    <include 
        android:id="@+id/recent_gallery_layout_id" 
        layout="@layout/recent_gallery_layout"
        android:layout_below="@id/running_gallery_layout_id" />

    <include
        android:id="@+id/service_gallery_layout_id"
        layout="@layout/service_gallery_layout"
        android:layout_below="@id/recent_gallery_layout_id" />

    <include
        android:id="@+id/process_gallery_layout_id"
        layout="@layout/process_gallery_layout"
        android:layout_below="@id/service_gallery_layout_id" />

</RelativeLayout>

Пример включенного файла слияния:

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
    <TextView 
        style="@style/TitleText"
        android:id="@+id/service_gallery_title_text_id"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="left"
        android:text="@string/service_title" />

    <Gallery
        android:id="@+id/service_gallery_id"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:layout_below="@id/service_gallery_title_text_id" />

    <TextView 
        style="@style/SubTitleText"
        android:id="@+id/service_gallery_current_text_id"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/service_gallery_title_text_id"
        android:layout_above="@id/service_gallery_id" />
</merge>

У меня две проблемы:

1) android:layout_*Кажется, что атрибуты игнорируются при использовании в теге include, и все объединенные макеты отображаются друг над другом. Согласно этому сообщению ( http://developer.android.com/resources/articles/layout-tricks-reuse.html ) «любой android:layout_*атрибут может использоваться с <include />тегом»

2) Поскольку я не мог заставить это работать, я решил попробовать добавить android:layout_belowатрибут к первому TextViewэлементу в каждом файле макета слияния, что означает, что каждый файл слияния будет ссылаться на идентификатор из другого файла макета слияния ... По большей части это действительно сработал, и мой макет выглядит нормально. Однако я получаю сообщение об ошибке в одном из android:layout_belowатрибутов, в котором говорится, что он не может найти указанный мной идентификатор ... Я дважды и трижды проверил идентификаторы, чтобы убедиться, что они верны. Самое странное то, что я использовал эту AutoFillфункцию, чтобы поместить идентификатор в атрибут в первую очередь.

Если у кого-то есть предложения или обходные пути, я буду более чем счастлив попробовать их. Кроме того, если кто-нибудь может придумать для меня способ иметь только один файл макета слияния xml вместо 5, это будет очень признательно. Я не мог найти способ сделать это, потому что мне нужен доступ к каждому элементу в файлах макета слияния во время выполнения ...

Джастин
источник

Ответы:

214

Возникла проблема с тегом include. Проверить: https://issuetracker.google.com/issues/36908001

Чтобы исправить это, убедитесь, что вы перезаписываете ОБА layout_widthи layout_heightпри включении, иначе все будет проигнорировано.

Macarse
источник
15
Это лучшее решение, чем принятое, поскольку оно позволяет избежать создания лишнего объекта макета. Кроме того, отстойно, что, по мнению разработчиков Android, это нормально.
mikołak 04
4
Это действительно проще, менее жестко запрограммировано и более оптимизировано, чем упаковка <include /> в другой макет. Подумайте, что бы вы сделали, если бы работали со списками.
teoREtik 02
2
Это работает намного лучше, чем принятый ответ. Большое спасибо! И ... давай, гугл, уже исправь эту проблему, это ерунда! :)
Фелипе Калдас
2
@JeffAxelrod, исходный код LayoutInflater показывает, что, к сожалению, переопределение тегов id, visibility и layout_ * не применяется, если корневой элемент является тегом слияния. Поскольку у вас не может быть View в качестве корня xml, у нас должна быть там дополнительная ViewGroup ...
Рафаэль Нобре,
13
У меня это просто не сработало. У меня есть и то, layout_widthи layout_heightдругое <include>. Я пробовал также установить layout_widthи layout_heightна моем, <merge>но безуспешно. Что мне не хватает?
dum4ll3
33

См. Ответ, получивший наибольшее количество голосов ниже. Моя ужасно устарела


Я могу решить одну проблему, поднятую Джастином : неспособность RelativeLayout управлять позиционированием включения (по крайней мере, в этом простом случае на эмуляторе 1.6)

CommonsWare предлагает обертывание включает в уникальном родительском контейнере, но делает это для того , чтобы помочь решению и обзорным одинаково именованных видов внутри Джастина включает

У каждого должен быть уникальный родительский контейнер, и вы должны вызывать findViewById () для этого контейнера (ViewGroup), а не для Activity.

Фактически, вы также должны сделать это , чтобы RelativeLayout работал так, как ожидалось:

Это работает ( нижний колонтитул расположен хорошо):

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent" android:layout_height="fill_parent">
    <include android:id="@+id/header" layout="@layout/header"
        android:layout_alignParentTop="true" />
    <WebView android:id="@+id/webView" android:layout_below="@id/header"
        android:background="#77CC0000" android:layout_height="wrap_content"
        android:layout_width="fill_parent" android:focusable="false" />
    <LinearLayout android:layout_alignParentBottom="true"
        android:layout_height="wrap_content" android:layout_width="fill_parent">
        <include android:id="@+id/footer" layout="@layout/footer" />
    </LinearLayout>
</RelativeLayout>

Этого не происходит ( нижний колонтитул плавает вверху экрана):

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent" android:layout_height="fill_parent">
    <include android:id="@+id/header" layout="@layout/header"
        android:layout_alignParentTop="true" />
    <WebView android:id="@+id/webView" android:layout_below="@id/header"
        android:background="#77CC0000" android:layout_height="wrap_content"
        android:layout_width="fill_parent" android:focusable="false" />
    <include android:id="@+id/footer" layout="@layout/footer"
        android:layout_alignParentBottom="true" />
</RelativeLayout>

Включаемый нижний колонтитул не будет выровнен по низу родительского объекта без окружающего его LinearLayout .. Я бы не назвал это ожидаемым поведением.

Кроме того, WebView, кажется, красиво прикрепляется к заголовку по идентификатору, но я считаю, что это иллюзия, потому что он просто течет под заголовком по вертикали. Я также попытался установить кнопку прямо над включением нижнего колонтитула, но она тоже стала плавной и неправильной

В RelativeLayout было больше проблем в 1.5, но он мне все равно нравится :)

alienjazzcat
источник
2
Я увеличил его на 1, а затем снова уменьшил, увидев комментарий @Macarse, это правильный способ сделать это.
Jayshil Dave 08
Удалите, пожалуйста, этот вводящий в заблуждение ответ :(
Дэниел Смит
8

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

Я думаю, что хитрость здесь в том, что <merge>тег в сочетании с <include>тегом по существу удаляет любую «родительскую» группу представлений на этом уровне. Итак, кого именно вы просите "layout_below" кого-то еще? Ни один. Никто. На этом уровне нет обзора.

<merge>Тег принимает мнения ребенка и хлопает их прямо в родителю <include>тега. Поэтому вы должны попросить детей в макете, который вы включаете, соответствующим образом закрепить себя.

Плантаже
источник
4

Чтобы позиционирование работало в RelativeLayout, вам необходимо установить параметры layout_ * во включаемом файле, а не в основном файле макета. Туда

main_layout.xml

<RelativeLayout
  android:id="@+id/header"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content">
   ....
</RelativeLayout>

<RelativeLayout 
  android:id="@+id/footer"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:layout_alignParentBottom="true">
    .....
</RelativeLayout>

<include layout="@layout/content_layout" />

content_layout.xml

<merge xmlns:android="http://schemas.android.com/apk/res/android">
<RelativeLayout
    android:id="@+id/content"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_above="@id/footer"
    android:layout_below="@id/header" >

    ....
</RelativeLayout>
</merge>

Это явно не то, чего хотят мы, разработчики, но это единственное решение, которое я нашел, чтобы избежать дублирования xml.

Maragues
источник
1
Почему нет голосов? Это сработало для меня. Я не люблю вставлять параметры в объект, который может быть включен в макет, который в них не нуждается, но если это LinLay, кажется, что layout_ * RelLay просто игнорируется. Я что-то упускаю?
QED
1

Атрибуты android: layout_ *, похоже, игнорируются при использовании в теге include, и все объединенные макеты отображаются друг над другом.

Я предполагаю, что из правил компоновки нельзя ссылаться на android:idатрибуты, определенные в <include>элементах, только на те, которые находятся в «реальных» виджетах и ​​контейнерах.

Кроме того, если кто-нибудь может придумать для меня способ иметь только один файл макета слияния xml вместо 5, это будет очень признательно.

Просто: поместите их все в один файл.

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

Независимо от того, есть ли у вас один <include>элемент или 1000, все содержимое должно быть доступно во время выполнения. Единственное исключение - если у вас есть дублированные android:idатрибуты - вам нужно будет правильно определить область ваших findViewById()вызовов, чтобы получить нужный, как и при получении виджетов из строки ListView.

Если вы можете создать пример проекта , который использует 2+ слияния файлов , где вы можете продемонстрировать , что содержимое не доступны во время выполнения, дайте мне знать.

CommonsWare
источник
1
Я не хочу помещать их все в один массивный файл макета, потому что с этим труднее справиться в долгосрочной перспективе ... Вот почему я попросил возможность иметь один основной XML с одним файлом слияния ... потому что прямо сейчас У меня есть 5 файлов с элементом слияния в качестве корня, которые имеют точно такой же макет, за исключением того, что идентификаторы разные. Я делаю это так, чтобы иметь к ним доступ во время выполнения. Кажется, что я хотел бы сделать обзор моего вызова findViewById (). Как вы задаете область действия этого вызова, чтобы можно было включать один и тот же файл макета несколько раз и при этом иметь доступ ко всем компонентам во время выполнения?
Джастин
1
Спасибо за ответ. Я займусь этим. Между тем (и, может быть, я демонстрирую здесь свое невежество) не будет ли упаковка каждого тега include в родительский контейнер, что приведет к нарушению цели использования RelativeLayout? Вся шумиха над RelativeLayout состоит в том, чтобы избегать вложенных макетов ...
Джастин,
3
Единственная причина, по которой я спросил об этом, заключалась в том, чтобы сэкономить место и придумать хороший дизайн ... Я читал о повторном использовании макета здесь: developer.android.com/resources/articles/ ... и подумал, что это звучит хорошо. Когда я попытался реализовать это, я столкнулся с некоторыми проблемами. В настоящее время у меня есть 5 макетов, которые по существу дублируются, за исключением идентификатора в них ... поэтому я подумал, что повторное использование макета будет хорошим кандидатом. Может быть, мне что-то здесь не хватает, но похоже, что RelativeLayout - это еще не все, за что его рекламируют ...
Джастин,
1
Вау ... спасибо за невероятную помощь. Как правило, ваши ответы очень полезны, поэтому я не знаю, был ли у вас просто плохой день или что-то еще, но я просто пытался лучше понять концепции RelativeLayout, тега include и тега слияния на основе статьи, которые я прочитал, и пытаюсь найти подходящее решение для макета, которого я хочу достичь.
Джастин,
1
«И для настоящего повторного использования создание настраиваемого класса View включает в себя:« Согласовано. Я просто не хотел бы этого делать, если бы существовал относительно простой способ использования базовых макетов ... "Вы уловили - пожалуйста, не удивляйтесь, когда люди на это отреагируют. И хотя мой последний комментарий высоко на смекалке, баллы по-прежнему в силе "Я справился, потому что я чувствовал, что вы сделали в своих ответах. «переключение на строки в ListView могло бы быть лучше». Listview не будет работать с внешним видом моего приложения. Мое приложение на маркете - AppSwipe! если вы хотите знать, что я делаю ...
Джастин
1

пытаться :

<RelativeLayout xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    tools:showIn="@layout/activity_home">
Захра Салманинежад
источник
0

В моем случае макет, который я пытался включить, начинается с <mergeтега. Когда я изменил его на макет, говорят, что <RelativeLayoutон работал. Ниже представлена ​​иллюстрация.

РАБОТАЕТ

<RelativeLayout xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    tools:showIn="@layout/activity_home">

НЕ РАБОТАЕТ

<merge xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    tools:showIn="@layout/activity_home">
Рохит Мандивал
источник
это создаст еще один вложенный слой, что не является оптимальным решением
Silvia H
0

У меня была такая же проблема и даже определение, layout_widthи layout_heightэто не сработало. Проблема заключалась в том, что в макете, который я включал, были теги, и после их удаления все работало как шарм. Я полагаю, что слияние не является тегом макета и из-за этого не может получать параметры позиционирования и размера. Поскольку все, что вы определяете, переносится во внутренний родительский макет, настройки просто выбрасываются.

TL: DR: Просто удалите теги, переместите определения xmlns в реальный просмотрщик макета, и все будет хорошо.

Перед:

<merge
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <...ui.component.BorderCardView
        android:layout_width="112dp"
        android:layout_height="32dp"
        app:cardCornerRadius="4dp"
        app:cardUseCompatPadding="true">

        <ImageView
            android:layout_width="16dp"
            android:layout_height="16dp"
            android:src="@drawable/ic_logout"
            android:tint="@color/divider" />

    </...ui.component.BorderCardView>
</merge>

Работает:

<...ui.component.BorderCardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="112dp"
    android:layout_height="32dp"
    app:cardCornerRadius="4dp"
    app:cardUseCompatPadding="true">

    <ImageView
        android:layout_width="16dp"
        android:layout_height="16dp"
        android:src="@drawable/ic_logout"
        android:tint="@color/divider" />

</...ui.component.BorderCardView>
silvio.pereira
источник