首页 文章

Android Fragment生命周期取决于方向的变化

提问于
浏览
107

使用兼容包以使用Fragments定位2.2 .

重新编码活动以在应用程序中使用片段后,我无法获得方向更改/状态管理工作,因此我创建了一个带有单个FragmentActivity和单个片段的小型测试应用程序 .

方向更改的日志很奇怪,多次调用片段OnCreateView .

我显然遗漏了一些东西 - 比如分离片段并重新连接它而不是创建一个新实例,但是我看不到任何可以指示我出错的文档 .

请问有人能说清楚我在做错了吗 . 谢谢

方向更改后的日志如下 .

Initial creation
12-04 11:57:15.808: D/FragmentTest.FragmentTestActivity(3143): onCreate
12-04 11:57:15.945: D/FragmentTest.FragmentOne(3143): OnCreateView
12-04 11:57:16.081: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState null


Orientation Change 1
12-04 11:57:39.031: D/FragmentTest.FragmentOne(3143): onSaveInstanceState
12-04 11:57:39.031: D/FragmentTest.FragmentTestActivity(3143): onCreate
12-04 11:57:39.031: D/FragmentTest.FragmentOne(3143): OnCreateView
12-04 11:57:39.031: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState not null
12-04 11:57:39.031: D/FragmentTest.FragmentOne(3143): OnCreateView
12-04 11:57:39.167: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState null


Orientation Change 2
12-04 11:58:32.162: D/FragmentTest.FragmentOne(3143): onSaveInstanceState
12-04 11:58:32.162: D/FragmentTest.FragmentOne(3143): onSaveInstanceState
12-04 11:58:32.361: D/FragmentTest.FragmentTestActivity(3143): onCreate
12-04 11:58:32.361: D/FragmentTest.FragmentOne(3143): OnCreateView
12-04 11:58:32.361: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState not null
12-04 11:58:32.361: D/FragmentTest.FragmentOne(3143): OnCreateView
12-04 11:58:32.361: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState not null
12-04 11:58:32.498: D/FragmentTest.FragmentOne(3143): OnCreateView
12-04 11:58:32.498: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState null

主要活动(FragmentActivity)

public class FragmentTestActivity extends FragmentActivity {
/** Called when the activity is first created. */

private static final String TAG = "FragmentTest.FragmentTestActivity";


FragmentManager mFragmentManager;

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

    Log.d(TAG, "onCreate");

    mFragmentManager = getSupportFragmentManager();
    FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();

    FragmentOne fragment = new FragmentOne();

    fragmentTransaction.add(R.id.fragment_container, fragment);
    fragmentTransaction.commit();
}

而片段

public class FragmentOne extends Fragment {

private static final String TAG = "FragmentTest.FragmentOne";

EditText mEditText;

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

    Log.d(TAG, "OnCreateView");

    View v = inflater.inflate(R.layout.fragmentonelayout, container, false);

    // Retrieve the text editor, and restore the last saved state if needed.
    mEditText = (EditText)v.findViewById(R.id.editText1);

    if (savedInstanceState != null) {

        Log.d(TAG, "OnCreateView->SavedInstanceState not null");

        mEditText.setText(savedInstanceState.getCharSequence("text"));
    }
    else {
        Log.d(TAG,"OnCreateView->SavedInstanceState null");
    }
    return v;
}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    Log.d(TAG, "FragmentOne.onSaveInstanceState");

    // Remember the current text, to restore if we later restart.
    outState.putCharSequence("text", mEditText.getText());
}

表现

<uses-sdk android:minSdkVersion="8" />

<application
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name" >
    <activity
        android:label="@string/app_name"
        android:name=".activities.FragmentTestActivity" 
        android:configChanges="orientation">
        <intent-filter >
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

