Reputation: 70406
I have setup a very simple test project https://github.com/ArtworkAD/ViewPagerDialogTest to evaluate following situation: the main activity has a view pager which hosts a single fragment using support fragment manager:
public class MainActivity extends AppCompatActivity {
// ...
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
viewPager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager()));
// ...
tabLayout.setupWithViewPager(viewPager);
}
@Override
protected void onResume() {
super.onResume();
MainActivity.CustomDialog dialog = (MainActivity.CustomDialog) getSupportFragmentManager().findFragmentByTag(MainActivity.CustomDialog.TAG);
if (dialog == null) {
new MainActivity.CustomDialog().show(getSupportFragmentManager().beginTransaction(), MainActivity.CustomDialog.TAG);
}
}
// ...
}
When the activity is resumed a dialog fragment is shown inside the main activity.
The single fragment inside the view pager is defined like this:
public class RootFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.root_fragment, container, false);
if (savedInstanceState == null) {
getFragmentManager().beginTransaction().add(R.id.root_frame, new FirstLevelFragment(), "ROOT").commit();
}
return root;
}
}
This root fragment allows us to stack other fragments on the "root_frame". So we stack another and another:
public class FirstLevelFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
setRetainInstance(true);
View root = inflater.inflate(R.layout.first_level_fragment, container, false);
root.findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SecondLevelFragment f = (SecondLevelFragment) getActivity().getSupportFragmentManager().findFragmentByTag("NESTED");
if (f == null) {
getActivity().getSupportFragmentManager().beginTransaction().add(R.id.root_frame, new SecondLevelFragment(), "NESTED").addToBackStack(null).commit();
}
}
});
return root;
}
public static class SecondLevelFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
setRetainInstance(true);
return inflater.inflate(R.layout.second_level_fragment, container, false);
}
}
}
This works great! The stacking idea is taken from https://stackoverflow.com/a/21453571/401025 . However when dialog is shown and the users goes to the second level fragment and rotates the screen I get following exception:
E/AndroidRuntime: java.lang.RuntimeException: Unable to start activity ComponentInfo{de.azzoft.viewpagerdialogtest/de.azzoft.viewpagerdialogtest.MainActivity}: java.lang.IllegalArgumentException: No view found for id 0x7f0c0083 (de.azzoft.viewpagerdialogtest:id/root_frame) for fragment SecondLevelFragment{15c0db38 #0 id=0x7f0c0083 NESTED}
E/AndroidRuntime: Caused by: java.lang.IllegalArgumentException: No view found for id 0x7f0c0083 (de.azzoft.viewpagerdialogtest:id/root_frame) for fragment SecondLevelFragment{15c0db38 #0 id=0x7f0c0083 NESTED}
Full stack trace: https://github.com/ArtworkAD/ViewPagerDialogTest/blob/master/README.md
Without the dialog appearing everything works great. You can test it by downloading the test project.
It seems that the dialog, which is actually a fragment, messes up fragment hierarchy when it is added to the activity. Any ideas how to fix this?
It is important that the second fragment is retained.
Upvotes: 3
Views: 1754
Reputation: 17085
No view found for id 0x7f0c0083 (de.azzoft.viewpagerdialogtest:id/root_frame) for fragment SecondLevelFragment
When Activity
recreates on rotate, the Activity FragmentManger
tries to add the SecondLevelFragment
into R.id.root_frame
. But the root_frame
view is not in Activity layout, its in FirstLevelFragment
layout. Thats why the app crashes.
You have to make two changes to fix this issue.
Add the FirstLevelFragment
into the RootFragment
using the getChildFragmentManager
getChildFragmentManager().beginTransaction().add(R.id.root_frame, new FirstLevelFragment(), "ROOT").commit();
Add the SecondLevelFragment
using FragmentManager
getFragmentManager().beginTransaction().add(R.id.root_frame, new SecondLevelFragment(), "NESTED").addToBackStack(null).commit();
Finally remove the setRetainInstance
from FirstLevelFragment
and SecondLevelFragment
as nested fragments doesn't required to set retain.
If you need to pop back the SecondLevelFragment
on back press you need to pass the back press the event to RootFragment and pop from back stack.
Override the back press on activity
@Override
public void onBackPressed() {
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.viewpager);
if(fragment instanceof RootFragment){
boolean handled = ((RootFragment)fragment).onBackPressed();
if(handled){
return;
}
}
super.onBackPressed();
}
And handle the back press on RootFragment
public boolean onBackPressed() {
int count = getChildFragmentManager().getBackStackEntryCount();
if(count > 0){
getChildFragmentManager().popBackStackImmediate();
return true;
}
return false;
}
I created a Pull request to your repository . please check https://github.com/ArtworkAD/ViewPagerDialogTest/pull/1
Let me know if any questions.
Upvotes: 2
Reputation: 2746
I think you missed setContentView()
in onCreate()
of your Activity. See your Fragment can not be added without a View hierarchy. Your Fragment is hosted by an activity. So you need to set the content to the activity first.
Hope this Helps, Thanks.
Upvotes: 0
Reputation: 1388
If you override onDismiss so resolved crash. enjoy it.
@Override
protected void onResume() {
super.onResume();
DialogFragment dialog = (DialogFragment) getSupportFragmentManager().findFragmentByTag(TAG);
if(dialog == null){
CustomDialog.newInstance().show(getSupportFragmentManager(), TAG);
}
}
public static class CustomDialog extends DialogFragment {
public static CustomDialog newInstance() {
CustomDialog d = new CustomDialog();
return d;
}
@Override
public void onDismiss(DialogInterface dialog) {
// super.onDismiss(dialog);
Toast.makeText(getActivity(), "onDismiss", Toast.LENGTH_LONG).show();
}
@Override
public void onCancel(DialogInterface dialog) {
// super.onCancel(dialog);
Toast.makeText(getActivity(), "onCancel", Toast.LENGTH_LONG).show();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("Dialog");
builder.setMessage("This is a message!");
builder.setPositiveButton("Okay", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
Toast.makeText(getActivity(), "onClick", Toast.LENGTH_LONG).show();
}
});
builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
Toast.makeText(getActivity(), "onClick", Toast.LENGTH_LONG).show();
}
});
return builder.show();
}
}
Upvotes: 1
Reputation: 15336
Well, I had downloaded your Test app and it seems that I have fixed the problem.
In your FirstLevelFragment
class, comment the following line
//if (nestedNestedFragment == null) {
getActivity().getSupportFragmentManager().beginTransaction().add(R.id.root_frame, new SecondLevelFragment(), "NESTED").addToBackStack(null).commit();
//}
And
Comment setRetainInstance(true);
in SecondLevelFragment
Upvotes: 0
Reputation: 13548
If you want to keep the state of your Fragments
you should use a FragmentStatePagerAdapter.
From the docs:
Implementation of PagerAdapter that uses a Fragment to manage each page. This class also handles saving and restoring of fragment's state.
If you use this you can also remove the setRetainInstance(true)
calls.
Upvotes: 0