ViewPager in DialogFragment - IllegalStateException: Fragment does not have a view
Asked Answered
C

7

34

What I want to achieve

  • From a FragmentActivity show a dialog when clicking an Action Button in the Action Bar
  • DialogFragment - A Dialog without title
  • TabHost - Tabs at the top of the dialog
  • ViewPager with FragmentPagerAdapter - Swipable, which content is connected to the Tabs
  • 2-3 Dialog Buttons (different subclasses of the Dialog, different buttons) - Are not supposed to be in one of the ViewPager's Fragment, meaning the same buttons should remain at the bottom of the Dialog regardless of what Fragment the ViewPager is showing.


The problem

IllegalStateException: Fragment does not have a view


What I have tried/done so far

  • Using the android.support.v4 package for necessary classes
  • Calling getChildFragmentManager() instead of getSupportedFragmentManager()
  • Implemented what post #10 suggested from this link https://code.google.com/p/android/issues/detail?id=42601. I copy/paste the code directly into my two Fragment classes, which the ViewPager is suppose to be showing, plus the DialogFragment class.
  • In my custom DialogFragment I first tried to override onCreateView, then onCreateDialog and then both at the same time. All of which I got to run but with unexpected results.
    • Only onCreateView: Can't reach the AlertDialog.Builder to create the needed buttons, other than that the Dialog's results were great.
    • Only onCreateDialog: the error message shown above. I still imagine this method to be as close as I've gotten to what I want to achieve.
    • Both onCreateView and onCreateDialog: Inflated the Dialog layout in onCreateView and added the Dialog buttons to the AlertDialog.Builder in onCreateDialog. This displayed the dialog, but the added buttons from the AlertDialog.Builder were not visable. Plus the keyboard didn't show up when clicking on a EditText field.


Source code

Most come from Tutorial to implement the use of TabHost in Android 2.2 + ViewPager and Fragments. The code of the ActivityFragment is instead in a DialogFragment. However I replaced its ViewPager with a modified one from the source code from this answer https://mcmap.net/q/107947/-android-i-am-unable-to-have-viewpager-wrap_content. This was to be able to wrap_content on height.

The faulty code in my project is in DialogFragment's onCreateDialog method, I believe.

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), AlertDialog.THEME_HOLO_DARK);
    LayoutInflater inflater = getActivity().getLayoutInflater();
    view = inflater.inflate(R.layout.dialog_test, null);
    addActionButtons(builder, view);    
    builder.setView(view);

    mViewPager = (WrapContentHeightViewPager) view.findViewById(R.id.viewpager);

    initialiseTabHost();

    List<Fragment> fragments = getFragments();

    pageAdapter = new DialogPageAdapter(getChildFragmentManager(), fragments);
    mViewPager.setAdapter(pageAdapter);
    mViewPager.setOnPageChangeListener(this);

    Dialog dialog = builder.create();
    dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
    dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
    dialog.show();

    return dialog;
}


Stack trace LogCat log

