Почему я хочу избежать нестандартных конструкторов во фрагментах?

173

Я создаю приложение с Fragmentsи в одном из них я создал конструктор не по умолчанию и получил это предупреждение:

Avoid non-default constructors in fragments: use a default constructor plus Fragment#setArguments(Bundle) instead

Может кто-нибудь сказать мне, почему это не очень хорошая идея?

Можете ли вы также предложить, как я мог бы сделать это:

public static class MenuFragment extends ListFragment {
    public ListView listView1;
    Categories category;

    //this is my "non-default" constructor
    public MenuFragment(Categories category){
        this.category = category;
    }....

Без использования конструктора не по умолчанию?

BlackHatSamurai
источник
3
Нет, это не помогает. Они не ответили на мой вопрос. Но, тем не менее, спасибо :)
BlackHatSamurai
31
@BlaineOmega На самом деле именно этот: stackoverflow.com/a/11602478/321697 определенно отвечает на ваш вопрос. При изменении ориентации или другом событии, которое вызывает воссоздание фрагмента, Android использует конструктор по умолчанию, а также пакет, передаваемый в качестве аргумента. Если вы используете пользовательский конструктор, то после повторного создания фрагмента из-за одного из этих событий все, что вы делали в пользовательском конструкторе, теряется.
Кевин Коппок
1
Спасибо, но это отвечает почему, а не как.
BlackHatSamurai
Это покрыто первой и второй ссылками в моем оригинальном комментарии.
CommonsWare

Ответы:

110

Сделайте объект связки и вставьте свои данные (в этом примере ваш Categoryобъект). Будьте осторожны, вы не можете передать этот объект непосредственно в пакет, если он не сериализуем. Я думаю, что лучше построить свой объект во фрагменте и поместить в связку только идентификатор или что-то еще. Это код для создания и прикрепления пакета:

Bundle args = new Bundle();
args.putLong("key", value);
yourFragment.setArguments(args);

После этого в вашем фрагменте получите доступ к данным:

Type value = getArguments().getType("key");

Вот и все.

nistv4n
источник
3
как передать объект? Я хочу передать объект контекста или любой другой объект.
Адиль Малик
12
Пакеты могут переносить сериализованные объекты Java, а также Parcelableобъекты. Кроме того , вы не должны пропускать Context, потому что эта информация может быть доступна через фрагмента getActivity()метода .
Кракатау
Во фрагменте где это сделать Type value = getArguments().getType("key");?
Мухаммед Бабар
4
@ Мухаммед Бабар: Если бы я был тобой, я бы добавил это к newInstance()методу. Например: public static FragmentName newInstance(your variables){}. Как рекомендует документация Android, не создавайте конструктор с параметрами, потому что конструктор по умолчанию (без параметров) будет вызываться автоматически после перезапуска вашего фрагмента.
nistv4n
@MuhammadBabar onCreateView в порядке
chanjianyi
272

Кажется, что ни один из ответов на самом деле не отвечает «зачем использовать пакет для передачи параметров, а не конструкторы по умолчанию»

Причина, по которой вы должны передавать параметры через пакет, заключается в том, что когда система восстанавливает fragment(например, при изменении конфигурации), она автоматически восстанавливает ваши bundle.

Обратные вызовы, такие как onCreateили onCreateViewдолжны читать параметры из bundle- таким образом вы гарантированно восстановите fragmentправильное состояние до того же состояния, с которым оно fragmentбыло инициализировано (обратите внимание, что это состояние может отличаться от того onSaveInstanceState bundle, которое передается в onCreate/onCreateView)

Рекомендация использования статического newInstance()метода - это просто рекомендация. Вы можете использовать конструктор не по умолчанию, но убедитесь, что вы заполняете параметры инициализации bundleвнутри тела этого конструктора. И прочитайте эти параметры в onCreate()или onCreateView()методах.

Numan Salati
источник
2
Хорошо объяснил. Спасибо. Если бы мне задали вопрос, я бы поставил галочку
Кару Бенсон Кару
5
Вы больше не можете использовать конструктор не по умолчанию (по любой причине) .... он выдает ошибку компилятора (раньше это было предупреждением).
MPavlak
51

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

Например:

public static MyFragment newInstance(int title, String message) {
    MyFragment fragment = new MyFragment();
    Bundle bundle = new Bundle(2);
    bundle.putInt(EXTRA_TITLE, title);
    bundle.putString(EXTRA_MESSAGE, message);
    fragment.setArguments(bundle);
    return fragment ;
}

И прочитайте эти аргументы по адресу onCreate:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    title = getArguments().getInt(EXTRA_TITLE);
    message = getArguments().getString(EXTRA_MESSAGE);

    //...
}

Таким образом, если объект отсоединен и повторно присоединен, состояние объекта может быть сохранено с помощью аргументов, подобно тому, как bundlesприсоединено к Intents.

Асаф Пинхасси
источник
9

Если вы используете параметр для какого-то класса. попробуй это

SomeClass mSomeInstance;
public static final MyFragment newInstance(SomeClass someInstance){
    MyFragment f = new MyFragment();
    f.mSomeInstance = someInstance;
    return f;
}
김동기
источник
5
На самом деле это плохое предложение. Как только Fragment будет воссоздан FragmentManager, вы потеряете mSomeInstance.
Ярослав Мыткалык
Согласитесь, SomeClass должен быть доступен для хранения и храниться в пакете с помощью setArguments ()
Jake_
1

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

В своих проектах я использую Kotlin и реализую фрагменты с помощью основного конструктора без аргументов и вторичного конструктора для аргументов, которые просто сохраняют их в пакет и устанавливают в качестве аргументов фрагмента, все работает отлично.

Павлуш
источник
0

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

Акоп Варданян
источник