Najlepsza praktyka tworzenia instancji nowego fragmentu Androida

Widziałem dwie ogólne praktyki tworzenia instancji nowego fragmentu w aplikacji:

Fragment newFragment = new MyFragment();

I

Fragment newFragment = MyFragment.newInstance();

Druga opcja wykorzystuje metodę statyczną newInstance() i ogólnie zawiera następującą metodę.

public static Fragment newInstance() 
{
    MyFragment myFragment = new MyFragment();
    return myFragment;
}

Na początku myślałem, że główną korzyścią jest to, że mogę przeciążyć metodę newInstance (), aby dać elastyczność podczas tworzenia nowych instancji fragmentu - ale mogłem to zrobić również, tworząc przeciążony konstruktor dla fragmentu.

Coś mnie ominęło?

Jakie są korzyści z jednego podejścia nad drugim? Czy to tylko dobra praktyka?

Author: AnV, 2012-02-12

14 answers

Jeśli Android zdecyduje się odtworzyć Twój Fragment później, wywoła bezdyskusyjny konstruktor twojego fragmentu. Przeciążenie konstruktora nie jest więc rozwiązaniem.

Mając to na uwadze, sposobem przekazywania rzeczy do fragmentu, aby były dostępne po odtworzeniu fragmentu przez Androida, jest przekazanie pakietu do metody setArguments.

Więc, na przykład, gdybyśmy chcieli przekazać liczbę całkowitą do fragmentu, użylibyśmy czegoś w stylu:

public static MyFragment newInstance(int someInt) {
    MyFragment myFragment = new MyFragment();

    Bundle args = new Bundle();
    args.putInt("someInt", someInt);
    myFragment.setArguments(args);

    return myFragment;
}

I później w Fragment onCreate() możesz uzyskać dostęp do tej liczby całkowitej, używając:

getArguments().getInt("someInt", 0);

Ten pakiet będzie dostępny, nawet jeśli Fragment zostanie w jakiś sposób odtworzony przez Androida.

Uwaga: setArguments można wywołać tylko przed dołączeniem fragmentu do aktywności.

[[5]} to podejście jest również udokumentowane w Android Developer reference: https://developer.android.com/reference/android/app/Fragment.html
 1186
Author: yydl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-14 10:54:21

Jedyne korzyści z używania newInstance(), które widzę są następujące:

  1. Będziesz miał jedno miejsce, gdzie wszystkie argumenty użyte przez fragment mogą być połączone i nie musisz pisać poniższego kodu za każdym razem, gdy tworzysz instancję fragmentu.

    Bundle args = new Bundle();
    args.putInt("someInt", someInt);
    args.putString("someString", someString);
    // Put any other arguments
    myFragment.setArguments(args);
    
  2. Jest to dobry sposób, aby powiedzieć innym klasom, jakie argumenty oczekuje, że będą działać wiernie(chociaż powinieneś być w stanie obsłużyć przypadki, jeśli nie ma argumentów w pakiecie we fragmencie instancja).

Moim zdaniem używanie statycznego newInstance() do tworzenia instancji fragmentu jest dobrą praktyką.

 97
Author: 500865,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-19 12:36:05

Jest też inny sposób:

Fragment.instantiate(context, MyFragment.class.getName(), myBundle)
 63
Author: drlue,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-09 14:33:49

Podczas gdy @yydl podaje przekonujący powód, dlaczego metoda newInstance jest lepsza:

Jeśli Android zdecyduje się odtworzyć Twój Fragment później, zadzwoni bezdyskusyjny konstruktor twojego fragmentu. Więc przeciążenie konstruktor nie jest rozwiązaniem.

Wciąż możliwe jest użycie konstruktora . Aby zobaczyć, dlaczego tak jest, najpierw musimy zobaczyć, dlaczego powyższe obejście jest używane przez Androida.

Zanim fragment może być użyty, instancja jest potrzebne. Android wywołuje YourFragment() (konstruktor bez argumentów ), aby skonstruować instancję fragmentu. Tutaj każdy przeciążony konstruktor, który napiszesz, zostanie zignorowany, ponieważ Android nie może wiedzieć, którego użyć.

W czasie trwania aktywności fragment zostaje utworzony jak wyżej i wielokrotnie niszczony przez Androida. Oznacza to, że jeśli umieścisz dane w samym obiekcie fragment, zostaną one utracone po zniszczeniu fragmentu.

Aby obejść, android prosi, aby przechowuj dane za pomocą Bundle (wywołanie setArguments()), do którego można uzyskać dostęp z YourFragment. Argumenty bundle S są chronione przez system Android, a zatem gwarantowane są trwałe.

Jednym ze sposobów ustawienia tego pakietu jest użycie statycznej metody newInstance:

public static YourFragment newInstance (int data) {
    YourFragment yf = new YourFragment()
    /* See this code gets executed immediately on your object construction */
    Bundle args = new Bundle();
    args.putInt("data", data);
    yf.setArguments(args);
    return yf;
}

