Jak sprawić, by RelativeLayout działał z scalaniem i dołączaniem?

114

Od kilku dni próbuję zwiększyć wydajność moich układów, konwertując z kilku poziomów zagnieżdżonych LinearLayoutsna jeden RelativeLayouti napotkałem kilka problemów, których nie mogłem znaleźć obejścia ...

Przeszukałem grupę początkujących użytkowników Androida i tę witrynę i nie udało mi się znaleźć niczego, co pomogłoby mi rozwiązać problem.

Czytałem na jednym z blogów, że można łączyć układy ze scalaniem i dołączaniem tagów. Mam więc główny plik układu z RelativeLayoutelementem głównym. Wewnątrz tego mam 5 tagów include, które odwołują się do 5 różnych plików układu XML, z których każdy ma element scalający dla katalogu głównego (wszystkie moje pliki scalające są takie same, z wyjątkiem identyfikatorów w nich).

Mam dwa problemy, które wyjaśnię po opublikowaniu uproszczonej wersji mojego kodu układu:

Przykładowy plik układu głównego:

<?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>

Przykładowy dołączony plik scalający:

<?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>

Mam dwa problemy:

1) android:layout_*Atrybuty wydają się być ignorowane, gdy są używane w tagu włączania, a wszystkie scalone układy są wyświetlane jeden na drugim. Zgodnie z tym postem ( http://developer.android.com/resources/articles/layout-tricks-reuse.html ) „ android:layout_*z <include />tagiem można użyć dowolnego atrybutu ”

2) Ponieważ nie mogłem tego uruchomić, postanowiłem spróbować dodać android:layout_belowatrybut do pierwszego TextViewelementu w każdym pliku układu scalania, co oznacza, że ​​każdy plik scalania będzie odnosił się do identyfikatora z innego pliku układu scalania ... W większości przypadków to faktycznie zadziałało, a mój układ wygląda dobrze. Jednak pojawia się błąd w jednym z android:layout_belowatrybutów, który mówi, że nie może znaleźć identyfikatora, który podałem ... Podwójnie i potrójnie sprawdziłem identyfikatory, aby upewnić się, że są poprawne. Najdziwniejsze jest to, że użyłem tej AutoFillfunkcji, aby umieścić identyfikator w atrybucie na pierwszym miejscu.

Jeśli ktoś ma jakieś sugestie lub obejścia, z przyjemnością je wypróbuję. Poza tym, jeśli ktoś może wymyślić sposób, w jaki mógłbym mieć tylko jeden plik układu scalonego XML zamiast 5, byłoby to bardzo mile widziane. Nie mogłem znaleźć sposobu, aby to zrobić, ponieważ muszę mieć dostęp do każdego elementu w plikach układu scalania w czasie wykonywania ...

Justin
źródło

Odpowiedzi:

214

Wystąpił problem z tagiem include. Sprawdź: https://issuetracker.google.com/issues/36908001

Aby to naprawić, upewnij się, że nadpisałeś OBIE, layout_widtha layout_heightpodczas dołączania , w przeciwnym razie wszystko zostanie zignorowane.

Macarse
źródło
15
Jest to lepsze rozwiązanie niż przyjęte, ponieważ pozwala uniknąć tworzenia zbędnego obiektu układu. Poza tym jest do bani, jak według twórców Androida jest to w porządku.
mikołak
4
Jest to naprawdę łatwiejsze, mniej zakodowane i bardziej zoptymalizowane rozwiązanie niż pakowanie <include /> do innego układu. Pomyśl, co byś zrobił, gdybyś pracował z listami.
teoREtik
2
Działa to znacznie lepiej niż zaakceptowana odpowiedź. Wielkie dzięki! I ... dalej Google, napraw już ten problem, to jest BS! :)
Felipe Caldas
2
@JeffAxelrod, kod źródłowy LayoutInflater pokazuje, że nadpisywanie tagów id, visibility i layout_ * nie jest stosowane, gdy element główny jest tagiem scalającym, niestety. Ponieważ nie możesz mieć widoku jako katalogu głównego xml, musimy mieć tam dodatkową ViewGroup ...
Rafael Nobre
13
Po prostu mi się to nie udało. Mam jedno layout_widthi drugie i layout_heightustawiłem na moim <include>. Próbowałem też ustawić layout_widthi layout_heightna moim, <merge>ale bezskutecznie. Czego mi brakuje ?
dum4ll3
33

Zobacz poniżej bardziej wysoko ocenioną odpowiedź. Mój jest żałośnie przestarzały


