Поиск…


Вступление

Введение об фрагментах и ​​их механизм взаимодействия

Синтаксис

  • void onActivityCreated (Bundle savedInstanceState) // Вызывается, когда была создана активность фрагмента и создана иерархия представлений этого фрагмента.

  • void onActivityResult (int requestCode, int resultCode, Intent data) // Получать результат из предыдущего вызова startActivityForResult (Intent, int).

  • void onAttach (активность активности) // Этот метод устарел на уровне API 23. Вместо этого используйте onAttach (Context).

  • void onAttach (контекст контекста) // Вызывается, когда фрагмент сначала привязан к его контексту.

  • void onAttachFragment (фрагмент childFragment) // Вызывается, когда фрагмент прикреплен как дочерний элемент этого фрагмента.

  • void onConfigurationChanged (Конфигурация newConfig) // Вызывается системой при изменении конфигурации устройства во время работы вашего компонента.

  • void onCreate (Bundle savedInstanceState) // Вызывается для первоначального создания фрагмента.

  • Просмотр onCreateView (LayoutInflater inflater, контейнер ViewGroup, Bundle savedInstanceState) // Вызывается, чтобы фрагмент создавал экземпляр своего пользовательского интерфейса.

  • void onDestroy () // Вызывается, когда фрагмент больше не используется.

  • void onDestroyView () // Вызывается, когда представление, ранее созданное onCreateView (LayoutInflater, ViewGroup, Bundle), было отделено от фрагмента.

  • void onDetach () // Вызывается, когда фрагмент больше не привязан к его активности.

  • void onInflate (активность активности, AttributeSet attrs, Bundle savedInstanceState) // Этот метод был устаревшим на уровне API 23. Вместо этого используйте onInflate (Context, AttributeSet, Bundle).

  • void onInflate (контекст контекста, AttributeSet attrs, Bundle savedInstanceState) // Вызывается, когда фрагмент создается как часть инфляции макета представления, как правило, из настройки представления контента для действия.

  • void onPause () // Вызывается, когда фрагмент больше не возобновляется.

  • void onResume () // Вызывается, когда фрагмент виден пользователю и активно работает.

  • void onSaveInstanceState (Bundle outState) // Вызывается, чтобы попросить фрагмент сохранить его текущее динамическое состояние, чтобы впоследствии его можно было восстановить в новом экземпляре его процесса.

  • void onStart () // Вызывается, когда фрагмент отображается пользователю.

  • void onStop () // Вызывается, когда фрагмент больше не запускается.

  • void onViewStateRestored (Bundle savedInstanceState) // Вызывается, когда все сохраненное состояние было восстановлено в иерархию представления фрагмента.

замечания

Фрагмент представляет собой поведение или часть пользовательского интерфейса в Activity. Вы можете объединить несколько фрагментов в одном действии для создания многоуровневого пользовательского интерфейса и повторного использования фрагмента в нескольких действиях. Вы можете представить фрагмент как модульный раздел активности, который имеет свой собственный жизненный цикл, получает свои собственные входные события и которые вы можете добавлять или удалять во время работы (вроде как «вспомогательная активность», которую вы можете повторное использование в разных видах деятельности).

Конструктор

Каждый фрагмент должен иметь пустой конструктор , поэтому его можно создать при восстановлении состояния своей активности. Настоятельно рекомендуется, чтобы в подклассах не было других конструкторов с параметрами, поскольку эти конструкторы не будут вызываться, когда фрагмент будет повторно создан; вместо этого аргументы могут предоставляться вызывающим абонентом с помощью setArguments (Bundle), а затем извлекаются фрагментом с помощью getArguments ().

Шаблон newInstance ()

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

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

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

public class MyFragment extends Fragment
{
  // Our identifier for obtaining the name from arguments
  private static final String NAME_ARG = "name";

  private String mName;

  // Required
  public MyFragment(){}

  // The static constructor.  This is the only way that you should instantiate
  // the fragment yourself
  public static MyFragment newInstance(final String name) {
    final MyFragment myFragment = new MyFragment();
    // The 1 below is an optimization, being the number of arguments that will
    // be added to this bundle.  If you know the number of arguments you will add
    // to the bundle it stops additional allocations of the backing map.  If
    // unsure, you can construct Bundle without any arguments
    final Bundle args = new Bundle(1);

    // This stores the argument as an argument in the bundle.  Note that even if
    // the 'name' parameter is NULL then this will work, so you should consider
    // at this point if the parameter is mandatory and if so check for NULL and
    // throw an appropriate error if so
    args.putString(NAME_ARG, name);

    myFragment.setArguments(args);
    return myFragment;
  }

