在具有嵌套片段的视图寻呼机上方显示对话框

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

最后从 FirstLevelFragmentSecondLevelFragment 中删除 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

如有任何问题,请告诉我。