Как заставить 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при включении, иначе все будет проигнорировано.


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, но он мне все равно нравится :)


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.


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

1

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

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

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

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

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

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

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


1
Я не хочу помещать их все в один массивный файл макета, потому что с этим труднее справиться в долгосрочной перспективе ... Вот почему я попросил возможность иметь один основной XML с одним файлом слияния ... потому что прямо сейчас У меня есть 5 файлов с элементом слияния в качестве корня, которые имеют точно такой же макет, за исключением того, что идентификаторы разные. Я делаю это так, чтобы иметь к ним доступ во время выполнения. Кажется, что я хотел бы сделать обзор моего вызова findViewById (). Как вы задаете область действия этого вызова, чтобы можно было включать один и тот же файл макета несколько раз и при этом иметь доступ ко всем компонентам во время выполнения?
— Джастин

1
Спасибо за ответ. Я займусь этим. Между тем (и, может быть, я демонстрирую здесь свое невежество) не будет ли упаковка каждого тега include в родительский контейнер, что приведет к нарушению цели использования RelativeLayout? Вся шумиха над RelativeLayout состоит в том, чтобы избегать вложенных макетов ...
— Джастин,

3
Единственная причина, по которой я спросил об этом, заключалась в том, чтобы сэкономить место и придумать хороший дизайн ... Я читал о повторном использовании макета здесь: developer.android.com/resources/articles/ ... и подумал, что это звучит хорошо. Когда я попытался реализовать это, я столкнулся с некоторыми проблемами. В настоящее время у меня есть 5 макетов, которые по существу дублируются, за исключением идентификатора в них ... поэтому я подумал, что повторное использование макета будет хорошим кандидатом. Может быть, мне что-то здесь не хватает, но похоже, что RelativeLayout - это еще не все, за что его рекламируют ...
— Джастин,

1
Вау ... спасибо за невероятную помощь. Как правило, ваши ответы очень полезны, поэтому я не знаю, был ли у вас просто плохой день или что-то еще, но я просто пытался лучше понять концепции RelativeLayout, тега include и тега слияния на основе статьи, которые я прочитал, и пытаюсь найти подходящее решение для макета, которого я хочу достичь.
— Джастин,

1
«И для настоящего повторного использования создание настраиваемого класса View включает в себя:« Согласовано. Я просто не хотел бы этого делать, если бы существовал относительно простой способ использования базовых макетов ... "Вы уловили - пожалуйста, не удивляйтесь, когда люди на это отреагируют. И хотя мой последний комментарий высоко на смекалке, баллы по-прежнему в силе "Я справился, потому что я чувствовал, что вы сделали в своих ответах. «переключение на строки в ListView могло бы быть лучше». Listview не будет работать с внешним видом моего приложения. Мое приложение на маркете - AppSwipe! если вы хотите знать, что я делаю ...
— Джастин


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>
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.