在具有嵌套片段的视图寻呼机上方显示对话框
Show dialog above view pager that has nested fragments
我已经设置了一个非常简单的测试项目 https://github.com/ArtworkAD/ViewPagerDialogTest 来评估以下情况:主要 activity 有一个视图寻呼机,它使用支持片段管理器托管单个片段:
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);
}
}
// ...
}
当 activity 恢复时,主 activity 内会显示一个对话框片段。
视图分页器中的单个片段定义如下:
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;
}
}
这个根片段允许我们在 "root_frame" 上堆叠其他片段。所以我们堆叠一个又一个:
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);
}
}
}
效果很好!堆叠思想取自 。但是,当显示对话框并且用户转到第二级片段并旋转屏幕时,出现以下异常:
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}
完整堆栈跟踪:https://github.com/ArtworkAD/ViewPagerDialogTest/blob/master/README.md
没有出现对话框,一切正常。您可以通过下载测试项目来测试它。
似乎实际上是片段的对话框在添加到 activity 时弄乱了片段层次结构。有什么解决办法吗?
保留第二个片段很重要。
如果您想保持Fragments
的状态,您应该使用FragmentStatePagerAdapter。
来自文档:
Implementation of PagerAdapter that uses a Fragment to manage each
page. This class also handles saving and restoring of fragment's
state.
如果您使用它,您还可以删除 setRetainInstance(true)
调用。
好的,我已经下载了您的测试应用程序,看来我已经解决了问题。
在您的 FirstLevelFragment
class 中,注释以下行
//if (nestedNestedFragment == null) {
getActivity().getSupportFragmentManager().beginTransaction().add(R.id.root_frame, new SecondLevelFragment(), "NESTED").addToBackStack(null).commit();
//}
和
在 SecondLevelFragment
中评论 setRetainInstance(true);
如果你覆盖 onDismiss 那么解决崩溃。享受吧。
@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();
}
}
我认为您在 Activity 的 onCreate()
中错过了 setContentView()
。看到没有视图层次结构就无法添加您的片段。您的片段由 activity 托管。所以需要先设置内容为activity
希望对您有所帮助,
谢谢
No view found for id 0x7f0c0083 (de.azzoft.viewpagerdialogtest:id/root_frame) for fragment SecondLevelFragment
当 Activity
在旋转时重新创建时,Activity FragmentManger
尝试将 SecondLevelFragment
添加到 R.id.root_frame
中。但是 root_frame
视图不在 Activity 布局中,它在 FirstLevelFragment
布局中。这就是应用程序崩溃的原因。
您必须进行两项更改才能解决此问题。
使用 getChildFragmentManager
将 FirstLevelFragment
添加到 RootFragment
getChildFragmentManager().beginTransaction().add(R.id.root_frame, new FirstLevelFragment(), "ROOT").commit();
使用 FragmentManager
添加 SecondLevelFragment
getFragmentManager().beginTransaction().add(R.id.root_frame, new SecondLevelFragment(), "NESTED").addToBackStack(null).commit();
最后从 FirstLevelFragment
和 SecondLevelFragment
中删除 setRetainInstance
,因为嵌套片段不需要设置保留。
如果你需要在返回时弹出 SecondLevelFragment
你需要将返回事件传递给 RootFragment 并从返回堆栈中弹出。
覆盖 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();
}
并处理 RootFragment
上的后退压力
public boolean onBackPressed() {
int count = getChildFragmentManager().getBackStackEntryCount();
if(count > 0){
getChildFragmentManager().popBackStackImmediate();
return true;
}
return false;
}
我向您的存储库创建了一个拉取请求。请检查
https://github.com/ArtworkAD/ViewPagerDialogTest/pull/1
如有任何问题,请告诉我。
我已经设置了一个非常简单的测试项目 https://github.com/ArtworkAD/ViewPagerDialogTest 来评估以下情况:主要 activity 有一个视图寻呼机,它使用支持片段管理器托管单个片段:
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);
}
}
// ...
}
当 activity 恢复时,主 activity 内会显示一个对话框片段。
视图分页器中的单个片段定义如下:
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;
}
}
这个根片段允许我们在 "root_frame" 上堆叠其他片段。所以我们堆叠一个又一个:
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);
}
}
}
效果很好!堆叠思想取自 。但是,当显示对话框并且用户转到第二级片段并旋转屏幕时,出现以下异常:
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}
完整堆栈跟踪:https://github.com/ArtworkAD/ViewPagerDialogTest/blob/master/README.md
没有出现对话框,一切正常。您可以通过下载测试项目来测试它。
似乎实际上是片段的对话框在添加到 activity 时弄乱了片段层次结构。有什么解决办法吗?
保留第二个片段很重要。
如果您想保持Fragments
的状态,您应该使用FragmentStatePagerAdapter。
来自文档:
Implementation of PagerAdapter that uses a Fragment to manage each page. This class also handles saving and restoring of fragment's state.
如果您使用它,您还可以删除 setRetainInstance(true)
调用。
好的,我已经下载了您的测试应用程序,看来我已经解决了问题。
在您的 FirstLevelFragment
class 中,注释以下行
//if (nestedNestedFragment == null) {
getActivity().getSupportFragmentManager().beginTransaction().add(R.id.root_frame, new SecondLevelFragment(), "NESTED").addToBackStack(null).commit();
//}
和
在 SecondLevelFragment
setRetainInstance(true);
如果你覆盖 onDismiss 那么解决崩溃。享受吧。
@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();
}
}
我认为您在 Activity 的 onCreate()
中错过了 setContentView()
。看到没有视图层次结构就无法添加您的片段。您的片段由 activity 托管。所以需要先设置内容为activity
希望对您有所帮助, 谢谢
No view found for id 0x7f0c0083 (de.azzoft.viewpagerdialogtest:id/root_frame) for fragment SecondLevelFragment
当 Activity
在旋转时重新创建时,Activity FragmentManger
尝试将 SecondLevelFragment
添加到 R.id.root_frame
中。但是 root_frame
视图不在 Activity 布局中,它在 FirstLevelFragment
布局中。这就是应用程序崩溃的原因。
您必须进行两项更改才能解决此问题。
使用 getChildFragmentManager
FirstLevelFragment
添加到 RootFragment
getChildFragmentManager().beginTransaction().add(R.id.root_frame, new FirstLevelFragment(), "ROOT").commit();
使用 FragmentManager
SecondLevelFragment
getFragmentManager().beginTransaction().add(R.id.root_frame, new SecondLevelFragment(), "NESTED").addToBackStack(null).commit();
最后从 FirstLevelFragment
和 SecondLevelFragment
中删除 setRetainInstance
,因为嵌套片段不需要设置保留。
如果你需要在返回时弹出 SecondLevelFragment
你需要将返回事件传递给 RootFragment 并从返回堆栈中弹出。
覆盖 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();
}
并处理 RootFragment
public boolean onBackPressed() {
int count = getChildFragmentManager().getBackStackEntryCount();
if(count > 0){
getChildFragmentManager().popBackStackImmediate();
return true;
}
return false;
}
我向您的存储库创建了一个拉取请求。请检查 https://github.com/ArtworkAD/ViewPagerDialogTest/pull/1
如有任何问题,请告诉我。