片段setRetainInstance(true)保存视图属性

时间:2014-07-18 17:45:38

标签: android android-fragments android-view

我对保存Android中碎片内部视图状态的最佳做法感到困惑。

我认为setRetainInstance(true)可以做到这一点;但是,虽然这确实保留了View中声明的旧全局Fragment引用,但在扩展布局时不会使用这些视图。因此,我必须手动将属性从旧的全局视图引用转移到膨胀布局中的新视图。请参阅下面的代码,了解我是如何做到的。

public static class MyFragment extends Fragment {

    // I need to save the state of two views in the fragment
    ProgressBar mProgress;
    TextView mText;


    @Override public void onCreate(Bundle savedInstanceState) {
        // savedInstanceState == null if setRetainInstance == true
        super.onCreate(savedInstanceState);

        // Retain the instance between configuration changes
        setRetainInstance(true);
    }

    @Override public View onCreateView(LayoutInflater i, ViewGroup c, Bundle b) {
        // Inflate xml layout for fragement (orientation dependent)
        View v = i.inflate(R.layout.fragment_main, c, false);

        // Grab Views from layout
        ProgressBar tmpProgress = (ProgressBar) v.findViewById(R.id.progress);
        TextView tmpText        = (TextView) v.findViewById(R.id.text);

        // If View References exist, transfer state from previous Views
        if(mProgress != null){ 
            tmpProgress.setProgress(mProgress.getProgress());
            tmpText.setText(mText.getText());
        }

        // Replace View references
        mProgress = tmpProgress;
        mText     = tmpText;
        return v;
    }
}

我觉得setRetainInstanceState(true)应主要用于保持片段处于活动状态,以便后台进程可以保持与它的连接。如果是这种情况,我应该不使用这种方法来保留View的状态吗?

更具体地说:

  1. 如果setRetainInstanceStatetrue,则上述代码是在定向调用之间保留View状态的最佳方式吗?
  2. 如果我没有与后台任务交互,我应该使用setRetainInstanceState(false)并使用Bundle来维护视图状态吗? (注意:bundles cannot be used if setRetainInstance == true

2 个答案:

答案 0 :(得分:4)

使用setRetainInstance请勿将您的视图保留在内存中,只需根据您为片段提供的Singletontag保持同一实例ID。这很好,因为当你Fragment进入后台时,你的视图会被破坏,而不是不必要的记忆。

回答你的两个问题:

  1. 不要保持您的视图创建!每次调用onCreateView时重新创建视图。保持状态和更多使用全局变量。

  2. 不,一旦您将其设置为Fragment,Android操作系统将为相同的标记或ID保留相同的实例,因此当它转到后台时请记住将Bundle保存为视图状态在onSaveInstanceState,以便在下次恢复查看状态。

  3. 在此处详细了解Fragments生命周期:http://developer.android.com/guide/components/fragments.html#Lifecycle

    关于onSaveInstanceState,请点击此处了解详情:http://developer.android.com/reference/android/app/Fragment.html#onSaveInstanceState(android.os.Bundle)

    例如,您可以这样做:

    package com.example.stackoverflowsandbox;
    
    import android.app.Fragment;
    import android.os.Bundle;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    
    public class MyFragment extends Fragment {
        public static String VIEW_STATE_A = "a";
        public static String VIEW_STATE_B = "b";
        public static String VIEW_STATE_C = "c";
    
        private String       currentState = MyFragment.VIEW_STATE_A;
        private View         view;
    
        public void changeStateTo( final String newState ) {
            if ( this.currentState != newState ) {
                this.currentState = newState;
    
                this.updateViewState( this.currentState );
            }
        }
    
        @Override
        public View onCreateView( final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState ) {
            this.setRetainInstance( true );
    
            this.view = inflater.inflate( R.layout.my_view, null );
    
            this.updateViewState( this.currentState );
    
            return this.view;
        }
    
        @Override
        public void onDestroyView() {
            this.view = null; // release the reference to GC
    
            super.onDestroyView();
        }
    
        private void updateViewState( final String state ) {
            // do what you want to do with your view here...
        }
    }
    

    将您的fragment州改为changeStateTo

答案 1 :(得分:-1)

以下是她选择setRetainInstance的一些很好的理由:Handling Configuration Changes with Fragments

public class SomeFragment extends Fragment {

    /**
    * This method will only be called once when the retained
    * Fragment is first created.
    */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Retain this fragment across configuration changes.
        setRetainInstance(true);
    }
}

然后在您的父Activity中,在实例化新实例之前检查Fragment的现有实例。 如果由于配置更改而再次调用活动onCreate(Bundle),则FragmentManager将包含Fragment的现有实例。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_priceindex);
    // create a new Fragment only if we can't find an existing one
    if (getSupportFragmentManager.findFragmentById(R.id.containerId) == null){
       getSupportFragmentManager.beginTransaction().add(R.id.containerId, 
       new SomeFragment()).commit();
    }
}

<强>编辑:

对于对暴露问题的不理解感到抱歉,但我相信即使没有与正在运行的任务的交互,仍有充分的理由 不要重新创建视图。

  

让我们假设一个典型的场景 - 期间的昂贵操作   活动创建(onCreate(Bundle)),同时显示一个   在您的应用程序正在进行繁重的工作时,屏幕会被丢失。你没有   但是,处理屏幕旋转。这将导致以下结果   用户体验:您的用户加载应用,看到加载屏幕,   旋转设备,再次看到相同的加载屏幕,旋转它   再次,看到另一个加载屏幕......好吧,你得到了图片。哦,   等等,你的应用程序还有可能崩溃:)