Jednak konstruktor:

public YourFragment(int data) {
    Bundle args = new Bundle();
    args.putInt("data", data);
    setArguments(args);
}

Może zrobić dokładnie to samo, co metoda newInstance.

Oczywiście, to nie powiedzie się i jest jednym z powodów, dla których Android chce, abyś używał]} "metoda": {]}
public YourFragment(int data) {
    this.data = data; // Don't do this
}

Jako dalsze wyjaśnienie, oto Klasa fragmentu Androida:

/**
 * Supply the construction arguments for this fragment.  This can only
 * be called before the fragment has been attached to its activity; that
 * is, you should call it immediately after constructing the fragment.  The
 * arguments supplied here will be retained across fragment destroy and
 * creation.
 */
public void setArguments(Bundle args) {
    if (mIndex >= 0) {
        throw new IllegalStateException("Fragment already active");
    }
    mArguments = args;
}

Zauważ, że Android prosi o ustawienie argumentów tylko przy budowie i gwarantuje, że zostaną one zachowane.

EDIT: Jak wspomniano w komentarzach przez @JHH, jeśli dostarczasz własny konstruktor, który wymaga pewnych argumentów, to Java nie dostarczy twojego fragmentu z domyślnym konstruktorem no arg. Wymagałoby to więc zdefiniowania No ARG constructor, czyli kod, którego można uniknąć za pomocą metody fabrycznej newInstance.

EDIT : Android nie pozwala na używanie przeciążonego konstruktora dla fragmentów. Należy użyć metody newInstance.

 50
Author: xyz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-11-16 16:35:04

Some kotlin kod:

companion object {
    fun newInstance(first: String, second: String) : SampleFragment {
        return SampleFragment().apply {
            arguments = Bundle().apply {
                putString("firstString", first)
                putString("secondString", second)
            }
        }
    }
}

I możesz uzyskać argumenty za pomocą tego:

val first: String by lazy { arguments?.getString("firstString") ?: "default"}
val second: String by lazy { arguments?.getString("secondString") ?: "default"}
 25
Author: Rafols,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-06 08:23:49

I nie zgadzam się z yydi odpowiedź mówiąc:

Jeśli Android zdecyduje się odtworzyć Twój Fragment później, zadzwoni bezdyskusyjny konstruktor twojego fragmentu. Więc przeciążenie konstruktor nie jest rozwiązaniem.

Myślę, że jest to rozwiązanie i dobre, właśnie dlatego zostało opracowane przez Java core language.

To prawda, że system Android może zniszczyć i odtworzyć twoje Fragment. Więc możesz to zrobić to:
public MyFragment() {
//  An empty constructor for Android System to use, otherwise exception may occur.
}

public MyFragment(int someInt) {
    Bundle args = new Bundle();
    args.putInt("someInt", someInt);
    setArguments(args);
}

Pozwoli Ci wyciągnąć someInt z getArguments(), nawet jeśli Fragment został odtworzony przez system. Jest to bardziej eleganckie rozwiązanie niż konstruktor static.

Moim zdaniem static konstruktory są bezużyteczne i nie powinny być używane. Również będą cię ograniczać, jeśli w przyszłości będziesz chciał rozszerzyć to Fragment i dodać więcej funkcjonalności do konstruktora. Z konstruktorem static nie możesz tego zrobić.

Update:

[[9]}Android dodał inspekcję oznacza to, że wszystkie konstruktory inne niż domyślne zawierają błąd.
Polecam go wyłączyć, z powodów wymienionych powyżej.
 21
Author: Ilya Gazman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-25 07:05:17

Najlepszą praktyką instancji fragmentów z argumentami w Androidzie jest posiadanie statycznej metody fabrycznej w swoim fragmencie.

public static MyFragment newInstance(String name, int age) {
    Bundle bundle = new Bundle();
    bundle.putString("name", name);
    bundle.putInt("age", age);

    MyFragment fragment = new MyFragment();
    fragment.setArguments(bundle);

    return fragment;
}

Należy unikać ustawiania pól z instancją fragmentu. Ponieważ ilekroć system Android odtworzy Twój fragment, jeśli uzna, że system potrzebuje więcej pamięci, niż odtworzy Twój fragment za pomocą konstruktora bez argumentów.

Możesz znaleźć więcej informacji o najlepszych praktykach tworzenia instancji fragmentów z argumentami proszę.

 3
Author: Gunhan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-01 18:03:44

Ponieważ pytania dotyczące najlepszych praktyk, dodam, że bardzo często dobrym pomysłem jest użycie podejścia hybrydowego do tworzenia fragmentów podczas pracy z niektórymi usługami sieciowymi REST

Nie możemy przekazać złożonych obiektów, na przykład jakiegoś modelu użytkownika, w przypadku wyświetlenia fragmentu użytkownika

