Pull to refresh

Немного о Fragment

Reading time 5 min
Views 73K
Добрый день Хабр, в этой статье я хочу рассказать о таком интересном элементе как Fragment, эта статья не научный прорыв, а просто небольшой туториал о использовании этого элемента. Всем, кому интересно узнать, что-то новое, прошу под кат.

Fragment — модульная часть activity, у которой свой жизненный цикл и свои обработчики различных событий. Android добавил фрагменты с API 11, для того, чтобы разработчики могли разрабатывать более гибкие пользовательские интерфейсы на больших экранах, таких как экраны планшетов. Через некоторое время была написана библиотека, которая добавляет поддержку фрагментов в более старые версии.

Плюсы по сравнению с использованием activity видны сразу:
  • С помощью них можно легко сделать дизайн адаптивный под планшеты
  • Разделение кода на функциональные модули, а следовательно поддержка кода обходится дешевле


Основные классы


Есть три основных класса:
android.app.Fragment — от него, собственно говоря. и будут наследоваться наши фрагменты
android.app.FragmentManager — с помощью экземпляра этого класса происходит все взаимодействие между фрагментами
android.app.FragmentTransaction — ну и этот класс, как понятно по названию, нужен для совершения транзакций.
В настоящее время появляются разновидности класса Fragment, для решения определенных задач — ListFragment, PreferenceFragment и др.

Основы работы с fragment'ами


Чтобы создать фрагмент все что нужно это наследовать свой класс от Fragment. Чтобы привязать фрагмент к определенной разметке нужно определить в нем метод onCreateView(). Этот метод возвращает View, которому и принадлежит ваш фрагмент.

public class ExampleFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.you_layout_for_fragment,  container, false);

        return view;
    }
}


Чтобы получить это View из любого места фрагмента достаточно вызвать getView()

Фрагмент мы создали, но хотелось бы поместить его на экран. Для этого нам нужно получить экземпляр FragmentManager и совершить нужную нам транзакцию.
Сначала нужно узнать что мы с фрагментом можем сделать:
add() — добавление фрагмента
remove() — удаление фрагмента
replace() — замена фрагмента
hide() — делает фрагмент невидимым
show() — отображает фрагмент

Так же для того чтобы добавлять наши транзакции в стек, как это происходит по умолчанию с активностями, можно использовать addToBackStack(String), а чтобы вернуть предыдущее состояние стэка нужно вызвать метод popBackStack().

Добавим фрагмент на экран:

ExampleFragment youFragment = new ExampleFragment();  
FragmentManager fragmentManager = getFragmentManager()
fragmentManager.beginTransaction()          // получаем экземпляр FragmentTransaction 
    .add(R.id.container_for_fragments, youFragment)
    .addToBackStack("myStack")
    .commit();            // вызываем commit для совершения действий FragmentTransaction


Как связать activity и fragment?


Чтобы вызывать методы активити, достаточно получить его экземпляр через метод getActivity()

if (getActivity() != null)
        MainActivity act = (MainActivity ) getActivity();


Для того же чтобы получить доступ к фрагменту, у нас есть ссылка на объект фрагмента, которую мы создали при совершении транзакции

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

Иногда нам нужно передать во фрагмент какое то значение или целый обьект, например у нас есть список покупок по нажатию на элемент которого открывается подробное описание этой покупки. Сразу хочется взять и передать этот параметр через конструктор фрагмента, но google категорически не советует переопрелять кострукторы фрагментов. Поэтому есть два выхода из этой ситуации
  • сериализовать объект
  • передать объект в контейнере Parcel, переопределив методы Parcable
  • кое-где видел, что для создания фрагмента через переопределенный конструктор, создают статический фабричный метод


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

public class MyObject implements Parcelable {

  public String paramOne;
  public int paramToo;

  public MyObject(String paramOne, int paramToo) {
    this.paramOne = paramOne;
    this.paramToo = paramToo;
  }
  
  private MyObject(Parcel parcel) {  // Создание объекта через Parcel
    paramOne = parcel.readString();
    paramToo = parcel.readInt();
  }

  public int describeContents() {
    return 0;
  }

  public void writeToParcel(Parcel parcel, int flags) {      //Упаковывание объекта в Parcel
    parcel.writeString(paramOne);
    parcel.writeInt(paramToo);
  }

  public static final Parcelable.Creator<MyObject> CREATOR = new Parcelable.Creator<MyObject>() {   // Статический метод с помощью которого создаем обьект
    public MyObject createFromParcel(Parcel in) {
      return new MyObject(in);
    }

    public MyObject[] newArray(int size) {
      return new MyObject[size];
    }
  };
}


Здесь мы реализовали интерфейс Parcelable в классе, который хотим передавать между фрагментами.
Чтобы передать его во фрагмент нужно сделать следующее:

MyObject obj = new MyObject("Строка",  1 );
        Bundle bundle = new Bundle();
        bundle.putParcelable("key", obj);
        youFragment.setArguments(bundle);


Дальше нужно всего лишь получить переданный нами объект в методе onCreateView() нового фрагмента:
         @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.you_layout_for_fragment,  container, false);
        if(getArguments() != null)
              MyObject obj = getArguments().getParcelable("key");

        return view;
    }


UPD. исправил получение объекта obj из getArguments(), спасибо firexel

Анимация фрагментов


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

Чтобы создать свою анимацию добавления и удаления фрагмента? нужно создать два файла в директории res/animator, один из них будет служить для анимации добавления, второй для удаления

Приведу пример одного из них:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:ordering="together">

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
                android:interpolator="@android:anim/accelerate_decelerate_interpolator"
                android:propertyName="x"                     //свойство которое будет изменяться
                android:valueType="floatType"
                android:valueTo="0"                               //показывает координату начала анимации
                android:valueFrom="1280"                   // координата конца анимации
                android:duration="2000"/>                    //длительность анимации

</set>


Корневым элементом служит objectAnimator, его атрибуты и задают параметры анимации.
Теперь нам нужно вызвать метод setCustomAnimations() с нашими анимациями и при следующей транзакции наши фрагменты оживут.

ExampleFragment youFragment = new ExampleFragment();  
FragmentManager fragmentManager = getFragmentManager()
fragmentManager.beginTransaction()        
    .setCustomAnimations(R.animator.show_fr, R.animator.remove_fr);
    .add(R.id.container_for_fragments, youFragment)
    .addToBackStack("myStack")
    .commit();          


Много еще можно говорить о фрагментах, хотя большая часть всего уже написана, я же хотел объединить все основные знания о фрагментах, чтобы человек, ничего не знающий о этом элементе, после прочтения стал свободно ими пользоваться.
Tags:
Hubs:
-5
Comments 5
Comments Comments 5

Articles