  @Override
  public void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    final Bundle arguments = getArguments();
    if (arguments == null || !arguments.containsKey(NAME_ARG)) {
      // Set a default or error as you see fit
    } else {
      mName = arguments.getString(NAME_ARG);
    }
  }
}

Теперь в Управлении:

FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
MyFragment mFragment = MyFragment.newInstance("my name");
ft.replace(R.id.placeholder, mFragment);
//R.id.placeholder is where we want to load our fragment
ft.commit();

Этот шаблон является наилучшей практикой для обеспечения того, чтобы все необходимые аргументы были переданы фрагментам при создании. Обратите внимание, что когда система уничтожает фрагмент и повторно создает его позже, он автоматически восстанавливает свое состояние - но вы должны предоставить ему реализацию onSaveInstanceState(Bundle) .

Навигация между фрагментами с использованием backstack и статической структуры ткани

Прежде всего, нам нужно добавить наш первый Fragment в начале, мы должны сделать это в onCreate() нашей деятельности:

if (null == savedInstanceState) {
    getSupportFragmentManager().beginTransaction()
      .addToBackStack("fragmentA")
      .replace(R.id.container, FragmentA.newInstance(), "fragmentA")
      .commit();
}

Затем нам нужно управлять нашей задней стопой. Самый простой способ - использовать функцию, добавленную в нашу деятельность, которая используется для всех FragmentTransactions.

public void replaceFragment(Fragment fragment, String tag) {
    //Get current fragment placed in container
    Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.container);

    //Prevent adding same fragment on top
    if (currentFragment.getClass() == fragment.getClass()) {
        return;
    }

    //If fragment is already on stack, we can pop back stack to prevent stack infinite growth
    if (getSupportFragmentManager().findFragmentByTag(tag) != null) {
        getSupportFragmentManager().popBackStack(tag, FragmentManager.POP_BACK_STACK_INCLUSIVE);
    }

    //Otherwise, just replace fragment
    getSupportFragmentManager()
            .beginTransaction()
            .addToBackStack(tag)
            .replace(R.id.container, fragment, tag)
            .commit();
}

Наконец, мы должны переопределить onBackPressed() чтобы выйти из приложения, возвращаясь из последнего фрагмента, доступного в backstack.

@Override
public void onBackPressed() {
    int fragmentsInStack = getSupportFragmentManager().getBackStackEntryCount();
    if (fragmentsInStack > 1) { // If we have more than one fragment, pop back stack
        getSupportFragmentManager().popBackStack();
    } else if (fragmentsInStack == 1) { // Finish activity, if only one fragment left, to prevent leaving empty screen
        finish();
    } else {
        super.onBackPressed();
    }
}

Исполнение в деятельности:

replaceFragment(FragmentB.newInstance(), "fragmentB");

Выполнение внешней деятельности (при условии, что MainActivity - это наша деятельность):

((MainActivity) getActivity()).replaceFragment(FragmentB.newInstance(), "fragmentB");

Передача данных из Activity в Fragment с помощью Bundle

Все фрагменты должны иметь пустой конструктор (т. Е. Метод конструктора без входных аргументов). Поэтому, чтобы передать ваши данные в созданный фрагмент, вы должны использовать метод setArguments() . Эти методы получают пакет, в который хранятся ваши данные, и хранит Bundle в аргументах. Впоследствии этот Bundle может быть восстановлен в onCreate() и onCreateView() вызов фрагмента.

Деятельность:

 Bundle bundle = new Bundle();
 String myMessage = "Stack Overflow is cool!";
 bundle.putString("message", myMessage );
 FragmentClass fragInfo = new FragmentClass();
 fragInfo.setArguments(bundle);
 transaction.replace(R.id.fragment_single, fragInfo);
 transaction.commit();

Фрагмент:

 @Override
 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    String myValue = this.getArguments().getString("message");
    ...
 }