Ale co możemy zrobić, to sprawdzić onCreate tego użytkownika!=null, a jeśli nie-to przynieś go z warstwy danych, w przeciwnym razie-użyj existing.

W ten sposób zyskujemy zarówno możliwość odtworzenia przez userId w przypadku fragment recreation przez Androida i snappiness dla działań użytkownika, a także możliwość tworzenia fragmentów przez trzymanie się obiektu lub tylko jego id

Coś takiego:

public class UserFragment extends Fragment {
    public final static String USER_ID="user_id";
    private User user;
    private long userId;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        userId = getArguments().getLong(USER_ID);
        if(user==null){
            //
            // Recreating here user from user id(i.e requesting from your data model,
            // which could be services, direct request to rest, or data layer sitting
            // on application model
            //
             user = bringUser();
        }
    }

    public static UserFragment newInstance(User user, long user_id){
        UserFragment userFragment = new UserFragment();
        Bundle args = new Bundle();
        args.putLong(USER_ID,user_id);
        if(user!=null){
            userFragment.user=user;
        }
        userFragment.setArguments(args);
        return userFragment;

    }

    public static UserFragment newInstance(long user_id){
        return newInstance(null,user_id);
    }

    public static UserFragment newInstance(User user){
        return newInstance(user,user.id);
    }
}
 2
Author: Tigra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-04 13:29:47

Użyj tego kodu w 100% rozwiąż swój problem

Wpisz ten kod w firstFragment

public static yourNameParentFragment newInstance() {

    Bundle args = new Bundle();
    args.putBoolean("yourKey",yourValue);
    YourFragment fragment = new YourFragment();
    fragment.setArguments(args);
    return fragment;
}

Ta próbka wysyła dane logiczne

I w SecendFragment

yourNameParentFragment name =yourNameParentFragment.newInstance();
   Bundle bundle;
   bundle=sellDiamondFragments2.getArguments();
  boolean a= bundle.getBoolean("yourKey");

Wartość Must w pierwszym fragmencie jest statyczna

Happy code

 1
Author: Amin Emadi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-11-09 13:55:55

Jestem tu ostatnio. Ale wiem coś, co może Ci trochę pomóc.

Jeśli używasz Javy, nie ma nic do zmiany. Ale dla programistów Kotlina, oto następujący fragment, który może sprawić, że będziesz mógł uruchomić: {]}

  • fragment nadrzędny:
inline fun <reified T : SampleFragment> newInstance(text: String): T {
    return T::class.java.newInstance().apply {
        arguments = Bundle().also { it.putString("key_text_arg", text) }
    }
}
  • normalne wywołanie
val f: SampleFragment = SampleFragment.newInstance("ABC")
// or val f = SampleFragment.newInstance<SampleFragment>("ABC")
  • możesz rozszerzyć operację INIT rodzica w klasie fragment potomka o:
fun newInstance(): ChildSampleFragment {
    val child = UserProfileFragment.newInstance<ChildSampleFragment>("XYZ")
    // Do anything with the current initialized args bundle here
    // with child.arguments = ....
    return child
}

Szczęśliwego kodowania.
 1
Author: vhfree,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-02-17 04:56:07

Najlepszym sposobem utworzenia instancji fragmentu jest użycie domyślnego fragmentu .instantiate method lub create factory method to instantiate the fragment
Uwaga: zawsze Utwórz jeden pusty konstruktor we fragmencie Inne podczas przywracania pamięci fragment wyrzuci wyjątek run-time.

 0
Author: Mahesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-30 09:34:45

Możesz użyć smth w następujący sposób:

val fragment = supportFragmentManager.fragmentFactory.instantiate(classLoader, YourFragment::class.java.name)

Ponieważ ta odpowiedź jest teraz przestarzała

 0
Author: Morozov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-01 15:56:22

setArguments() jest bezużyteczny. To tylko bałagan.

public class MyFragment extends Fragment {

    public String mTitle;
    public String mInitialTitle;

    public static MyFragment newInstance(String param1) {
        MyFragment f = new MyFragment();
        f.mInitialTitle = param1;
        f.mTitle = param1;
        return f;
    }

    @Override
    public void onSaveInstanceState(Bundle state) {
        state.putString("mInitialTitle", mInitialTitle);
        state.putString("mTitle", mTitle);
        super.onSaveInstanceState(state);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle state) {
        if (state != null) {
            mInitialTitle = state.getString("mInitialTitle");
            mTitle = state.getString("mTitle");
        } 
        ...
    }
}
 -2
Author: Vadim Star,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-10 16:06:37

Wydaje mi się, że mam na to dużo prostsze rozwiązanie.

public class MyFragment extends Fragment{

   private String mTitle;
   private List<MyObject> mObjects;

   public static MyFragment newInstance(String title, List<MyObject> objects)
   MyFragment myFrag = new MyFragment();
   myFrag.mTitle = title;
   myFrag.mObjects = objects;
   return myFrag;
   }
 -12
Author: Stefan Bogaard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-29 13:30:21