Android Fragment. Что это?

В этом уроке, я хочу объяснить, что такое Fragment и почему его нужно использовать. Мы рассмотрим немного теории, а также практический пример применения Fragments.

Представьте себе пазл, который вы можете сложить и получить общий вид картинки, именно так можно представить Fragment. Или же если вы работали разработкой сайтов, а именно PHP то вам знакома возможность подключать общие части сайта например меню как отдельного блока с помощью include.

 

Шаг 0. Теория

Так как определение не совсем понятно, я решил сказать своими словами его.

Fragment (Фрагмент) — по сути это подобие Activity, которое мы можем подключать в разные части приложения. Но одно Activity может содержать несколько fragment.

Фрагменты появились в API 11 (Android 3.0) для поддержки на более старых версиях был доработан Android Support library.

Также важно понимать, что фрагменты — это не замена активности, они не могут существовать сами по себе, а только вместе с Activity. Поэтому в AndroidManifest регистрировать Fragments не нужно.

 

Шаг 1. Создаем проект

Давайте создадим простой проект, пока без использования Fragment-ов. Создаем Android Gradle Project:

Теперь создадим Activity в пакете com.myfragmentexam.app назовем его MainActivity:

package com.myfragmentexam.app;

import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {

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

Для этого activity нам нужно создать layout, создаем в res/layout новый layout и называем его main_layout:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Кнопка 1"
            android:id="@+id/button" android:layout_marginTop="20dp"/>
    <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Кнопка 2"
            android:id="@+id/button2"/>
    <CheckBox
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Новый компонент 1"
            android:id="@+id/checkBox" android:layout_gravity="center_horizontal" android:checked="false"
            android:layout_marginTop="20dp"/>
    <CheckBox
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Новый компонент 2"
            android:id="@+id/checkBox2" android:layout_gravity="center_horizontal" android:checked="false"/>
    <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/imageView" android:layout_gravity="center_horizontal" 
            android:src="@drawable/android_img"
            android:layout_marginTop="20dp"/>
</LinearLayout>

Как видите в 33-й строке мы указываем на ресурс изображения, для этого вам нужно в res/drawable добавить следующее изображение:

Последним шагом нужно зарегистрировать activity MainActivity в AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.myfragmentexam.app">

    <application android:allowBackup="true"
        android:label="@string/app_name"
        android:icon="@drawable/ic_launcher"
        android:theme="@style/AppTheme">

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>

</manifest>

Начиная с 9-й строки мы регистрируем Activity в контексте.

Теперь запустим. Мы должны увидеть следующее:

Пока это простое приложение, которое не выполняет никакой логики и не имеет фрагментов.

 

Шаг 2. Делим на части

Особеность Fragment-ов в том, что вы можите разбить внешний вид на блоки и потом подключать их для отображения на разных стройствах по своему (смартфон/планшет). Также мы получим возможность переиспользования блоков(Fragments).

Первыйм делом вынесем кнопки в отдельный фрагмент, в res/layout создаем новый layout и называем его button_fragment:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Кнопка 1"
            android:id="@+id/button" android:layout_marginTop="20dp"/>
    <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Кнопка 2"
            android:id="@+id/button2"/>

</LinearLayout>

Выглядеть это будет так:

 

Теперь вынесем CheckBox-сы в отдельный layout назовем его checkbox_fragment:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <CheckBox
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Новый компонент 1"
            android:id="@+id/checkBox" android:layout_gravity="center_horizontal" android:checked="false"
            android:layout_marginTop="20dp"/>
    <CheckBox
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Новый компонент 2"
            android:id="@+id/checkBox2" android:layout_gravity="center_horizontal" android:checked="false"/>

</LinearLayout>

Выглядеть это будет так:

 

И вынесем картинку в layout назвав его image_fragment:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/imageView" android:layout_gravity="center_horizontal"
            android:src="@drawable/android_img"
            android:layout_marginTop="20dp"/>

</LinearLayout>

Выглядеть это будет так:

 

Шаг 3. Создаем Fragments

Мы поделили наш первоначальный вид на части, теперь давайте создадим на их основе фрагменты.

Посмотрите на структуру классов ниже и на её основе создайте пакет fragments и в нем классы классы ButtonFragmentCheckBoxFragmentImageFragment.

 

Для того чтобы все эти классы стали фрагментами их нужно унаследовать от класса Fragment. Обратите внимание что класс фрагмент мы будем использовать не со стандартной библиотеки android.app, а с android.support.v4.app.

Причина этого то что фрагменты в вспомогательном пакете более лучше поддерживаются и если сравнивать со стандартным, то он не имеет тех багов, которые имеет стандартный пакет(android.app).

В Activity мы подключали layout в методе onCreate() через метод setContentView(). Но в фрагментах метод onCreate() используется немного для других целей. Поэтому для подключения layout используется отдельный метод onCreateView().

 

Давайте начнем с ButtonFragment:

package com.myfragmentexam.app.fragments;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.myfragmentexam.app.R;

public class ButtonFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, 
                             Bundle savedInstanceState) {
        
        View viewHierarchy = inflater.inflate(R.layout.button_fragment, container, false);
        
        return viewHierarchy;
    }
}

Теперь детальней рассмотрим что же мы тут делаем.

LayoutInflater —  позволяет построить нужный макет, считывая информацию из указанного XML-файла.

Обратите внимание на то что 16-й строке в метод inflate() мы передаем 3 параметра разметку нашуго фрагмента, контейнер, и значение (true |false), которое указывает на возможнось подключения фрагментов в Activity через контейнер. Мы указали false так как сами создаем блоки для фрагментов.

 

Теперь по аналогии создадим реализацию для фрагмента CheckBoxFragment:

package com.myfragmentexam.app.fragments;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.myfragmentexam.app.R;

public class CheckBoxFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        View viewHierarchy = inflater.inflate(R.layout.checkbox_fragment, container, false);

        return viewHierarchy;
    }

}

 

И для последнего ImageFragment:

package com.myfragmentexam.app.fragments;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.myfragmentexam.app.R;

public class ImageFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        View viewHierarchy = inflater.inflate(R.layout.image_fragment, container, false);

        return viewHierarchy;
    }
}

 

Шаг 4. Собираем все в кучу

Мы создали пачку фагментов, но что с ними делать? Их мы теперь можем подключать в те места куда нам нужно. Для примера мы переделаем MainActivity и main_layout под фрагменты.

Для начало давайте модифицируем MainActivity:

package com.myfragmentexam.app;

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;

public class MainActivity extends FragmentActivity {

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

}

Обратите на строку 6, как видите мы наследуемся не просто от Activity, а от FragmentActivity это нужно делать когда вы используете Fragment с пакета android.support.v4.app.

Теперь модифицируем layout для нашего Activity, а именно main_layout:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent">

    <fragment
            android:name="com.myfragmentexam.app.fragments.ButtonFragment"
            android:id="@+id/button_fragment"
            android:layout_height="wrap_content"
            android:layout_width="match_parent"/>

    <fragment
            android:name="com.myfragmentexam.app.fragments.CheckBoxFragment"
            android:id="@+id/checkbox_fragment"
            android:layout_height="wrap_content"
            android:layout_width="match_parent"/>

    <fragment
            android:name="com.myfragmentexam.app.fragments.ImageFragment"
            android:id="@+id/image_fragment"
            android:layout_weight="1"
            android:layout_height="0dp"
            android:layout_width="match_parent"/>

</LinearLayout>

Обратите внимание на выделенные строки, тут мы указываем какой фрагмент подключать.

Но теперь в Intellij IDEA режим Preview работает не корректно:

Для того чтобы это исправить нам нужно немного модифицировать main_layout:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:tools="http://schemas.android.com/tools"
              android:orientation="vertical"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent">

    <fragment
            android:name="com.myfragmentexam.app.fragments.ButtonFragment"
            android:id="@+id/button_fragment"
            android:layout_height="wrap_content"
            android:layout_width="match_parent"
            tools:layout="@layout/button_fragment"/>

    <fragment
            android:name="com.myfragmentexam.app.fragments.CheckBoxFragment"
            android:id="@+id/checkbox_fragment"
            android:layout_height="wrap_content"
            android:layout_width="match_parent"
            tools:layout="@layout/checkbox_fragment"/>

    <fragment
            android:name="com.myfragmentexam.app.fragments.ImageFragment"
            android:id="@+id/image_fragment"
            android:layout_weight="1"
            android:layout_height="0dp"
            android:layout_width="match_parent"
            tools:layout="@layout/image_fragment"/>