Отправка событий обратно в действие с интерфейсом обратного вызова

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

пример

Отправлять обратный вызов в действие, когда кнопка фрагмента нажата

Прежде всего, определите интерфейс обратного вызова:
public interface SampleCallback {
    void onButtonClicked();
}

Следующий шаг - назначить этот обратный вызов в фрагменте:

public final class SampleFragment extends Fragment {

    private SampleCallback callback;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof SampleCallback) {
            callback = (SampleCallback) context;
        } else {
            throw new RuntimeException(context.toString()
                    + " must implement SampleCallback");
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        callback = null;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        final View view = inflater.inflate(R.layout.sample, container, false);
        // Add button's click listener
        view.findViewById(R.id.actionButton).setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                callback.onButtonClicked(); // Invoke callback here
            }
        });
        return view;
    }
}

И, наконец, реализовать обратный вызов в действии:

public final class SampleActivity extends Activity implements SampleCallback {

    // ... Skipped code with settings content view and presenting the fragment

    @Override
    public void onButtonClicked() {
        // Invoked when fragment's button has been clicked
    }
}

Анимировать переход между фрагментами

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

Для одного FragmentTransaction существует два разных способа выполнения анимаций: вы можете использовать стандартную анимацию или можете предоставить свои собственные анимации.

Стандартные анимации определяются вызовом FragmentTransaction.setTransition(int transit) и использованием одной из предварительно определенных констант, доступных в классе FragmentTransaction . На момент написания эти константы:

FragmentTransaction.TRANSIT_NONE
FragmentTransaction.TRANSIT_FRAGMENT_OPEN
FragmentTransaction.TRANSIT_FRAGMENT_CLOSE
FragmentTransaction.TRANSIT_FRAGMENT_FADE

Полная транзакция может выглядеть примерно так:

getSupportFragmentManager()
    .beginTransaction()
    .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
    .replace(R.id.contents, new MyFragment(), "MyFragmentTag")
    .commit();

Пользовательские анимации задаются путем вызова либо FragmentTransaction.setCustomAnimations(int enter, int exit) либо FragmentTransaction.setCustomAnimations(int enter, int exit, int popEnter, int popExit) .

Анимации enter и exit будут воспроизводиться для FragmentTransaction s, которые не включают выпадение фрагментов из заднего стека. popEnter и popExit будут воспроизводиться при появлении фрагмента из заднего стека.

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

getSupportFragmentManager()
    .beginTransaction()
    .setCustomAnimations(R.anim.slide_in_left, R.anim.slide_out_right)
    .replace(R.id.contents, new MyFragment(), "MyFragmentTag")
    .commit();

Определения анимации XML будут использовать тег objectAnimator . Пример файла slide_in_left.xml может выглядеть примерно так:

<?xml version="1.0" encoding="utf-8"?>
<set>
  <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:propertyName="x" 
    android:valueType="floatType"
    android:valueFrom="-1280"
    android:valueTo="0" 
    android:duration="500"/>
</set>

Связь между фрагментами

Все сообщения между фрагментами должны проходить через Activity. Фрагменты не могут связываться друг с другом без участия.

Дополнительные ресурсы

В этом примере у нас есть MainActivity котором размещены два фрагмента, SenderFragment и ReceiverFragment , для отправки и получения message (простая строка в этом случае) соответственно.

Кнопка в SenderFragment инициирует процесс отправки сообщения. TextView в ReceiverFragment обновляется, когда сообщение получено им.

Ниже приведен фрагмент для MainActivity с комментариями, объясняющими важные строки кода:

// Our MainActivity implements the interface defined by the SenderFragment. This enables
// communication from the fragment to the activity
public class MainActivity extends AppCompatActivity implements SenderFragment.SendMessageListener {

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

/**
 * This method is called when we click on the button in the SenderFragment
 * @param message The message sent by the SenderFragment
 */
@Override
public void onSendMessage(String message) {
    // Find our ReceiverFragment using the SupportFragmentManager and the fragment's id
    ReceiverFragment receiverFragment = (ReceiverFragment)
            getSupportFragmentManager().findFragmentById(R.id.fragment_receiver);

    // Make sure that such a fragment exists
    if (receiverFragment != null) {
        // Send this message to the ReceiverFragment by calling its public method
        receiverFragment.showMessage(message);
    }
}
}

