web-dev-qa-db-ja.com

新しいAndroidフラグメントをインスタンス化するためのベストプラクティス

アプリケーションで新しいフラグメントをインスタンス化するための2つの一般的な方法を見ました。

Fragment newFragment = new MyFragment();

そして

Fragment newFragment = MyFragment.newInstance();

2番目のオプションは静的メソッドnewInstance()を使用し、 一般に は以下のメソッドを含みます。

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

最初は、主な利点は、Fragmentの新しいインスタンスを作成するときにnewInstance()メソッドをオーバーロードして柔軟性を持たせることができるという事実だと思いました。

私は何か見落としてますか?

1つのアプローチが他のアプローチより優れている点は何ですか?それともただの良い習慣ですか?

645
Graham Smith

Androidが後でフラグメントを再作成することにした場合は、フラグメントの引数のないコンストラクタを呼び出します。だから、コンストラクタをオーバーロードすることは解決策ではありません。

そうは言っても、FragmentがAndroidによって再作成された後にそれらが利用可能になるようにあなたのFragmentにものを渡す方法はsetArgumentsメソッドにバンドルを渡すことです。

したがって、たとえば、フラグメントに整数を渡したい場合は、次のようにします。

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

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

    return myFragment;
}

そして後でフラグメントonCreate()であなたはその整数にアクセスすることができます:

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

このバンドルは、フラグメントが何らかの形でAndroidによって再作成された場合でも使用可能になります。

また、注意してください。setArgumentsは、フラグメントがアクティビティに添付される前にのみ呼び出すことができます。

このアプローチは、Android開発者向けリファレンスにも記載されています。 https://developer.Android.com/reference/Android/app/Fragment.html

1045
yydl

私が見るnewInstance()を使うことの唯一の利点は以下の通りです:

  1. フラグメントで使用されるすべての引数をまとめることができる場所は1つになります。フラグメントをインスタンス化するたびに以下のコードを記述する必要はありません。

    Bundle args = new Bundle();
    args.putInt("someInt", someInt);
    args.putString("someString", someString);
    // Put any other arguments
    myFragment.setArguments(args);
    
  2. 他のクラスにが期待する引数が忠実に機能することを伝えるための良い方法です(ただし、引数がフラグメントインスタンスにバンドルされていない場合は処理できるはずです)。

ですから、フラグメントをインスタンス化するために静的なnewInstance()を使うのは良い習慣です。

94
500865

別の方法もあります:

Fragment.instantiate(context, MyFragment.class.getName(), myBundle)
61
user1145201

@yydlがnewInstanceメソッドが優れている理由について説得力のある理由を示していますが:

Androidが後でフラグメントを再作成することにした場合は、フラグメントの引数のないコンストラクタを呼び出します。だから、コンストラクタをオーバーロードすることは解決策ではありません。

コンストラクタ を使うことはまだかなり可能です。これがなぜであるか見るために、最初に私達はなぜ上記の回避策がアンドロイドによって使用されるか見る必要があります。

フラグメントを使用する前に、インスタンスが必要です。 Androidはフラグメントのインスタンスを構築するためにYourFragment() 引数なし constructor)を呼び出します。ここであなたが書いたオーバーロードされたコンストラクタは無視されます。Androidはどちらを使うべきかわからないからです。

アクティビティの存続期間中に、フラグメントは上記のように作成され、Androidによって複数回破壊されます。つまり、フラグメントオブジェクト自体にデータを入れると、フラグメントが破棄されるとデータは失われます。

回避策として、AndroidはBundlesetArguments()を呼び出す)を使用してデータを保存するよう要求します。これはYourFragmentからアクセスできます。引数bundlesはAndroidによって保護されているので、 persistent であることが保証されています。

このバンドルを設定する1つの方法は、静的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;
}

しかし、コンストラクタ:

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

newInstanceメソッドとまったく同じことができます。

当然、これは失敗し、AndroidがnewInstanceメソッドを使用することをAndroidが望んでいる理由の1つです。