FATAL EXCEPTION: main
java.lang.IllegalStateException: Fragment does not have a view
    at android.support.v4.app.Fragment$1.findViewById(Fragment.java:1425)
    at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:901)
    at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1088)
    at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682)
    at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1444)
    at android.support.v4.app.FragmentManagerImpl.executePendingTransactions(FragmentManager.java:461)
    at android.support.v4.app.FragmentPagerAdapter.finishUpdate(FragmentPagerAdapter.java:141)
    at android.support.v4.view.ViewPager.populate(ViewPager.java:1011)
    at android.support.v4.view.ViewPager.populate(ViewPager.java:880)
    at android.support.v4.view.ViewPager.onMeasure(ViewPager.java:1374)
    at my.app.package.name.WrapContentHeightViewPager.onMeasure(WrapContentHeightViewPager.java:31)
    at android.view.View.measure(View.java:15481)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5059)
    at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1396)
    at android.widget.LinearLayout.measureVertical(LinearLayout.java:681)
    at android.widget.LinearLayout.onMeasure(LinearLayout.java:574)
    at android.view.View.measure(View.java:15481)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5059)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
    at android.view.View.measure(View.java:15481)
    at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:617)
    at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:399)
    at android.view.View.measure(View.java:15481)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5059)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
    at android.view.View.measure(View.java:15481)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5059)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
    at android.view.View.measure(View.java:15481)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5059)
    at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1396)
    at android.widget.LinearLayout.measureVertical(LinearLayout.java:681)
    at android.widget.LinearLayout.onMeasure(LinearLayout.java:574)
    at android.view.View.measure(View.java:15481)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5059)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
    at android.view.View.measure(View.java:15481)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5059)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
    at android.view.View.measure(View.java:15481)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5059)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
    at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2377)
    at android.view.View.measure(View.java:15481)
    at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:1982)
    at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1200)
    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1398)
    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1118)
    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:4525)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:725)
    at android.view.Choreographer.doCallbacks(Choreographer.java:555)
    at android.view.Choreographer.doFrame(Choreographer.java:525)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:711)
    at android.os.Handler.handleCallback(Handler.java:615)
    at android.os.Handler.dispatchMessage(Handler.java:92)
    at android.os.Looper.loop(Looper.java:137)
    at android.app.ActivityThread.main(ActivityThread.java:4946)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.jav


Also...

  • I have unsuccessfully been able to try the other possible solution mentioned in https://code.google.com/p/android/issues/detail?id=42601, mentioned in post #2 and #13, because I haven't understood how and where I can use it in my code (I guess I'm in the same boat as the person who wrote #18).
Canescent answered 30/11, 2013 at 18:15 Comment(1)
If you successfully achieved then please share your codeJessjessa
F
38

I think I just ran into this same problem and learned a few things by looking at the source for DialogFragment.

It looks like even though overriding onCreateDialog(...) is a valid way to create a custom dialog, it will result in the DialogFragment having a null View, just like the error message says. In most cases this is fine - the DialogFragment doesn't need a View to show a Dialog, but if you want to nest fragments further (like you do), this won't fly.

Considering that you want to interact with an AlertDialog.Builder, there is really no perfect solution that I can see, but you've got a few options:

  1. Create the buttons in your dialog as part of the View (not using AlertDialog.Builder). You do this by overriding onCreateView instead of onCreateDialog. You should be able to get the functionality you mention by putting the buttons in their own fragment. We do something similar at my gig, and I very much prefer this method.
  2. Implement your own type that inherits from Fragment and that mirrors the DialogFragment in every way except allowing what you need. This shouldn't be too scary as DialogFragment is only ~400 and is heavily commented. Could be fun.
  3. Use a regular PagerAdapter instead of a FragmentPagerAdapter. This way it won't matter that your DialogFragment doesn't have a View.
Faefaeces answered 28/1, 2014 at 23:14 Comment(0)
H
22

Use the

onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)

instead of onCreateDialog(Bundle savedInstanceState). Don't create an alert dialog, use the inflater provided by the method, and build your view. It works for me.

Best regards!

Hearsh answered 3/9, 2014 at 22:30 Comment(2)
this actually worked. if you are overriding onCreateDialog to create dialog as me, this will save you.Mahmud
I had the same issue with BottomSheetDialogFragment, using this method as opposed to setupDialog(Dialog dialog, int style) did the trick when I was trying to add a fragment on the bottom sheet itself.Suanne
E
9

If you implement onCreateDialog to use AlertDialog, you will bump into IllegalStateException: Fragment does not have a view when accessing getChildFragmentManager or something equivalent.

To solve this issue, implement both onCreateDialog and onCreateView, where onCreateView return the view inflated in onCreateDialog.

class LocationPickerDialog : DialogFragment() {

