9

I am having trouble wrapping my head around something but let me first describe my setup:

flow

I have an activity that references 3 fragments, each one of them get shown at the correct time. This is how the ChildrenSpecificationFragment looks:

If the user clicks the floating action button the following DialogFragment opens:

I found the following information in the new material design guidelines: https://www.google.com/design/spec/components/dialogs.html#dialogs-full-screen-dialogs

Avoid dialogs that: Open additional dialogs from within a dialog. Contain scrolling content, particularly alerts. Instead, consider alternate containers or layouts that are optimized for reading or interacting with significant amounts of content.

Exceptions include:Full-screen dialogs may open additional dialogs, such as pickers, because their design accommodates additional layers of material without significantly increasing the app’s perceived z-depth or visual noise.

This is where my problems begin. The 'add child' dialog has scrollable content (in landscape mode) and when the user clicks 'Birth date' a date picker opens.

I am trying to find a way to implement a full screen dialog (as in the guidelines) that has a callback to the ChildrenSpecificationFragment, so that I can add the child to the RecyclerView .

I hope that my questing is clear and would greatly appreciate any input that would lead me to the solution. Thanks in Advance!

Jdruwe
  • 3,450
  • 6
  • 36
  • 57
  • what is your add child fragment's layout file? – windchime Jun 13 '15 at 20:25
  • http://pastebin.com/bhscbGu2 – Jdruwe Jun 13 '15 at 20:27
  • I've seen answers and other questions that use a FragmentTransaction to add the dialog, but I've yet to get it working. – Jon Jun 23 '15 at 10:06
  • Hey Jon, I found a way to make it work, I wrote a blog post about it yesterday: http://jeroendruwe.be/full-screen-dialogs-in-android/. Though I don't know if it's the best way to solve the issue. – Jdruwe Jun 23 '15 at 10:08
  • Did you have any luck with the instructions here: https://developer.android.com/guide/topics/ui/dialogs.html#FullscreenDialog. This doesn't work for me, but I'm also using the android design NavigtionView, which may affect it. – Jon Jun 23 '15 at 14:27
  • I did but my solution seemed beter for my case. – Jdruwe Jun 23 '15 at 14:33
  • Is your problem with making a full-screen dialog fragment or is it about having a callback from a fragment to an other ? – user2641570 Jun 24 '15 at 13:02
  • I was looking for a solution that handles both. – Jdruwe Jun 24 '15 at 13:10
  • Any specific reason you want to use Dialog only? You could start a new activity (instead of full screen Dialog) and return the result (child info) in `onActivityResult`? – Abhishek V Jun 30 '15 at 04:59
  • Would you like a solution that establish a communication between fragments and/or activity? In Java, it is not a callback. And, it is not clear to me what your code is like for showing the full-screen dialog. Is it in a fragment? – The Original Android Jun 30 '15 at 21:01
  • I was trying to open a full screen dialog from the fragment but it did not work out so I just created a new activity as explained in my post. – Jdruwe Jun 30 '15 at 21:05
  • I thought you only have 1 Activity in your app. Do you now have 2 Activities? If that's what you want, that may be fine but I don't think you had to do that. – The Original Android Jun 30 '15 at 21:32
  • I had only one but I did not figure out how to do it with 1 activity, do you have a solution? – Jdruwe Jun 30 '15 at 21:33
  • There is a way to establish communication between fragments and/or activity, like I said previously. It is faster and safer I think. Do you want to redesign your app that way? It has been done by many developers, and recommended by Google. – The Original Android Jun 30 '15 at 21:36
  • If it looks the same as the 'fullscreen dialoog' it's fine by me, can you show me an example? – Jdruwe Jun 30 '15 at 21:38
  • I think it would be easier to help you if you provided some code. You do however provide some good details on your app. With code snippet, perhaps that would attract more attention, advice for next time. – The Original Android Jul 01 '15 at 01:50

4 Answers4

3

TL;DR - DialogFragment is insufficient for anything other than completely full-screen. Use an Activity instead.

It is possible to make a DialogFragment full-screen (with the ActionBar shown), but it comes with lots of irritations.

A DialogFragment is, as the name suggests, a Dialog and a Fragment rolled into one: it can be treated as both a Dialog, using show() and dismiss(), or as a Fragment, using it with a FragmentManager.

