web-dev-qa-db-ja.com

Android-フラグメント状態の保存/復元

いくつかのフラグメントを処理するアクティビティがあります。すべてのフラグメントには、いくつかのビューがあります(EditText, ListView, Mapなど)。

その時点で表示されているフラグメントのインスタンスを保存するにはどうすればよいですか?アクティビティがonPause() --> onResume()のときに動作する必要があります。また、別のフラグメント(バックスタックからポップ)から戻るときに機能する必要があります。

メインActivityから最初のフラグメントを呼び出し、次にフラグメントから次のフラグメントを呼び出します。

私の活動のコード:

public class Activity_Main extends FragmentActivity{

public static Fragment_1 fragment_1;
public static Fragment_2 fragment_2;
public static Fragment_3 fragment_3;
public static FragmentManager fragmentManager;

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

     fragment_1 = new Fragment_1();

     fragment_2 = new Fragment_2();

     fragment_3 = new Fragment_3();

     fragmentManager = getSupportFragmentManager();
     FragmentTransaction transaction_1 = fragmentManager.beginTransaction();
     transaction_1.replace(R.id.content_frame, fragment_1);
     transaction_1.commit();
}}

次に、フラグメントの1つのコードを示します。

public class Fragment_1 extends Fragment {

      private EditText title;
      private Button go_next;


      @Override
      public View onCreateView(final LayoutInflater inflater,
        ViewGroup container, Bundle savedInstanceState) {

            View rootView = inflater.inflate(R.layout.fragment_1,
            container, false);

            title = (EditText) rootView.findViewById(R.id.title);

            go_next = (Button) rootView.findViewById(R.id.go_next);

            image.setOnClickListener(new View.OnClickListener() {

         @Override
         public void onClick(View v) {

                 FragmentTransaction transaction_2 = Activity_Main.fragmentManager
                .beginTransaction();

                 transaction_2.replace(R.id.content_frame,
                  Activity_Main.fragment_2);
                 transaction_2.addToBackStack(null);
                 transaction_2.commit();  

            });
        }}

私は多くの情報を検索しましたが、明確なものはありません。誰かが明確な解決策と例を教えてもらえますか?

43
Stanete

フラグメントがバックスタックに移動されても、破壊されません。すべてのインスタンス変数がそこに残ります。これがデータを保存する場所です。 onActivityCreatedで、次の条件を確認します。

  1. バンドルは!= nullですか?はいの場合、そこにデータが保存されます(おそらく向きの変更)。
  2. インスタンス変数に保存されたデータはありますか?はいの場合、それらから状態を復元します(または、何もしない可能性があります。すべてが正常であるためです)。
  3. それ以外の場合、フラグメントが初めて表示され、すべてを新しく作成します。

編集:ここに例があります

public class ExampleFragment extends Fragment {
    private List<String> myData;

    @Override
    public void onSaveInstanceState(final Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putSerializable("list", (Serializable) myData);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        if (savedInstanceState != null) {
            //probably orientation change
            myData = (List<String>) savedInstanceState.getSerializable("list");
        } else {
            if (myData != null) {
                //returning from backstack, data is fine, do nothing
            } else {
                //newly created, compute data
                myData = computeData();
            }
        }
    }
}
81
Kirill Rakhman

Androidフラグメントには、いくつかの利点と欠点があります。フラグメントの最も不利な点は、フラグメントを使用するときにフラグメントを作成することです。使用すると、フラグメントのonCreateViewが毎回呼び出されます。フラグメント内のコンポーネントの状態を保持する場合は、フラグメントの状態を保存する必要があり、次に示すようにその状態をロードする必要があります。これにより、フラグメントビューが少し遅くなり、奇妙になります。

私は解決策を見つけ、この解決策を使用しました。「すべてが素晴らしい。すべての人が試すことができます」。

初めてonCreateViewを実行するときは、グローバル変数としてビューを作成します。このフラグメントを2回目に呼び出すと、onCreateViewが再び呼び出され、このグローバルビューを返すことができます。フラグメントコンポーネントの状態は保持されます。

View view;

@Override
public View onCreateView(LayoutInflater inflater,
        @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    setActionBar(null);
    if (view != null) {
        if ((ViewGroup)view.getParent() != null)
            ((ViewGroup)view.getParent()).removeView(view);
        return view; 
    }
    view = inflater.inflate(R.layout.mylayout, container, false);
}
16
nebyan

これを試して :

@Override
protected void onPause() {
    super.onPause();
    if (getSupportFragmentManager().findFragmentByTag("MyFragment") != null)
        getSupportFragmentManager().findFragmentByTag("MyFragment").setRetainInstance(true);
}

@Override
protected void onResume() {
    super.onResume();
    if (getSupportFragmentManager().findFragmentByTag("MyFragment") != null)
        getSupportFragmentManager().findFragmentByTag("MyFragment").getRetainInstance();
}

これが役立つことを願っています。

また、これをmenifestファイルのアクティビティタグに書き込むことができます:

  Android:configChanges="orientation|screenSize"

幸運を !!!

7
Sar

フラグメントの状態を保存するには、onSaveInstanceState()を実装する必要があります:「アクティビティのように、アクティビティのプロセスが強制終了され、フラグメントの状態を復元する必要がある場合、Bundleを使用してフラグメントの状態を保持できますアクティビティの再作成時に、フラグメントのonSaveInstanceState()コールバック中に状態を保存し、onCreate()onCreateView()、またはonActivityCreated()のいずれかで復元できます。 、アクティビティドキュメントをご覧ください。」

http://developer.Android.com/guide/components/fragments.html#Lifecycle

4
Raanan

ここで述べたように: Fragment#setRetainInstance(boolean)を使用する理由

次のようにフラグメントメソッドsetRetainInstance(true)を使用することもできます。

public class MyFragment extends Fragment {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // keep the fragment and all its data across screen rotation
        setRetainInstance(true);

    }
}
4
Richard R

FragmentManagerから現在のフラグメントを取得できます。そして、フラグメントマネージャにそれらのどれもない場合は、Fragment_1を作成できます

public class MainActivity extends FragmentActivity {


    public static Fragment_1 fragment_1;
    public static Fragment_2 fragment_2;
    public static Fragment_3 fragment_3;
    public static FragmentManager fragmentManager;


    @Override
    protected void onCreate(Bundle arg0) {
        super.onCreate(arg0);
        setContentView(R.layout.main);

        fragment_1 = (Fragment_1) fragmentManager.findFragmentByTag("fragment1");

        fragment_2  =(Fragment_2) fragmentManager.findFragmentByTag("fragment2");

        fragment_3 = (Fragment_3) fragmentManager.findFragmentByTag("fragment3");


        if(fragment_1==null && fragment_2==null && fragment_3==null){           
            fragment_1 = new Fragment_1();          
            fragmentManager.beginTransaction().replace(R.id.content_frame, fragment_1, "fragment1").commit();
        }


    }


}

また、setRetainInstanceを使用して、フラグメント内のonDestroy()メソッドを無視し、アプリケーションがバックグラウンドになり、アプリケーションを強制終了して、必要なすべてのデータを保存するために必要なメモリを割り当てますonSaveInstanceStateバンドル

public class Fragment_1 extends Fragment {


    private EditText title;
    private Button go_next;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true); //Will ignore onDestroy Method (Nested Fragments no need this if parent have it)
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        onRestoreInstanceStae(savedInstanceState);
        return super.onCreateView(inflater, container, savedInstanceState);
    }


    //Here you can restore saved data in onSaveInstanceState Bundle
    private void onRestoreInstanceState(Bundle savedInstanceState){
        if(savedInstanceState!=null){
            String SomeText = savedInstanceState.getString("title");            
        }
    }

    //Here you Save your data
    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString("title", "Some Text");
    }

}
0

この質問がまだあなたを悩ませているのかどうかは、数ヶ月前からわかりません。しかし、私はこれに対処した方法を共有したいと思います。ソースコードは次のとおりです。

int FLAG = 0;
private View rootView;
private LinearLayout parentView;

/**
 * The fragment argument representing the section number for this fragment.
 */
private static final String ARG_SECTION_NUMBER = "section_number";

/**
 * Returns a new instance of this fragment for the given section number.
 */