Файл макета для MainActivity содержит два фрагмента внутри LinearLayout:

<?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:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.naru.fragmentcommunication.MainActivity">

<fragment
    android:id="@+id/fragment_sender"
    android:name="com.naru.fragmentcommunication.SenderFragment"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1"
    tools:layout="@layout/fragment_sender" />

<fragment
    android:id="@+id/fragment_receiver"
    android:name="com.naru.fragmentcommunication.ReceiverFragment"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1"
    tools:layout="@layout/fragment_receiver" />
</LinearLayout>

SenderFragment предоставляет интерфейс SendMessageListener который помогает MainActivity знать, когда была нажата кнопка в SenderFragment .

Ниже приведен фрагмент кода для SenderFragment объясняющий важные строки кода:

public class SenderFragment extends Fragment {

private SendMessageListener commander;

/**
 * This interface is created to communicate between the activity and the fragment. Any activity
 * which implements this interface will be able to receive the message that is sent by this
 * fragment.
 */
public interface SendMessageListener {
    void onSendMessage(String message);
}

/**
 * API LEVEL >= 23
 * <p>
 * This method is called when the fragment is attached to the activity. This method here will
 * help us to initialize our reference variable, 'commander' , for our interface
 * 'SendMessageListener'
 *
 * @param context
 */
@Override
public void onAttach(Context context) {
    super.onAttach(context);
    // Try to cast the context to our interface SendMessageListener i.e. check whether the
    // activity implements the SendMessageListener. If not a ClassCastException is thrown.
    try {
        commander = (SendMessageListener) context;
    } catch (ClassCastException e) {
        throw new ClassCastException(context.toString()
                + "must implement the SendMessageListener interface");
    }
}

/**
 * API LEVEL < 23
 * <p>
 * This method is called when the fragment is attached to the activity. This method here will
 * help us to initialize our reference variable, 'commander' , for our interface
 * 'SendMessageListener'
 *
 * @param activity
 */
@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    // Try to cast the context to our interface SendMessageListener i.e. check whether the
    // activity implements the SendMessageListener. If not a ClassCastException is thrown.
    try {
        commander = (SendMessageListener) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString()
                + "must implement the SendMessageListener interface");
    }
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                         @Nullable Bundle savedInstanceState) {
    // Inflate view for the sender fragment.
    View view = inflater.inflate(R.layout.fragment_receiver, container, false);

    // Initialize button and a click listener on it
    Button send = (Button) view.findViewById(R.id.bSend);
    send.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            // Sanity check whether we were able to properly initialize our interface reference
            if (commander != null) {

                // Call our interface method. This enables us to call the implemented method
                // in the activity, from where we can send the message to the ReceiverFragment.
                commander.onSendMessage("HELLO FROM SENDER FRAGMENT!");
            }
        }
    });

    return view;
}
}

Файл макета для SenderFragment :

<?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:gravity="center"
    android:orientation="vertical">

<Button
    android:id="@+id/bSend"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="SEND"
    android:layout_gravity="center_horizontal" />
</LinearLayout>

Функция ReceiverFragment проста и предоставляет простой публичный метод для обновления TextView. Когда MainActivity получает сообщение от SenderFragment он вызывает этот общедоступный метод ReceiverFragment

Ниже приведен фрагмент кода для ReceiverFragment с комментариями, объясняющими важные строки кода:

public class ReceiverFragment extends Fragment {
TextView tvMessage;

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                         @Nullable Bundle savedInstanceState) {
    // Inflate view for the sender fragment.
    View view = inflater.inflate(R.layout.fragment_receiver, container, false);

    // Initialize the TextView
    tvMessage = (TextView) view.findViewById(R.id.tvReceivedMessage);

    return view;
}


/**
 * Method that is called by the MainActivity when it receives a message from the SenderFragment.
 * This method helps update the text in the TextView to the message sent by the SenderFragment.
 * @param message Message sent by the SenderFragment via the MainActivity.
 */
public void showMessage(String message) {
    tvMessage.setText(message);
}
}

Файл макета для ReceiverFragment :

<?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:gravity="center"
    android:orientation="vertical">
<TextView
    android:id="@+id/tvReceivedMessage"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Waiting for message!" />
</LinearLayout>


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow