Вкладені layout

Один layout може містити інший layout. Для цього застосовується елемент include.

Наприклад, додамо в папку res/layout два файли layout, які нехай називатимуться text_panel.xml і button_panel.xml: У файлі text_panel.xml визначимо такий код:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">
    <TextView
        android:id="@+id/clicksText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="30sp"
        android:text="0 Clicks"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        />
</androidx.constraintlayout.widget.ConstraintLayout>

По суті тут просто визначено поле TextView для виведення тексту. У файлі button_panel.xml визначимо таку розмітку:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click"
        android:onClick="onClick"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        />
 
</androidx.constraintlayout.widget.ConstraintLayout>

Тут визначено кнопку, натискання якої ми будемо обробляти.

Основним файлом розмітки, який визначає інтерфейс програми, як і раніше, є activity_main.xml. Змінимо його:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    tools:context=".MainActivity">
 
    <include
        android:id="@+id/textView"
        layout="@layout/text_panel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toTopOf="@+id/button"
        />
    <include
        android:id="@+id/button"
        layout="@layout/button_panel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView"
        />
 
</androidx.constraintlayout.widget.ConstraintLayout>

За допомогою ConstraintLayout весь інтерфейс тут організовується у вигляді вертикального стека. За допомогою елементів include всередину ConstraintLayout додається вміст файлів text_panel.xml і button_panel.xml. Для зазначення назви файлу застосовується атрибут layout.

Це все одно, що якби ми безпосередньо замість елемента include додали вміст файлів. Однак такий спосіб має свої переваги. Наприклад, якась частина розмітки, група елементів управління може повторюватися в різних activity. І щоб не визначати по сто разів ці елементи, можна винести їх в окремий файл layout і за допомогою include підключати їх.

Після додавання в ConstraintLayout до елементів include можна застосовувати всі ті стандартні атрибути, які застосовуються в цьому контейнері до вкладених елементів, наприклад, налаштувати розміри, розташування. Також варто зазначити, що додавати зовнішні layout можна не тільки в ConstraintLayout, а й в інші контейнери (LinearLayout, RelativeLayout тощо).

Також змінимо код MainActivity:

package com.example.viewapp;
 
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
 
public class MainActivity extends AppCompatActivity {
 
    int clicks = 0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
 
    public void onClick(View view){
        TextView clicksText = findViewById(R.id.clicksText);
        clicks++;
        clicksText.setText(clicks + " Clicks");
    }
}

У MainActivity ми можемо звертатися до елементів у вкладених файлах layout. Наприклад, ми можемо встановити обробник натискання кнопки, у якому під час натискання змінюватимемо текст у TextView.

При цьому ми кілька разів можемо додавати в один файл layout інший файл layout. Для цього спочатку змінимо файл button_panel.xml таким чином:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="#3F51B5"
    android:paddingTop="10dp"
    android:paddingBottom="10dp">
    <Button
        android:id="@+id/clickBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        />
 
</androidx.constraintlayout.widget.ConstraintLayout>

І змінимо файл activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    tools:context=".MainActivity">
 
    <include
        layout="@layout/text_panel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        />
    <include layout="@layout/button_panel"
        android:id="@+id/plus_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/minus_button"/>
 
    <include layout="@layout/button_panel"
        android:id="@+id/minus_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="36dp"
        app:layout_constraintLeft_toRightOf="@id/plus_button"
        app:layout_constraintBottom_toBottomOf="parent"/>
 
</androidx.constraintlayout.widget.ConstraintLayout>

Тепер файл button_panel.xml додається двічі. Важливо, що під час додавання цього файлу кожному елементу include присвоєно певний id. За цим id ми зможемо дізнатися, про який саме елемент include ідеться.

Також змінимо MainActivity:

package com.example.viewapp;
 
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
 
public class MainActivity extends AppCompatActivity {
 
    int clicks = 0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        View plusButtonView = findViewById(R.id.plus_button);
        View minusButtonView = findViewById(R.id.minus_button);
        TextView clicksText = findViewById(R.id.clicksText);
 
        Button plusButton = plusButtonView.findViewById(R.id.clickBtn);
        Button minusButton = minusButtonView.findViewById(R.id.clickBtn);
 
        plusButton.setText("+");
        minusButton.setText("-");
 
        plusButton.setOnClickListener(v -> {
            clicks++;
            clicksText.setText(clicks + " Clicks");
        });
        minusButton.setOnClickListener(v -> {
            clicks--;
            clicksText.setText(clicks + " Clicks");
        });
    }
}

Тут спочатку ми отримуємо окремі елементи include за id. Потім у межах цих елементів отримуємо кнопку. Після цього ми можемо встановити біля кнопки будь-який текст і повісити обробник події натискання. І таким чином, поведінка обох кнопок буде відрізнятися.