public YourFragment(int data) {
    this.data = data; // Don't do this
}

さらなる説明として、これはAndroidのFragment Classです。

/**
 * 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;
}

Androidは、引数は構築時に only に設定するよう要求し、これらが保持されることを保証することに注意してください。

_ edit _ :@JHHによるコメントで指摘されているように、引数を必要とするカスタムコンストラクタを提供している場合、Javaは no arg defaultコンストラクタでフラグメントを提供しません。そのため、これには no arg コンストラクタを定義する必要があります。これはnewInstanceファクトリメソッドで回避できるコードです。

_ edit _ :Androidでは、フラグメントにオーバーロードされたコンストラクタを使用できなくなりました。 newInstanceメソッドを使う必要があります。

39
ps95

I 反対 yydi付き 回答

Androidが後でフラグメントを再作成することにした場合は、フラグメントの引数のないコンストラクタを呼び出します。だから、コンストラクタをオーバーロードすることは解決策ではありません。

私はそれが解決策であり、良い解決策だと思います。これがまさにJavaコア言語によって開発された理由です。

AndroidシステムがあなたのFragmentを破壊して再作成する可能性があるのは事実です。だからあなたはこれを行うことができます:

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);
}

たとえsomeIntがシステムによって再作成されたとしても、後者からgetArguments()からFragmentを引き出すことができます。これはstaticコンストラクタよりもエレガントな解決策です。

私の意見ではstaticのコンストラクタは無用であり使用すべきではありません。将来的にこのFragmentを拡張してコンストラクタにさらに機能を追加したい場合にも、それらはあなたを制限するでしょう。 staticコンストラクタではこれができません。

更新:

Androidは、デフォルト以外のすべてのコンストラクタにエラーのフラグを立てる検査を追加しました。
上記の理由から、無効にすることをお勧めします。

16
Ilya Gazman

いくつか コトリン code:

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

そしてこれで引数を得ることができます:

val first: String by lazy { arguments?.getString("firstString") ?: "default"}
val second: String by lazy { arguments?.getString("secondString") ?: "default"}
11
Rafols

Androidで引数を使用してフラグメントをインスタンス化するためのベストプラクティスは、フラグメントに静的ファクトリメソッドを含めることです。

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;
}

あなたはフラグメントのインスタンスであなたのフィールドを設定することを避けるべきです。なぜなら、Androidシステムがあなたのフラグメントを再作成するときはいつでも、システムがより多くのメモリを必要としていると感じた場合、それは引数なしでコンストラクタを使用することによってあなたのフラグメントを再作成します。

についてのより多くの情報を見つけることができます - ここで引数 でフラグメントをインスタンス化するためのベストプラクティス。

3
Gunhan

ベストプラクティスに関する質問なので、REST Webサービスを操作するときにフラグメントを作成するためにハイブリッドアプローチを使用することをお勧めします。

ユーザーフラグメントを表示する場合のように、複雑なオブジェクト、たとえばユーザーモデルなどを渡すことはできません。

しかし、私たちにできることは、onCreateにそのユーザー!= nullをチェックインし、そうでなければ - それからデータ層から彼を持ってくる、そうでなければ - existingを使用することです。

これにより、Androidでフラグメントを再作成した場合にuserIdで再作成する機能とユーザーの操作をすばやく行えるようにするだけでなく、オブジェクト自体を保持することによってフラグメントを作成することも、idだけを取得することもできます。

このようなもの:

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
Tigra

フラグメントをインスタンス化する最善の方法は、default Fragment.instantiate methodを使用するか、フラグメントをインスタンス化するファクトリメソッドを作成することです。
注意:フラグメントメモリを復元すると実行時例外がスローされますが、常にfragment otherに1つの空のコンストラクタを作成してください。

0
Mahesh

setArguments()は役に立ちません。それは混乱をもたらすだけです。

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");
        } 
        ...
    }
}
0
Vadim Star