As the official documentation suggests, making a dialog completely full-screen (overlaying everything) is achieved by attaching the dialog to the root view android.R.id.content:

public void showDialog() {
    FragmentManager fragmentManager = getSupportFragmentManager();
    CustomDialogFragment newFragment = new CustomDialogFragment();

    if (mIsLargeLayout) {
        // The device is using a large layout, so show the fragment as a dialog
        newFragment.show(fragmentManager, "dialog");
    } else {
        // The device is smaller, so show the fragment fullscreen
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        // For a little polish, specify a transition animation
        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
        // To make it fullscreen, use the 'content' root view as the container
        // for the fragment, which is always the root view for the activity
        transaction.add(android.R.id.content, newFragment)
                   .addToBackStack(null).commit();
    }
}

To get the dialog to appear below the ActionBar, a FrameLayout is required which is used instead of the root layout:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer_layout"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    android:fitsSystemWindows="true">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <android.support.design.widget.CoordinatorLayout
            android:id="@+id/toolbar_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <!-- Use ThemeOverlay to make the toolbar and tablayout text
                 white -->
            <android.support.design.widget.AppBarLayout
                android:id="@+id/abl_top"
                android:layout_height="wrap_content"
                android:layout_width="match_parent"
                android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

                <android.support.v7.widget.Toolbar
                    android:id="@+id/toolbar"
                    android:fitsSystemWindows="true"
                    android:layout_height="wrap_content"
                    android:layout_width="match_parent"
                    app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
                    app:layout_scrollFlags="scroll|enterAlways"/>

            </android.support.design.widget.AppBarLayout>
        </android.support.design.widget.CoordinatorLayout>

        <FrameLayout
            android:id="@+id/content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>

    </LinearLayout>

    <android.support.design.widget.NavigationView
        android:id="@+id/nav_view"
        android:layout_height="match_parent"
        android:layout_width="wrap_content"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:headerLayout="@layout/nav_header"
        app:menu="@menu/nav_view"/>

</android.support.v4.widget.DrawerLayout>

Now comes the pain.

Depending on how the app's main navigation is setup, different hoops will need to be jumped through in order to get everything working perfectly.

The above example has a NavigationView. Since the home button android.R.id.home is handled in the main view, some logic is needed there to check if our dialog is shown so that the home button, which is now an X, will close the dialog. Returning false here allow the event to be handled in the dialog.

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home:
            FragmentManager fm = getSupportFragmentManager();
            Fragment f = fm.findFragmentById(R.id.content);
            if (f instanceof MyDialogFragment) {
                return false;
            }
            mDrawerLayout.openDrawer(GravityCompat.START);
            return true;
    }
    return super.onOptionsItemSelected(item);
}

Also, the back button needs similar logic to determine whether the NavigationView needs closing or the ActionBar content resetting.

@Override
public void onBackPressed() {
    if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
        mDrawerLayout.closeDrawer(GravityCompat.START);
    } else {
        FragmentManager fm = getSupportFragmentManager();
        Fragment f = fm.findFragmentById(R.id.content);
        if (f instanceof MyDialogFragment) {
            final ActionBar ab = getSupportActionBar();
            ab.setHomeAsUpIndicator(R.drawable.ic_menu);
            ab.setTitle(R.string.app_name);
        }
        super.onBackPressed();
    }
}

In the DialogFragment itself, the logic for closing the dialog (and abusing the ActionBar) needs to be implemented.

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home:
            if (mActionBar != null) {
                mActionBar.setHomeAsUpIndicator(R.drawable.ic_menu);
                mActionBar.setTitle(R.string.app_name);
            }
            getActivity().getSupportFragmentManager().popBackStack();
       case R.id.action_save:
            if (mOnAcceptListener != null) {
                mOnAcceptListener.onAccept();
            }
            if (mActionBar != null) {
                mActionBar.setHomeAsUpIndicator(R.drawable.ic_menu);
                mActionBar.setTitle(R.string.app_name);
            }
            getActivity().getSupportFragmentManager().popBackStack();
            return true;
    }
    return super.onOptionsItemSelected(item);
}

This alls feels really kludgy. Of course, if you're using a TabLayout, forget everything I've just said.

With a TabLayout you can just handle everything in the DialogFragment, but if you're using a ViewPager, it'll be impossible to get the dialog to cover the tabs but not the action bar. See Show DialogFragment over TabLayout.

