Reputation: 35661
What is the correct way to handle an orientation change when using Fragments?
I have a landscape layout that contains 2 fragments (instantiated in code into FrameLayout
s). When I switch to portrait mode (the layout of which contains only one FrameLayout
that holds the left pane only), the right hand fragment is no longer required.
I am receiving an error:
E/AndroidRuntime(4519): Caused by: java.lang.IllegalArgumentException: No view found for id 0x7f060085 for fragment myFragment{418a2200 #2 id=0x7f060085}
which is assume is my activity trying to re-attach the fragment where it was before the orientation change but as the view that contains the fragment does not exist in portrait mode the error is thrown.
I have tried the following hide/remove/detach methods but still get the error. What is the correct way to tell a fragment it is not needed any more and do not try to display?
@Override
public void onCreate(Bundle b) {
super.onCreate(b);
Fragment f = getSupportFragmentManager().findFragmentById(R.id.fragholder2);
//rightPane is a framelayout that holds my fragment.
if (rightPane == null && f != null) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.hide(f); // This doesnt work
ft.remove(f); // neither does this
ft.detach(f); // or this
ft.commit;
}
}
Upvotes: 34
Views: 21896
Reputation: 1496
Android does recreates both fragments during screen rotation. But if you add check below into onCreateView() it will prevent you from issues:
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
if (container == null) {
// Currently in a layout without a container, so no
// reason to create our view.
return null;
}
// inflate view and do other stuff
}
I took this from Android Developers blog.
Upvotes: -1
Reputation: 541
I ran into the same problem and I think I figured out another solution. This solution may be better because you don't have to add the fragment to the back stack.
Remove the right hand side fragment from your activity in Activity.onSaveInstanceState()
before calling super.onSaveInstanceState()
. This works for me:
public MyActivity extends Activity
{
@Override
public onCreate(Bundle state)
{
super.onCreate(state);
// Set content view
setContentView(R.layout.my_activity);
// Store whether this is a dual pane layout
mDualPane = findViewById(R.id.rightFragHolder) != null;
// Other stuff, populate the left fragment, etc.
.
.
.
if (mDualPane)
{
mRightFragment = new RightFragment();
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.rightFragHolder, mRightFragment);
ft.commit()
}
}
@Override
public void onSaveInstanceState(Bundle state)
{
if (mDualPane)
{
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.remove(mRightFragment);
ft.commit()
}
super.onSaveInstanceState(state);
}
private boolean mDualPane;
private Fragment mRightFragment;
}
Upvotes: 14
Reputation: 29867
If you have a two pane activity with a left and right pane and one of the panes (usually the right pane) is suppose to not show when the device switches to portrait mode, let Android do its thing and recreate the right pane. But during the onCreateView of the right pane, the first thing you should do is check if one of the layout elements used by the pane is even available. If it is not, remove the fragment using the FragmentManager and return immediately:
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
{
View myView = getActivity().findViewById(R.id.myView);
if (myView == null)
{
FragmentTransaction fragTransaction = getFragmentManager().beginTransaction();
fragTransaction.remove(this);
fragTransaction.commit();
return null;
}
}
Upvotes: 0
Reputation: 13254
If you are retaining the fragment, try not retaining it.
setRetainInstance(false)
instead of
setRetainInstance(true)
Upvotes: 1
Reputation: 6716
Usually you'll have two fragments (left/right), one main activity and one container activity for the right fragment (only when shown on phone devices). This is described in this blog entry: The Android 3.0 Fragments API
public class MyActivity extends FragmentActivity
implements MyListFragment.MyContextItemSelectedListener {
@Override
public void onCreate(final Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.activity);
}
// Callback from ListFragment
@Override
public void myContextItemSelected(final int action, final long id) {
if (action == R.id.men_show) {
processShow(id);
}
}
private void processShow(final long id) {
if (Tools.isXlargeLand(getApplicationContext())) {
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.right);
if (fragment == null ||
fragment instanceof MyEditFragment ||
(fragment instanceof MyShowFragment && ((MyShowFragment) fragment).getCurrentId() != id)) {
fragment = new MyShowFragment(id);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.right, fragment);
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
transaction.commit();
}
} else {
Intent intent = new Intent();
intent.setClass(this, MyShowActivity.class);
intent.putExtra("ID", id);
startActivityForResult(intent, MyConstants.DLG_TABLE1SHOW);
}
}
private static boolean isXlargeLand(final Context context) {
Configuration configuration = context.getResources().getConfiguration();
return (((configuration.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_XLARGE) &&
configuration.orientation == Configuration.ORIENTATION_LANDSCAPE);
}
}
Upvotes: 0
Reputation: 35661
I think I resolved it.
I added the fragment to the back stack and then before the activity closes popped it off again which effectively gets rid of it. Seems to work so far.
Upvotes: 0
Reputation: 3804
You do not need this activity anymore because the fragment will be shown in-line. So you can finish the activity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
// we are in landscape so you do not need this activity anymore
finish();
return;
}
if (savedInstanceState == null) {
// plugin right pane fragment
YourFragment frgm = new YourFragment();
getSupportFragmentManager().beginTransaction()
.add(..., frgm).commit();
}
}
Upvotes: -4