Як змусити RelativeLayout працювати з об'єднанням і включити його?


114

Я вже кілька днів намагаюся зробити свої схеми ефективнішими, перетворивши з використання декількох рівнів вкладених LinearLayoutsна один RelativeLayoutі зіткнувшись з декількома проблемами, які я не зміг знайти вирішення проблеми ...

Я здійснив пошук групи початківців Android та цього сайту і не зміг знайти нічого, що допомогло б мені вирішити проблему.

Я читав на одному з блогів, що ви можете комбінувати макети зі злиттям і включати теги. Отже, у мене є основний файл компонування з RelativeLayoutкореневим елементом. Всередині мене є 5 тегів, які посилаються на 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_*Атрибути, здається, ігноруються при використанні в тезі включення, і всі об'єднані макети відображаються один над одним. Відповідно до цієї публікації ( 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

Існує проблема з тегом включення. Перевірте: https://issuetracker.google.com/isissue/36908001

Щоб виправити це, переконайтеся, що ви перезаписали BOTH layout_widthта layout_heightколи включаєте , інакше все буде проігноровано.


15
Це краще рішення, ніж прийняте, оскільки це дозволяє уникнути створення інакше зайвого об’єкта компонування. Крім того, це чудово, як згідно Android-розробникам це нормально.
mikołak

4
Це дійсно простіше, менш жорстке кодування та більш оптимізоване рішення, ніж упаковка <включити /> в інший макет. Подумайте, що б ви зробили, якби працювали зі списками.
teoREtik

2
Це працює набагато краще, ніж прийнята відповідь. Велике дякую! І ... давай гугл, виправте цю проблему вже, це БС! :)
Феліпе Кальдас

2
@JeffAxelrod, вихідний код LayoutInflater показує, що ідентифікатор, видимість та теги layout_ * не застосовуються, коли, на жаль, кореневим елементом є тег злиття. Оскільки ви не можете мати View як корінь xml, ми повинні мати додатковий ViewGroup там ...
Rafael Nobre

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 добре прикріпився до заголовка за допомогою ідентифікатора, але я вважаю, що це ілюзія, через те, що він просто тече під заголовком вертикально. Я також спробував встановити кнопку, розташовану над колонтитулом, але це все плавно і неправильно

У відносної верстки було більше проблем у 1,5, але мені все одно подобається :)


2
Я збільшив його на 1, а потім зменшив назад, побачивши коментар @Macarse, це правильний спосіб зробити це.
Джейсіл Дейв

Будь ласка, видаліть цю оманливу відповідь :(
Даніель Сміт

8

Людина, це старе, але, схоже, воно виходить на вершині пошуків, тому я буду коментувати.

Я думаю, що фокус у тому, що <merge>тег у поєднанні з <include>тегом по суті видаляє будь-яку "батьківську" групу перегляду на цьому рівні. Тож, кого саме ви просите "розташувати_низу" когось іншого? Ніхто. На цьому рівні немає жодної точки зору.

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


4

Для позиціонування для роботи на RelativeLayout потрібно встановити параметри layout_ * у файлі include, а не в головному файлі макета. Цей шлях

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, здається, що макет RelLay_ * просто ігнорується. Я щось пропускаю?
QED

1

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

Я здогадуюсь, що ви не можете посилатися з правил компонування на android:idатрибути, визначені на <include>елементах, лише на ті, що є "справжніми" віджетами та контейнерами.

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

Просто: помістіть їх у один файл.

Я не зміг знайти спосіб це зробити, тому що мені потрібно мати доступ до кожного елемента файлів макета злиття під час виконання

У вас є один <include>елемент або 1000, весь вміст повинен бути доступним під час виконання. Один виняток - якщо у вас є скопійовані android:idатрибути - вам потрібно буде належним чином охопити ваші findViewById()дзвінки, щоб отримати правильний, як і ви, коли ви отримуєте віджети з ряду ListView.

Якщо ви можете створити приклад проекту , який використовує 2+ злиття файлів , де ви можете продемонструвати , що вміст НЕ доступні під час виконання, дайте мені знати.


1
Я не хочу розміщувати їх у одному масивному файлі компонування, тому що це важче керувати в довгостроковій перспективі ... Ось чому я попросив можливість мати один головний xml з одним файлом злиття ... тому що зараз У мене є 5 файлів з елементом злиття як корінь, які мають точно такий же макет, за винятком того, що ідентифікатори відрізняються. Я роблю це так, щоб я міг отримати доступ до них під час виконання. Схоже, я хотів би зробити свій дзвінок findViewById (). Як ви використовуєте цей виклик, щоб ви могли включати один і той же файл макета кілька разів і все-таки мати доступ до всіх компонентів під час виконання?
Джастін

1
Дякуємо за відповідь. Я розберуся в цьому. Тим часом (і, можливо, я виявляю тут своє невігластво), чи не загортав би кожен тег включення в поразку батьківського контейнера з метою використання RelativeLayout? Весь обман RelativeLayout полягає у тому, щоб уникнути вкладених макетів ...
Джастін,

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

1
Нічого ... дякую за те, що ви такі неймовірно корисні. Як правило, ваші відповіді дуже корисні, тому я не знаю, чи просто у вас поганий день чи що, але я просто намагався краще зрозуміти поняття, що стоять за RelativeLayout, тегом включення та тегом злиття на основі статті я прочитав і намагаюся знайти відповідне рішення для макета, якого я хочу досягти.
Джастін

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.