That question (by me) has an answer that suggests the same as @Jdruwe, which is to forget the hopelessness of the DialogFragment and use an Activity instead.

Community
  • 1
  • 1
Jon
  • 9,815
  • 9
  • 46
  • 67
  • So you are saying that I should stick with the solution I described in: http://jeroendruwe.be/full-screen-dialogs-in-android/ ? – Jdruwe Jul 01 '15 at 11:42
  • 1
    I think so, yes. The DialogFragment route is too kludgy; though, maybe I'm not smart enough. – Jon Jul 01 '15 at 12:14
1

A solution described on my blog using startActivityForResult(...): http://jeroendruwe.be/full-screen-dialogs-in-android/

Jdruwe
  • 3,450
  • 6
  • 36
  • 57
0

add this line to oncreate in your custom dialog fragment.

setStyle(DialogFragment.STYLE_NORMAL, android.R.style.Theme_Black_NoTitleBar_Fullscreen);

On the other hand, you can use content resolvers to store your children datas. It has observer pattern. So each CursorAdapter attached to that content it refreshes itself without calling notifySetDataChanged();.

I think you are using RecyclerView.Adapter. You can use this class.

Another advice for implementing adding child feature is using startActivityForResult(activity);. You can send back datas by using getIntent().getExtras().put(key,value); You can search for custom start activity for result.

Good luck

Emre Aktürk
  • 3,306
  • 2
  • 18
  • 30
  • The setStyle does not work, this is the result if I use it: imgur.com/Zb5dtPP. And I don't really see how this could provide the 'up navigation' as in the guidelines. – Jdruwe Jun 06 '15 at 16:15
0

I don't see code from your post. So I am guessing your code structure as a start. First build your dialog with a listener and process setPositiveButton() and the onClick event.

Code suggestion:

public class ChildrenSpecificationFragment extends Fragment {
...

public void passData(Object obj) {
}

   class SubChildFragment extends Fragment {
       AlertDialog.Builder builder = new AlertDialog.Builder(thisContext);
       ...
       // Add the buttons...
       builder.setPositiveButton("Save", new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
           ...
           passData(Object obj);   // pass data to the outer fragment class

Notes:

  • SubChildFragment, for example, is an inner class derived from Fragment. It can call the public method passData() in the outer class ChildrenSpecificationFragment for passing any data you need.
  • I am using an inner class because I think this is what you meant in your diagram by

Add child full-screen fragment

  • This coding technique is easier than starting a new Activity and Intent.

For showing fullscreen dialogs, there is a good Google webpage I think @ Dialog - Fullscreen. Search text for "Showing a Dialog Fullscreen or as an Embedded Fragment".

The Original Android
  • 6,147
  • 3
  • 26
  • 31
  • But does this show the content of the layout in fullscreen as the example by google: https://material-design.storage.googleapis.com/publish/material_v_4/material_ext_publish/0B6Okdz75tqQsTXVxUFJQdy15ZzA/components_dialogs_fullscreen1.png ? – Jdruwe Jul 01 '15 at 06:15
  • 1
    Because this was how I originally had things, a fragment extending dialogfragment and calling a by the parent implemented interface method. – Jdruwe Jul 01 '15 at 06:16
  • @Jdruwe, The fragment, SubChildFragment in my sample, can be the dialogFragment, and it can show the fullscreen. I think the dialog can be fullscreen by the layout specifications. The data passing is separate from the GUI layout. There is a link http://developer.android.com/guide/topics/ui/dialogs.html , in case you did not read this one. I thought you have the fullscreen issue solved already. – The Original Android Jul 01 '15 at 07:04
  • @Jdruwe, I add more comments on the bottom about "fullscreen dialogs". – The Original Android Jul 01 '15 at 07:08
  • Thanks for your solution! :D How do you compare it to my solution described in: http://jeroendruwe.be/full-screen-dialogs-in-android/ ? – Jdruwe Jul 01 '15 at 11:41
  • @Jdruwe, It's not easy to compare both code designs, mainly because your link shows partial code. Besides that, I like to use startActivity() only for launching other apps. So I avoid using it within only a specific app. Another thing, you may upvote some answers if you think they are useful. And I notice some detailed answers that seem useful. – The Original Android Jul 01 '15 at 17:30