5.9. Фрагменты

5.9. Фрагменты

Фрагменты в ОС Андроид играют роль кода который может быть использован неограниченное количество раз внутри любого Activity. В первую очередь фрагменты предназначены для переиспользования кода, когда код пишется один раз и используется сколько угодно раз по мере необходимости.

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

Теперь настало время более подробно рассмотреть использование фрагментов. Создайте проект с макетом Empty Activity, затем кликните правой кнопкой мыши на папку с названием пакета приложения и выберите New -> Java Class. Напишите в поле Name: SampleFragment и в поле Superclass напишите Fragment из предложений Android Studio выберите androidx.fragment.app. Конечное значение поля Superclass должно стать androidx.fragment.app.Fragment.

Шаги добавления нового фрагмента
После заполнения полей нажмите ОК и дождитесь сборки проекта.

После добавления класса фрагмента нажмите правой кнопкой мыши на папку res/layout и выберите New -> Layout Resource File. Назовите новый файл fragment_layout.xml и в поле Root Element напишите LinearLayout.

В файле activity_main.xml нет ничего к данному моменту неизученного, его код выглядит так:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
    tools:context=".MainActivity">
    <LinearLayout
        android:id="@+id/fragment_container1"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="vertical" />

    <LinearLayout
        android:id="@+id/fragment_container2"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="#ccd0d1"
        android:orientation="vertical"
        android:padding="10dp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="0.5"
        android:orientation="vertical">

        <EditText
            android:id="@+id/name_field"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:imeOptions="actionSearch"
            android:inputType="text" />

        <Button
            android:id="@+id/send"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Send info" />
    </LinearLayout>
</LinearLayout>

В файле макета фрагмента – fragment_layout.xml напишите следующий код:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Hello Fragment"
        android:textStyle="bold"
        android:textSize="60dp"
        android:id="@+id/text"/>
</LinearLayout>

Откройте файл MainActivity.java и напишите туда этот код:

public class MainActivity extends AppCompatActivity {
    // Объявление переменной для получения доступа к текстовому полю в макете
    EditText editText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // getSupportFragmentManager() - возвращает ссылку на менеджер фрагментов
        // beginTransaction() - заявляет о начале транзакции (включения фрагмент в Activity)
        // setTransition() - устанавливает значение для включения фрагмента с анимацией перехода
        // add() - добавляет фрагмент в транзакцию
        // commit() - начинает транзакцию по включению фрагмента в Activity
        getSupportFragmentManager().beginTransaction()
        .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
        .add(R.id.fragment_container1, new SampleFragment()).commit();

        // Строку 30 можно написать и так, от переноса строк читаемость и ввод изменений облегчились.
        getSupportFragmentManager()
                .beginTransaction()
                .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
                .add(R.id.fragment_container2, new SampleFragment())
                .commit();

        // Получение ссылки на текстовое поле в файле макета
        editText = findViewById(R.id.name_field);
        // Функция setOnEditorActionListener() добавляет обработчик к нажатию кнопок (поиск, переход к следующему полю...) объявленных в файле макета в атрибуте imeOptions, здесь значение imeOptions="actionSearch", что заменяет кнопку Enter на значок поиска и при нажатии на кнопку Поиск вызывается код ниже
        editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                // При нажатии на кнопку поиск функции onEditorAction в параметре actionId передаётся значение определяющее какая кнопка была нажата и в switch case идёт обработка
                switch (actionId) {
                    // Если наша кнопка это кнопка Поиск
                    case EditorInfo.IME_ACTION_SEARCH:
                        // Вызвать функцию ReplaceFragments()
                        ReplaceFragemnts();
                        break;
                }
                return false;
            }
        });
        // Ссылка на кнопку Send Info
        Button btn = findViewById(R.id.send);
        // Подключить обработчик событий для кнопки Send Info
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // Вызвать функцию ReplaceFragments()
                ReplaceFragemnts();
            }
        });

    }

    // Пользовательская функция - функция которую программист сам от себя написал для облегчения своей работы. Пользовательские функции удобно использовать, если надо выполнить один и тот же код несколько раз. Такие функции являются часть написания DRY (Don't Repeat Yourself ) кода, что облегчает дальнейшее изменение или дополнение подобных функция. ReplaceFragemnts - служит для замены существующих фрагментов на новые с переданным параметром.
    void ReplaceFragemnts() {
        // Экземпляр класса фрагмента
        SampleFragment sampleFragment = new SampleFragment();
        // Экземпляр класса для передачи параметров
        Bundle args = new Bundle();
        // Добавление строки в список параметров нашего фрагмента
        args.putString("name", editText.getText().toString());
        // Привязать параметры к экземпляру фрагмента
        sampleFragment.setArguments(args);

        // getSupportFragmentManager() - возвращает ссылку на менеджер фрагментов
        // beginTransaction() - заявляет о начале транзакции (включения фрагмент в Activity)
        // setTransition() - устанавливает значение для включения фрагмента с анимацией перехода
        // add() - добавляет фрагмент в транзакцию
        // commit() - начинает транзакцию по включению фрагмента в Activity
        getSupportFragmentManager()
                .beginTransaction()
                .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
                .replace(R.id.fragment_container1, sampleFragment)
                .addToBackStack(null)
                .commit();

        SampleFragment secondFragment = new SampleFragment();
        Bundle args2 = new Bundle();
        // toUpperCase() - функция которую можно вызвать для строк
        // и служит для смены регистра символов на ПРОПИСНЫЕ
        args2.putString("name", editText.getText().toString().toUpperCase());
        secondFragment.setArguments(args2);

        getSupportFragmentManager()
                .beginTransaction()
                .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
                .replace(R.id.fragment_container2, secondFragment)
                .addToBackStack(null)
                .commit();
    }
}

Откройте файл SampleFragment.java и напишите нижеследующий код:

public class SampleFragment extends Fragment {

    // Функция onCreateView создаёт главный View фрагмента и возвращает его Activity
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Через экземпляр класса Inflater привязываем файл макета к фрагменту. LayoutInflater.inflate аналогичен вызову функции setContentView в Activity
        View v = inflater.inflate(R.layout.fragment_layout, container, false);

        // Получаем ссылку на TextView в нашем фрагменте. Обратите внимание что id TextView получаем через поиск внутри View который привязали через функцию inflate
        TextView textView = v.findViewById(R.id.text);

        // try catch используется для перехвата исключений (ошибок), чтобы система не закрывала приложение, тут используется перехват исключений, потому что, если параметры не переданы с Activity через вызов функции setArguments, выбрасывается исключение, а в коде параметры при первоначальном включении фрагмента в Activity не были добавлены, а только создавался пустой экземпляр фрагмента
        try {
            textView.setText(getArguments().getString("name"));
        } catch (Exception e)
        {
            // Вывести в консоль LogCat сообщение об исключении и порядок вызова функций из стэка вызовов функци до строки вызвавшей исключение
            e.printStackTrace();
        }
        return v;
    }
}

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