Android full-screen dialog callback issue

前端 未结 4 2095
暖寄归人
暖寄归人 2020-12-28 21:46

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

\"flow\"

I ha

相关标签:
4条回答
  • 2020-12-28 22:27

    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.

    0 讨论(0)
  • 2020-12-28 22:32

    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".

    0 讨论(0)
  • 2020-12-28 22:35

    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

    0 讨论(0)
  • 2020-12-28 22:36

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

    0 讨论(0)
提交回复
热议问题