mogę rozwiązać jeden problem, który podniósł Justin : niezdolność RelativeLayout do zarządzania pozycjonowaniem dołączenia (przynajmniej w tym prostym przypadku na emulatorze 1.6)

CommonsWare sugeruje owijanie obejmuje w wyjątkowej kontenera nadrzędnego, ale czyni to w celu ułatwienia adresowania i określenie zakresu identycznie nazwanych poglądów w Justin zawiera

Każdy z nich musiałby mieć unikalny kontener nadrzędny i należy wywołać metodę findViewById () na tym kontenerze (ViewGroup), a nie na działaniu.

W rzeczywistości musisz to również zrobić , aby RelativeLayout zachowywał się zgodnie z oczekiwaniami:

To działa ( stopka jest dobrze ustawiona):

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

Tak nie jest ( stopka unosi się u góry ekranu):

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

Uwzględnienie w bosej stopce nie zostanie wyrównane do dołu elementu nadrzędnego bez otaczającego układu LinearLayout. Nie nazwałbym tego oczekiwanego zachowania.

Ponadto WebView wydaje się ładnie dołączać się do nagłówka według identyfikatora, ale uważam, że to iluzja, ponieważ po prostu płynie pod nagłówkiem pionowo. Próbowałem też ustawić przycisk tuż nad dołączeniem stopki, ale wszystko się unosiło i też było źle

RelativeLayout miał więcej problemów w 1.5, ale nadal mi się podoba :)

alienjazzcat
źródło
2
Zwiększyłem go o 1, a następnie zmniejszyłem, widząc komentarz @Macarse, to jest właściwy sposób, aby to zrobić.
Jayshil Dave,
Usuń tę mylącą odpowiedź :(
Daniel Smith,
8

Człowieku, to jest stare, ale wydaje się, że pojawia się na szczycie wyszukiwań, więc zamierzam to skomentować.

Myślę, że sztuczka polega na tym, że <merge>tag połączony z <include>tagiem zasadniczo usuwa jakąkolwiek „nadrzędną” grupę widoków na tym poziomie. Więc kogo dokładnie prosisz o „layout_below” kogoś innego? Nikt. Na tym poziomie nie ma widoku.

<merge>Tag przyjmuje poglądy dzieci i wyskakuje im prawo do nadrzędnego <include>tagu. Dlatego musisz poprosić dzieci w układzie, który dołączasz, aby odpowiednio się zakotwiczyły.

Plantage
źródło
4

Aby pozycjonowanie działało na RelativeLayout, musisz ustawić parametry layout_ * w pliku dołączanym, a nie w głównym pliku układu. W ten sposób

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>

Oczywiście nie tego chcemy my, programiści, ale jest to jedyne rozwiązanie, które znalazłem, aby uniknąć duplikowania xml

Maragues
źródło
1
Dlaczego nie ma głosów za? To zadziałało dla mnie. Nie przepadam za wklejaniem parametrów do obiektu, który może być zawarty w układzie, który ich nie potrzebuje, ale jeśli jest to LinLay, wygląda na to, że layout_ * RelLay są po prostu ignorowane. Czy coś mi brakuje?
QED,
1

Atrybuty android: layout_ * wydają się być ignorowane, gdy są używane w tagu include, a wszystkie scalone układy są wyświetlane jeden na drugim.

Domyślam się, że z reguł układu nie można odwoływać się do android:idatrybutów zdefiniowanych w <include>elementach, a jedynie do tych, które znajdują się w „prawdziwych” widżetach i kontenerach.

Poza tym, jeśli ktoś może wymyślić sposób, w jaki mógłbym mieć tylko jeden plik układu scalonego XML zamiast 5, byłoby to bardzo mile widziane.

Proste: umieść je wszystkie w jednym pliku.

Nie mogłem znaleźć sposobu, aby to zrobić, ponieważ muszę mieć dostęp do każdego elementu w plikach układu scalania w czasie wykonywania

Niezależnie od tego, czy masz jeden <include>element, czy 1000, cała zawartość powinna być dostępna w czasie wykonywania. Jedynym wyjątkiem jest sytuacja, gdy masz zduplikowane android:idatrybuty - musisz odpowiednio findViewById()określić zakres wywołań, aby uzyskać właściwy, tak jak w przypadku pobierania widżetów z wiersza ListView.

Jeśli możesz utworzyć przykładowy projekt, który używa 2+ plików scalania, w którym możesz wykazać, że zawartość nie jest dostępna w czasie wykonywania, daj mi znać.

CommonsWare
źródło
1
Nie chcę umieszczać ich wszystkich w jednym ogromnym pliku układu, ponieważ na dłuższą metę jest to trudniejsze do zarządzania ... Dlatego poprosiłem o możliwość posiadania jednego głównego pliku XML z jednym plikiem scalającym ... ponieważ teraz Mam 5 plików z elementem scalającym jako głównym, które mają dokładnie ten sam układ, z wyjątkiem tego, że identyfikatory są różne. Robię to w ten sposób, aby mieć do nich dostęp w czasie wykonywania. Wydaje się, że zakres mojego wywołania findViewById () jest tym, co chciałbym zrobić. Jak określić zakres tego wywołania, aby móc wielokrotnie dołączyć ten sam plik układu i nadal mieć dostęp do wszystkich składników w czasie wykonywania?
Justin,
1
Dzięki za odpowiedzi. Przyjrzę się temu. W międzyczasie (a może ujawniam tutaj swoją ignorancję) czy zawijanie każdego tagu include w kontenerze nadrzędnym nie pokrzyżuje celu użycia RelativeLayout? Cały szum RelativeLayout polega na unikaniu zagnieżdżonych układów ...
Justin
3
Jedynym powodem, dla którego o to zapytałem, było zaoszczędzenie miejsca i wymyślenie dobrego projektu ... Przeczytałem o możliwości ponownego wykorzystania układu tutaj: developer.android.com/resources/articles/ ... i pomyślałem, że brzmi dobrze. Kiedy próbowałem go wdrożyć, napotkałem pewne problemy. Obecnie mam 5 układów, które są zasadniczo zduplikowane, z wyjątkiem identyfikatorów w nich ... więc pomyślałem, że ponowne użycie układu byłoby dobrym kandydatem. Może czegoś mi tu brakuje, ale wygląda na to, że RelativeLayout to nie wszystko, co jest reklamowane ...
Justin,
1
Wow ... dzięki, że jesteś tak niesamowicie pomocny. Ogólnie Twoje odpowiedzi są bardzo pomocne, więc nie wiem, czy masz po prostu zły dzień, czy co, ale po prostu próbowałem lepiej zrozumieć koncepcje związane z RelativeLayout, tagiem dołączania i tagiem scalającym, na podstawie artykuły, które przeczytałem i staram się znaleźć odpowiednie rozwiązanie dla układu, który chcę osiągnąć.
Justin
1
„A dla prawdziwego ponownego wykorzystania, tworzenie niestandardowych atutów klasy View obejmuje:„ Zgoda. Po prostu nie chciałem tego robić, jeśli istniał stosunkowo łatwy sposób korzystania z podstawowych układów… „Poradziłeś sobie z problemem - nie zdziw się, gdy ludzie na to zareagują. I chociaż moim ostatnim komentarzem jest naćpany, punkty są nadal aktualne. „Poradziłem sobie z problemem, ponieważ czułem, że zrobiłeś to w swoich odpowiedziach. „Przełączanie się do wierszy w ListView może być lepsze”. Listview nie będzie działać z wyglądem mojej aplikacji. Moja aplikacja na rynku to AppSwipe! jeśli chcesz wiedzieć, co robię ...
Justin
1

próbować :

<RelativeLayout xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    tools:showIn="@layout/activity_home">
zahra salmaninejad
źródło
0

W moim przypadku układ, który próbowałem załączyć, zaczyna się od <mergetagu. Kiedy zmieniłem to na układ, powiedz, <RelativeLayoutże zadziałało. Poniżej znajduje się ilustracja.

PRACUJĄCY

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

NIE DZIAŁA

<merge xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    tools:showIn="@layout/activity_home">
Rohit Mandiwal
źródło
stworzy to kolejną zagnieżdżoną warstwę, która nie jest optymalnym rozwiązaniem
Silvia H
0

Miałem ten sam problem, a nawet definiowanie layout_widthi layout_heightto nie działało. Problem polegał na tym, że układ, który dołączałem, miał tagi i po ich usunięciu wszystko działało jak marzenie. Przypuszczam, że scalanie nie jest tagiem układu iz tego powodu nie może odbierać parametrów pozycjonowania i rozmiaru. Ponieważ wszystko, co zdefiniujesz, jest przenoszone do wewnętrznego układu nadrzędnego, ustawienia po prostu zostały wyrzucone.

TL: DR: Po prostu usuń tagi, przenieś definicje xmlns do rzeczywistego obiektu podglądu układu i powinieneś być dobry.

Przed:

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

Pracujący:

<...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
źródło