在具有嵌套片段

时间:2015-10-23 21:03:05

标签: android android-fragments android-viewpager android-support-library

我已经设置了一个非常简单的测试项目https://github.com/ArtworkAD/ViewPagerDialogTest来评估以下情况:主要活动有一个视图寻呼机,它使用支持片段管理器来托管单个片段:

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);
        }
    }
    // ...
}

当活动恢复时,主要活动内会显示一个对话框片段。

视图寻呼机内的单个片段定义如下:

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

这很棒!堆叠的想法取自https://stackoverflow.com/a/21453571/401025。但是,当显示对话框并且用户转到第二级片段并旋转屏幕时,我得到以下异常:

  

E / AndroidRuntime:java.lang.RuntimeException:无法启动活动   ComponentInfo {de.azzoft.viewpagerdialogtest / de.azzoft.viewpagerdialogtest.MainActivity}:   java.lang.IllegalArgumentException:找不到id 0x7f0c0083的视图   (de.azzoft.viewpagerdialogtest:id / root_frame)片段   SecondLevelFragment {15c0db38#0 id = 0x7f0c0083 NESTED}

     

E / AndroidRuntime:引起:java.lang.IllegalArgumentException:不   找到的内容id为0x7f0c0083   (de.azzoft.viewpagerdialogtest:id / root_frame)片段   SecondLevelFragment {15c0db38#0 id = 0x7f0c0083 NESTED}

完整堆栈跟踪:https://github.com/ArtworkAD/ViewPagerDialogTest/blob/master/README.md

如果没有出现对话框,一切都很有效。您可以通过下载测试项目来测试它。

似乎对话框实际上是一个片段,当它被添加到活动时会破坏片段层次结构。任何想法如何解决这个问题?

保留第二个片段非常重要。

5 个答案:

答案 0 :(得分:1)

如果覆盖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();
        }
    }

答案 1 :(得分:1)

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视图不在“活动”布局中,而是在FirstLevelFragment布局中。这就是应用程序崩溃的原因。

您必须进行两项更改才能解决此问题。

使用FirstLevelFragment

RootFragment添加到getChildFragmentManager
getChildFragmentManager().beginTransaction().add(R.id.root_frame, new FirstLevelFragment(), "ROOT").commit();

使用SecondLevelFragment

添加FragmentManager
getFragmentManager().beginTransaction().add(R.id.root_frame, new SecondLevelFragment(), "NESTED").addToBackStack(null).commit();

最后从setRetainInstanceFirstLevelFragment移除SecondLevelFragment,因为嵌套片段并不需要设置保留。

如果你需要在背面弹回SecondLevelFragment,你需要传递背面按事件到RootFragment并从后面堆栈弹出。

覆盖活动背面按

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

我为您的存储库创建了一个Pull请求。请检查 https://github.com/ArtworkAD/ViewPagerDialogTest/pull/1

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

答案 2 :(得分:0)

如果您希望保持状态 Fragments,则应使用FragmentStatePagerAdapter

来自文档:

  

使用Fragment管理每个的PagerAdapter的实现   页。该类还处理片段的保存和恢复   状态。

如果您使用此功能,还可以删除setRetainInstance(true)来电。

答案 3 :(得分:0)

好吧,我已经下载了你的测试应用程序,似乎我已经解决了这个问题。

FirstLevelFragment课程中,请注释以下内容

 //if (nestedNestedFragment == null) {
     getActivity().getSupportFragmentManager().beginTransaction().add(R.id.root_frame, new SecondLevelFragment(), "NESTED").addToBackStack(null).commit();
 //}

setRetainInstance(true);

中的评论SecondLevelFragment

答案 4 :(得分:0)

我认为您错过setContentView()活动中的onCreate()。如果没有View层次结构,则无法添加片段。您的片段由活动托管。因此,您需要先将内容设置为活动。

希望这有助于, 感谢。