    lateinit var customView: View

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return customView
    }

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        Log.d(TAG, "onCreateDialog")
        // StackOverflowError
        // customView = layoutInflater.inflate(R.layout.dialog_location_picker, null)
        customView = activity!!.layoutInflater.inflate(R.layout.dialog_location_picker, null)

        val builder = AlertDialog.Builder(context!!)
                .setView(customView)
                .setPositiveButton(android.R.string.ok) { _, _ ->
                    // do something
                }
                .setNegativeButton(android.R.string.cancel) { _, _ ->
                    // do something
                }
        val dialog = builder.create()

        return dialog
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        // if onCreateView doesn't return a view 
        // java.lang.IllegalStateException: Fragment does not have a view
        mapFragment = childFragmentManager.findFragmentByTag("map") as SupportMapFragment?
    }
}

https://code.luasoftware.com/tutorials/android/android-alertdialog-in-dialogfragment-fragment-does-not-have-a-view/

Ermina answered 26/7, 2018 at 4:16 Comment(1)
Thanks. Your suggestion enables me to handle a difficult corner case - #55067477Nowak
S
2

Thanks to @Tommy Visic for writing a really good description it worked.

Posting the code which worked for me.

I have removed the Dialog building code from the onCreateDialog method infact removed onCreateDialog method and the dialog view which I have been implementing in the Dialog's custom view I have included it as a View in the onCreateView method and all the things started working.

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.login_sigup_screen, null, false);
    bind = ButterKnife.bind(this, view);
    initViewPager();
    return view;
}

Faced one more problem with this implementation is:

When a Activity has Toolbar/ActionBar then it is also displayed into the DialogFragment to avoid that what is to be done is: Implement onViewCreated method of Fragment and add below code

Dialog dialog = getDialog();
dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
float dimAmount = 0.6f;
dialog.getWindow().setDimAmount(dimAmount);

This will remove the Toolbar from the DialogFragment and Activity will be displayed as it is.

Cheers

Thanks @Tommy

Regards Zeus

Sacha answered 11/4, 2017 at 6:40 Comment(0)
I
0

I ran into same issue and achieve the following solution :

class MyDialogFragment: DialogFragment {

  private lateinit var dialog: AlertDialog
  private lateinit var dialogView: View

  override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    // return the view inflated for your dialog fragment
    return dialogView
  }

  // this is called before onCreateView
  override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    dialogView = LayoutInflater
            .from(ContextThemeWrapper(requireContext(), R.style.MyAlertDialogStyle))
            .inflate(R.layout.fragment_dialog, null, false)
    dialog = AlertDialog
            .Builder(requireContext())
            .setView(dialogView)
            .create()

    dialog.setOnShowListener {
        childFragmentManager
                  .beginTransaction()
                  .replace(R.id.container, MyNestedFragmentInsideTheDialog())
                  .commit()
        // retrieve dialog buttons if any to manage onClickListener yourself
        // dialog.getButton(AlertDialog.BUTTON_POSITIVE)
    }
  }

}

The idea is to inflate the dialog content yourself and to keep a reference on it so it can be returned into onCreateView and then used by the childfragmentManager ;-)

Importunity answered 20/9, 2021 at 18:53 Comment(0)
A
0

maybe you can use setOnShowListener method in onViewCreated

    @Override
    public void onViewCreated(@NonNull @NotNull View view, @Nullable @org.jetbrains.annotations.Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        getDialog().setOnShowListener(dialog -> {
            getDialog().findViewById(R.id.et_input).requestFocus();
            SoftKeyBoardUtil.showKeyBoard(getDialog().getWindow());
        });
    }

Arsenopyrite answered 26/12, 2023 at 3:22 Comment(0)
G
-1

you can make variable and put the condition between try and catch same that

  val comeFrom = try {
                args.comeFrom.toString()
            }catch (e:Exception){
                "0"
            }
Gschu answered 31/10, 2022 at 8:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.