</LinearLayout>

На выделенных строках видно изменения, таким образом мы указали фрагментам на нашем layout какие layout они отображают.

И теперь режим Preview работает корректно:

После этого, можно запустить и проверить. Вы должны увидеть тоже что показанно на Preview.

 

Шаг 5. Добавляем альбомный вид

Для этого создаем в res папке layout-land и копируем в в него все содержимое папки layout.

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

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:orientation="horizontal"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent">

    <fragment
            android:name="com.myfragmentexam.app.fragments.ButtonFragment"
            android:id="@+id/button_fragment"
            android:layout_height="wrap_content"
            android:layout_width="match_parent"
            tools:layout="@layout/button_fragment"
            android:layout_alignParentLeft="true"
            android:layout_marginLeft="15dp"
            android:layout_alignParentTop="true"
            android:layout_toLeftOf="@+id/image_fragment"/>

    <fragment
            android:name="com.myfragmentexam.app.fragments.CheckBoxFragment"
            android:id="@+id/checkbox_fragment"
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"
            tools:layout="@layout/checkbox_fragment"
            android:layout_below="@+id/button_fragment"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true"
            android:layout_toLeftOf="@+id/image_fragment"
            android:layout_marginLeft="15dp"/>

    <fragment
            android:name="com.myfragmentexam.app.fragments.ImageFragment"
            android:id="@+id/image_fragment"
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"
            tools:layout="@layout/image_fragment"
            android:layout_alignParentTop="true"
            android:layout_alignParentRight="true"
            android:layout_alignParentEnd="true"
            android:layout_marginRight="15dp"
            android:layout_marginLeft="15dp"/>

</RelativeLayout>

Теперь запускаем и смотрим на результат:

Теперь у нас есть вид как для смартфонов так и для планшетов.

Урок создан: 24 мая 2014 | Просмотров: 28654 | Автор: Александр Барчук | Правила перепечатки


Добавить комментарий

Добавить комментарий

Ваш e-mail не будет опубликован.

Комментарии:

  • 28 мая 2014 в 19:10

    Сергей

    Отличная статья. Сразу стало все понятно по фрагментам

  • 17 июня 2014 в 09:36

    satun

    А есть возможность вызывать фрагменты например при нажатию на кнопку?

  • 19 января 2015 в 12:20

    Аноним

    Отличная, доступная статья!

  • 21 февраля 2015 в 20:33

    Сергей

    А можно ли подключать гифки?

  • 28 февраля 2015 в 16:32

    Andrew

    Отлично всё расписано, благодорю за трурд, очень помогло!

  • 12 марта 2015 в 14:12

    АР

    имеется ли возможность с фрагмента открыть навый активити через свой меню фрагмента отдельно от основного активити в ActionBar-е

  • 18 марта 2015 в 21:49

    дима

    полностью отказался от фрагментов. юзаю либу которая работает через релатив лайауты. 1 активити на все приложение из 20 экранов.
    с этими фрагментами одни проблемы.

    • 30 июня 2015 в 19:42

      Федор

      Не подскажите, что за библиотека?

    • 05 октября 2015 в 15:13

      Имя

      фрагменты должны умереть, слава custom view
      жизненный цикл усложнен что пц, куча негарантированных вызовов, утечки памяти у фрагментов в стеке
      а задумка была хорошая

  • 04 июня 2015 в 01:09

    Андрей

    Не совсем поянтно, для чего тут фрагменты используются. Точно так же можно сделать лейаут для лендскейпа и без фрагментов.
    Статья то хороша, понятна и проста, но немешало бы добавить чего-нибудь специфичного для фрагментов. Ну и объяснений почему для этого надо именно фрагменты использовать.

    А так, все это вполня и на заменяется

    • 20 апреля 2016 в 19:54

      Vladimir

      С помощью фрагментов можно сделать такую фишку, когда на планшете все размещается на одной активити, а на мобилке на двух. Пример почта Gmail.