public static Fragment2 newInstance(Bundle bundle) {
    Fragment2 fragment = new Fragment2();
    Bundle args = bundle;
    fragment.setArguments(args);
    return fragment;
}

public Fragment2() {

}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    super.onCreateView(inflater, container, savedInstanceState);
    Log.e("onCreateView","onCreateView");
    if(FLAG!=12321){
        rootView = inflater.inflate(R.layout.fragment_create_new_album, container, false);
        changeFLAG(12321);
    }       
    parentView=new LinearLayout(getActivity());
    parentView.addView(rootView);

    return parentView;
}

/* (non-Javadoc)
 * @see Android.support.v4.app.Fragment#onDestroy()
 */
@Override
public void onDestroy() {
    // TODO Auto-generated method stub
    super.onDestroy();
    Log.e("onDestroy","onDestroy");
}

/* (non-Javadoc)
 * @see Android.support.v4.app.Fragment#onStart()
 */
@Override
public void onStart() {
    // TODO Auto-generated method stub
    super.onStart();
    Log.e("onstart","onstart");
}

/* (non-Javadoc)
 * @see Android.support.v4.app.Fragment#onStop()
 */
@Override
public void onStop() {
    // TODO Auto-generated method stub
    super.onStop();
    if(false){
        Bundle savedInstance=getArguments();
        LinearLayout viewParent;

        viewParent= (LinearLayout) rootView.getParent();
        viewParent.removeView(rootView);

    }
    parentView.removeView(rootView);

    Log.e("onStop","onstop");
}
@Override
public void onPause() {
    super.onPause();
    Log.e("onpause","onpause");
}

@Override
public void onResume() {
    super.onResume();
    Log.e("onResume","onResume");
}

そして、これがMainActivityです:

/**
 * Fragment managing the behaviors, interactions and presentation of the
 * navigation drawer.
 */
private NavigationDrawerFragment mNavigationDrawerFragment;

/**
 * Used to store the last screen title. For use in
 * {@link #restoreActionBar()}.
 */

public static boolean fragment2InstanceExists=false;
public static Fragment2 fragment2=null;

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

    mNavigationDrawerFragment = (NavigationDrawerFragment) getSupportFragmentManager()
            .findFragmentById(R.id.navigation_drawer);
    mTitle = getTitle();

    // Set up the drawer.
    mNavigationDrawerFragment.setUp(R.id.navigation_drawer,
            (DrawerLayout) findViewById(R.id.drawer_layout));
}

@Override
public void onNavigationDrawerItemSelected(int position) {
    // update the main content by replacing fragments
    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction fragmentTransaction=fragmentManager.beginTransaction();
    switch(position){
    case 0:
        fragmentTransaction.addToBackStack(null);
        fragmentTransaction.replace(R.id.container, Fragment1.newInstance(position+1)).commit();
        break;
    case 1:

        Bundle bundle=new Bundle();
        bundle.putInt("source_of_create",CommonMethods.CREATE_FROM_ACTIVITY);

        if(!fragment2InstanceExists){
            fragment2=Fragment2.newInstance(bundle);
            fragment2InstanceExists=true;
        }
        fragmentTransaction.addToBackStack(null);
        fragmentTransaction.replace(R.id.container, fragment2).commit();

        break;
    case 2:
        fragmentTransaction.addToBackStack(null);
        fragmentTransaction.replace(R.id.container, FolderExplorerFragment.newInstance(position+1)).commit();
        break;
    default: 
        break;
    }
}

parentViewはキーポイントです。通常、onCreateViewの場合、return rootViewを使用します。しかし今、rootViewをparentViewに追加してから、parentViewを返します。 「指定された子にはすでに親があります。..でremoveView()を呼び出す必要があります」というエラーを防ぐには、parentView.removeView(rootView)を呼び出す必要があります。また、私がそれを見つけた方法を共有したいと思います。まず、インスタンスが存在するかどうかを示すブール値を設定します。インスタンスが存在する場合、rootViewは再び膨張しません。しかし、その後、logcatは子にすでに親の物があるので、中間の親ビューとして別の親を使用することにしました。それが動作する方法です。

お役に立てば幸いです。

0
butch