방향 변경에 대한 Android Fragment 수명주기 것이 분명하지만 어디에서 잘못 가고 있는지

호환 패키지를 사용하여 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>


답변

당신은 당신의 조각을 다른 조각 위에 레이어링하고 있습니다.

구성 변경이 발생하면 이전 프래그먼트가 다시 생성 될 때 새 활동에 자신을 추가합니다. 이것은 대부분의 경우 뒤쪽에 엄청난 고통입니다.

새 조각을 다시 만드는 대신 동일한 조각을 사용하여 발생하는 오류를 중지 할 수 있습니다. 다음 코드를 추가하기 만하면됩니다.

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 내부에서 활동 뷰에 액세스하려고하면 문제가 발생합니다. (조각에서 부모 활동에서 뷰를 가져 오는 것은 쉽지 않습니다).


답변

이 책 을 인용하자면 “일관된 사용자 경험을 보장하기 위해 Android는 구성 변경으로 인해 활동이 다시 시작될 때 조각 레이아웃과 관련 백 스택을 유지합니다.” (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();
    }
}

답변

활동의 onCreate () 메서드는 본 것처럼 방향이 변경된 후에 호출됩니다. 따라서 활동의 방향이 변경된 후 Fragment를 추가하는 FragmentTransaction을 실행하지 마십시오.

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

조각은 변경되지 않아야하며 변경되지 않아야합니다.


답변

@Override사용하여 FragmentActivity를 수행 할 수 있습니다 onSaveInstanceState(). super.onSaveInstanceState()메서드에서를 호출하지 마십시오 .


답변

항상 nullpointer 예외를 방지해야하므로 먼저 saveinstance 메서드에서 번들 정보를 확인해야합니다. 이 블로그 링크 를 확인하는 간단한 설명

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

답변

프로젝트 만 수행하는 경우 프로젝트 관리자는 전환 기능 화면을 달성해야한다고 말하지만 다른 레이아웃을로드하는 화면 전환을 원하지는 않습니다 (레이아웃 및 레이아웃 포트 시스템을 만들 수 있습니다.

화면 상태를 자동으로 결정하고 해당 레이아웃을로드합니다.) 활동이나 프래그먼트를 다시 초기화해야하기 때문에 사용자 경험이 좋지 않고 화면 스위치에 직접 표시되지 않습니다. Url = YgNfP-vHy-Nuldi7YHTfNet3AtLdN-w__O3z1wLOnzr3wDjYo7X7PYdNyhw8R24ZE22xiKnydni7R0r35s2fOLcHOiLGYT9Qh_fjqtytJki & wd = & eqid = f258719e0001f24000000004585a1082 = & eqid = f258719

전제는 다음과 같이 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 2 재 작성 활동 또는 onConfigurationChanged 메소드의 프래그먼트를 모니터링 할 수 있습니다.

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

답변

구성 변경시 프레임 워크는 조각의 새 인스턴스를 만들고 활동에 추가합니다. 그래서이 대신 :

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의 이전 인스턴스가 추가됩니다.