7 回答

  • 79

    我们应该总是尝试阻止nullpointer异常,因此我们必须先在saveinstance方法中检查bundle信息 . 如需简短解释,请查看此博客link

    public static class DetailsActivity extends Activity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            if (getResources().getConfiguration().orientation
                == Configuration.ORIENTATION_LANDSCAPE) {
                // If the screen is now in landscape mode, we can show the
                // dialog in-line with the list so we don't need this activity.
                finish();
                return;
            }
    
            if (savedInstanceState == null) {
                // During initial setup, plug in the details fragment.
                DetailsFragment details = new DetailsFragment();
                details.setArguments(getIntent().getExtras());
                getFragmentManager().beginTransaction().add(android.R.id.content, details).commit();
            }
        } 
    }
    
  • 9

    如果您只是做一个项目,那么项目经理说您需要实现切换功能屏幕,但您不希望屏幕切换负载不同的布局(可以创建布局和布局端口系统) .

    你会自动判断屏幕状态,加载相应的布局),因为需要重新初始化活动或片段,用户体验不好,不能直接在屏幕上切换,我参考? URL = YgNfP-VHY-Nuldi7YHTfNet3AtLdN-w__O3z1wLOnzr3wDjYo7X7PYdNyhw8R24ZE22xiKnydni7R0r35s2fOLcHOiLGYT9Qh_fjqtytJki&WD =&eqid = f258719e0001f24000000004585a1082

    前提是你的布局使用layout_weight的布局方式的权重,如下:

    <LinearLayout
    Android:id= "@+id/toplayout"
    Android:layout_width= "match_parent"
    Android:layout_height= "match_parent"
    Android:layout_weight= "2"
    Android:orientation= "horizontal" >
    

    所以我的方法是,当屏幕切换时,不需要加载视图文件的新布局,修改onConfigurationChanged动态权重中的布局,步骤如下:1首先设置:活动属性中的AndroidManifest.xml:android:configChanges =“keyboardHidden | orientation | screenSize”为了防止屏幕切换,请避免重新加载,以便能够在onConfigurationChanged方法中监视onConfigurationChanged 2重写活动或片段 .

    @Override
    Public void onConfigurationChanged (Configuration newConfig) {
        Super.onConfigurationChanged (newConfig);
        SetContentView (R.layout.activity_main);
        if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
            //On the layout / / weight adjustment
            LinearLayout toplayout = (LinearLayout) findViewById (R.id.toplayout);
            LinearLayout.LayoutParams LP = new LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 2.0f);
            Toplayout.setLayoutParams (LP);
            LinearLayout tradespace_layout = (LinearLayout) findViewById(R.id.tradespace_layout);
            LinearLayout.LayoutParams LP3 = new LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 2.8f);
            Tradespace_layout.setLayoutParams (LP3);
        }
        else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT)
        {
            //On the layout / / weight adjustment
            LinearLayout toplayout = (LinearLayout) findViewById (R.id.toplayout);
            LinearLayout.LayoutParams LP = new LayoutParams (LinearLayout.LayoutParams.MATCH_PARENT, 0, 2.8f);
            Toplayout.setLayoutParams (LP);
            LinearLayout tradespace_layout = (LinearLayout) findViewById (R.id.tradespace_layout);
            LinearLayout.LayoutParams LP3 = new LayoutParams (LinearLayout.LayoutParams.MATCH_PARENT, 0, 2.0f);
            Tradespace_layout.setLayoutParams (LP3);
        }
    }
    
  • 0

    你将碎片分层叠加在另一个上面 .

    当配置发生更改时,旧的Fragment会在重新创建时将其自身添加到新的Activity中 . 这是大多数时候后方的巨大痛苦 .

    您可以通过使用相同的片段来停止发生的错误,而不是重新创建新的片段 . 只需添加此代码:

    if (savedInstanceState == null) {
        // only create fragment if activity is started for the first time
        mFragmentManager = getSupportFragmentManager();
        FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
    
        FragmentOne fragment = new FragmentOne();
    
        fragmentTransaction.add(R.id.fragment_container, fragment);
        fragmentTransaction.commit();
    } else {        
        // do nothing - fragment is recreated automatically
    }
    

    但请注意:如果您尝试从Fragment内部访问活动视图,则会出现问题,因为生命周期会微妙地改变 . (从片段中获取父活动的视图并不容易) .

  • 0

    你可以使用 onSaveInstanceState() 来实现FragmentActivity . 请务必不要在方法中调用 super.onSaveInstanceState() .

  • -1

    在配置更改时,框架将为您创建片段的新实例并将其添加到活动中 . 所以不是这样的:

    FragmentOne fragment = new FragmentOne();
    
    fragmentTransaction.add(R.id.fragment_container, fragment);
    

    做这个:

    if (mFragmentManager.findFragmentByTag(FRAG1_TAG) == null) {
        FragmentOne fragment = new FragmentOne();
    
        fragmentTransaction.add(R.id.fragment_container, fragment, FRAG1_TAG);
    }
    

    请注意,除非调用setRetainInstance(true),否则框架会在方向更改时添加FragmentOne的新实例,在这种情况下,它将添加FragmentOne的旧实例 .

  • 4

    正如您所见,在方向更改后调用活动的onCreate()方法 . 因此,不要执行在活动中更改方向后添加Fragment的FragmentTransaction .

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState==null) {
            //do your stuff
        }
    }
    

    片段应该而且必须保持不变 .

  • 170

    引用this book,"to ensure a consistent user experience, Android persists the Fragment layout and associated back stack when an Activity is restarted due to a configuration change."(第124页)

    接近它的方法是首先检查Fragment后备堆栈是否已经填充,并且只有在没有填充时才创建新的片段实例:

    @Override
    public void onCreate(Bundle savedInstanceState) {
    
            ...    
    
        FragmentOne fragment = (FragmentOne) mFragmentManager.findFragmentById(R.id.fragment_container); 
    
        if (fragment == null) {
            FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
            fragmentTransaction.add(R.id.fragment_container, new FragmentOne());
            fragmentTransaction.commit();
        }
    }
    

相关问题