मैं संवाद में इमर्सिव मोड कैसे बनाए रख सकता हूं?


104

जब मेरी गतिविधियाँ एक कस्टम डायलॉग प्रदर्शित करती हैं तो मैं नए इमर्सिव मोड को कैसे बनाए रख सकता हूँ?

मैं डायलॉग में इमर्सिव मोड को बनाए रखने के लिए नीचे दिए गए कोड का उपयोग कर रहा हूं, लेकिन उस समाधान के साथ, नवबार एक सेकंड से भी कम समय के लिए प्रकट होता है जब मैं अपना कस्टम डायलॉग शुरू करता हूं, तब यह गायब हो जाता है।

निम्नलिखित वीडियो इस मुद्दे को बेहतर ढंग से समझाता है (स्क्रीन के निचले भाग पर देखें जब नवबर दिखाई देता है): http://youtu.be/epnd5ghey8g

मैं इस व्यवहार से कैसे बचूँ?

कोड

मेरे आवेदन में सभी गतिविधियों के पिता:

public abstract class ImmersiveActivity extends Activity {

    @SuppressLint("NewApi")
    private void disableImmersiveMode() {
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
            getWindow().getDecorView().setSystemUiVisibility(
                    View.SYSTEM_UI_FLAG_FULLSCREEN
            );
        }
    }

    @SuppressLint("NewApi")
    private void enableImmersiveMode() {
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
            getWindow().getDecorView().setSystemUiVisibility(
                      View.SYSTEM_UI_FLAG_LAYOUT_STABLE 
                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 
                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 
                    | View.SYSTEM_UI_FLAG_FULLSCREEN 
                    | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY 
                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
            );
        }
    }


    /**
     * Set the Immersive mode or not according to its state in the settings:
     * enabled or not.
     */
    protected void updateSystemUiVisibility() {
        // Retrieve if the Immersive mode is enabled or not.
        boolean enabled = getSharedPreferences(Util.PREF_NAME, 0).getBoolean(
                "immersive_mode_enabled", true);

        if (enabled) enableImmersiveMode();
        else disableImmersiveMode();
    }

    @Override
    public void onResume() {
        super.onResume();
        updateSystemUiVisibility();
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        updateSystemUiVisibility();
    }

}


मेरे सभी कस्टम डायलॉग इस विधि को अपनी onCreate(. . .)विधि में कहते हैं:

/**
 * Copy the visibility of the Activity that has started the dialog {@link mActivity}. If the
 * activity is in Immersive mode the dialog will be in Immersive mode too and vice versa.
 */
@SuppressLint("NewApi")
private void copySystemUiVisibility() {
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
        getWindow().getDecorView().setSystemUiVisibility(
                mActivity.getWindow().getDecorView().getSystemUiVisibility()
        );
    }
}


संपादित करें - समाधान (बीवर 6813 के लिए धन्यवाद, अधिक विवरण के लिए उसका उत्तर देखें):

मेरे सभी कस्टम संवाद इस तरह से शो विधि को ओवरराइड करते हैं:

/**
 * An hack used to show the dialogs in Immersive Mode (that is with the NavBar hidden). To
 * obtain this, the method makes the dialog not focusable before showing it, change the UI
 * visibility of the window like the owner activity of the dialog and then (after showing it)
 * makes the dialog focusable again.
 */
@Override
public void show() {
    // Set the dialog to not focusable.
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);

    copySystemUiVisibility();

    // Show the dialog with NavBar hidden.
    super.show();

    // Set the dialog to focusable again.
    getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
}

आप संवाद कैसे दिखाते हैं? क्या आप DialogFragments का उपयोग करते हैं?
ताड़ीस क्रिज़

मैं DialogFragments का उपयोग नहीं करता, लेकिन कस्टम डायलॉग उपवर्ग। developer.android.com/reference/android/app/Dialog.html मैं केवल उनके शो () विधि को कॉल करके संवाद दिखाता हूं।
वनदिर

जब संवाद दिखाई देता है onWindowFocusChanged कहा जा रहा है। संवाद दिखाई देने पर सक्षम का मूल्य क्या है? क्या यह सच है या कुछ गलत हुआ और क्या यह गलत है?
केविन वैन मिर्लो

क्या आपका मतलब है डायलॉग क्लास का onWindowFocusChanged (बूलियन हैफोकस) तरीका (और एक्टिविटी क्लास का नहीं)? इस मामले में "hasFocus" ध्वज सत्य है।
वनदिर

5
क्या किसी ने डायलॉगफ्रैगमेंट के साथ इमर्सिव मोड का इस्तेमाल किया?
गेरो जूल

जवाबों:


167

इस मुद्दे पर काफी शोध के बाद इसके लिए एक हैकी फिक्स है, जिसे खोजने के लिए डायलॉग क्लास के साथ छेड़छाड़ शामिल है । नेविगेशन बार तब दिखाया जाता है जब संवाद विंडो को विंडो प्रबंधक में जोड़ा जाता है, भले ही आपने प्रबंधक को जोड़ने से पहले यूआई दृश्यता निर्धारित की हो। में एंड्रॉयड इमर्सिव उदाहरण यह है कि टिप्पणी की है:

// * Uses semi-transparent bars for the nav and status bars
// * This UI flag will *not* be cleared when the user interacts with the UI.
// When the user swipes, the bars will temporarily appear for a few seconds and then
// disappear again.

मुझे विश्वास है कि हम यहाँ क्या देख रहे हैं (जब एक नया, फ़ोकस करने योग्य, विंडो दृश्य प्रबंधक में जोड़ा जाता है) तो उपयोगकर्ता-इंटरैक्शन ट्रिगर हो रहा है।

हम इसके आसपास कैसे काम कर सकते हैं? जब हम इसे बनाते हैं तो डायलॉग को ध्यान केंद्रित न करें (इसलिए हम उपयोगकर्ता-इंटरैक्शन को ट्रिगर नहीं करते हैं) और इसके प्रदर्शित होने के बाद इसे फ़ोकस करने योग्य बनाते हैं।

//Here's the magic..
//Set the dialog to not focusable (makes navigation ignore us adding the window)
dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);

//Show the dialog!
dialog.show();

//Set the dialog to immersive
dialog.getWindow().getDecorView().setSystemUiVisibility(
context.getWindow().getDecorView().getSystemUiVisibility());

//Clear the not focusable flag from the window
dialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);

स्पष्ट रूप से यह आदर्श नहीं है, लेकिन यह एक एंड्रॉइड बग प्रतीत होता है, उन्हें जांचना चाहिए कि क्या विंडो में इमर्सिव सेट है या नहीं।

मैं अपने काम के परीक्षण कोड (hacky messiness माफ कर) को नवीनीकृत किया है Github । मैंने नेक्सस 5 एमुलेटर पर परीक्षण किया है, यह संभवतः किटकैट से कम के साथ उड़ाएगा, लेकिन इसका केवल प्रूफ-ऑफ-कॉन्सेप्ट के लिए।


3
नमूना परियोजना के लिए धन्यवाद, लेकिन यह काम नहीं किया। देखो मेरे नेक्सस 5 में क्या होता है: youtu.be/-abj3V_0zcU
VanDir

मैंने इस मुद्दे पर कुछ जांच के बाद अपना जवाब अपडेट किया है, यह एक कठिन था! नेक्सस 5 एमुलेटर पर परीक्षण किया गया, उम्मीद है कि यह काम करेगा।
बीवर 6813

2
मुझे 'requestFeature' () को सामग्री जोड़ने से पहले 'कहा जाना चाहिए' (मुझे लगता है कि यह गतिविधि पर सक्रिय सुविधाओं पर निर्भर करता है)। समाधान: डायलॉग.शो () एक लाइन अप शो () को सिस्टमUiVisibility (लेकिन गैर-फ़ोकस करने योग्य स्थापित करने के बाद) कॉपी करने से पहले ले जाया जाता है। फिर यह नौसैनिकों के कूदने के बिना काम करता है।
अर्गन

5
@ Beaver6813 यह तब काम नहीं करता है जब EditText का उपयोग किया जाता है और DialogFragment में सॉफ्टकीबोर्ड दिखाई देता है। क्या आपके पास इसे संभालने के लिए नाय सुझाव हैं।
androidcodehunter

1
हाय बीवर ६13१३। यह तब काम नहीं करता जब मेरे पास संवाद में एक संपादित पाठ होता है। क्या कोई उपाय है ??
नवीन गुप्ता

33

आपकी जानकारी के लिए, @ Beaver6813 के उत्तर के लिए धन्यवाद, मैं DialogFragment का उपयोग करके यह काम पाने में सक्षम हूं।

मेरे DialogFragment के onCreateView मेथड में, मैंने अभी निम्नलिखित जोड़े हैं:

    getDialog().getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
    getDialog().getWindow().getDecorView().setSystemUiVisibility(getActivity().getWindow().getDecorView().getSystemUiVisibility());

    getDialog().setOnShowListener(new DialogInterface.OnShowListener() {
        @Override
        public void onShow(DialogInterface dialog) {
            //Clear the not focusable flag from the window
            getDialog().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);

            //Update the WindowManager with the new attributes (no nicer way I know of to do this)..
            WindowManager wm = (WindowManager) getActivity().getSystemService(Context.WINDOW_SERVICE);
            wm.updateViewLayout(getDialog().getWindow().getDecorView(), getDialog().getWindow().getAttributes());
        }
    });

5
क्या यह onCreateDialog () में बिल्डर पैटर्न के साथ काम करता है? मेरे डायलॉगफ्रैगमेंट के साथ काम करने के लिए मुझे अभी तक यह हैक नहीं मिला है, डायलॉग मेरे ऐप को "requestFeature () को कंटेंट जोड़ने से पहले कॉल करना चाहिए"
IAmKale

1
यह शानदार है और डायलॉगफ्रैगमेंट के मेरे उपयोग का एकमात्र समाधान था। बहुत बहुत धन्यवाद।
14:22 पर se22as

महान! यह पूरी तरह से काम करता है। इतनी सारी चीजों की कोशिश की, अब परिपूर्ण जारी है immersive मोड।
पीटरडक

वाह, एक आकर्षण की तरह काम कर रहा है। धन्यवाद
अब्दुल

16

यदि आप onCreateDialog () का उपयोग करना चाहते हैं , तो इस वर्ग का प्रयास करें। यह मेरे लिए बहुत अच्छा काम करता है ...

public class ImmersiveDialogFragment extends DialogFragment {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {

        AlertDialog alertDialog = new AlertDialog.Builder(getActivity())
                .setTitle("Example Dialog")
                .setMessage("Some text.")
                .create();

        // Temporarily set the dialogs window to not focusable to prevent the short
        // popup of the navigation bar.
        alertDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);

        return alertDialog;

    }

    public void showImmersive(Activity activity) {

        // Show the dialog.
        show(activity.getFragmentManager(), null);

        // It is necessary to call executePendingTransactions() on the FragmentManager
        // before hiding the navigation bar, because otherwise getWindow() would raise a
        // NullPointerException since the window was not yet created.
        getFragmentManager().executePendingTransactions();

        // Hide the navigation bar. It is important to do this after show() was called.
        // If we would do this in onCreateDialog(), we would get a requestFeature()
        // error.
        getDialog().getWindow().getDecorView().setSystemUiVisibility(
            getActivity().getWindow().getDecorView().getSystemUiVisibility()
        );

        // Make the dialogs window focusable again.
        getDialog().getWindow().clearFlags(
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
        );

    }

}

संवाद दिखाने के लिए, अपनी गतिविधि में निम्नलिखित करें ...

new ImmersiveDialogFragment().showImmersive(this);

जब ActionBar मेनू दिखाया जाता है तो नवबारों को प्रदर्शित होने से कैसे रोका जाए?
विलियम

मुझे अभी भी NPE मिलता है, क्योंकि getDialog () रिटर्न शून्य है। आपने इसे किस तरह से मैनेज़ किया?
एंटोन शुकेंको

8

यहाँ उत्तरों को मिलाकर मैंने एक सार वर्ग बनाया जो सभी मामलों में काम करता है:

public abstract class ImmersiveDialogFragment extends DialogFragment {

    @Override
    public void setupDialog(Dialog dialog, int style) {
        super.setupDialog(dialog, style);

        // Make the dialog non-focusable before showing it
        dialog.getWindow().setFlags(
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
    }

    @Override
    public void show(FragmentManager manager, String tag) {
        super.show(manager, tag);
        showImmersive(manager);
    }

    @Override
    public int show(FragmentTransaction transaction, String tag) {
        int result = super.show(transaction, tag);
        showImmersive(getFragmentManager());
        return result;
    }

    private void showImmersive(FragmentManager manager) {
        // It is necessary to call executePendingTransactions() on the FragmentManager
        // before hiding the navigation bar, because otherwise getWindow() would raise a
        // NullPointerException since the window was not yet created.
        manager.executePendingTransactions();

        // Copy flags from the activity, assuming it's fullscreen.
        // It is important to do this after show() was called. If we would do this in onCreateDialog(),
        // we would get a requestFeature() error.
        getDialog().getWindow().getDecorView().setSystemUiVisibility(
                getActivity().getWindow().getDecorView().getSystemUiVisibility()
        );

        // Make the dialogs window focusable again
        getDialog().getWindow().clearFlags(
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
        );
    }
}

के बाद से androidx, setupDialog विधि एक प्रतिबंधित एपीआई बन गया है, हम इसे कैसे अनदेखी सिवाय इसके कि के साथ सौदा कर सकते हैं?
मिंगकांग पैन

5

यह आपके संवाद खंड के onDismiss विधि की सवारी पर भी काम करता है। और उस पद्धति के भीतर उस गतिविधि की विधि को कहते हैं जिससे वह फिर से पूर्ण स्क्रीन झंडे सेट करने के लिए संलग्न होती है।

@Override
    public void onDismiss(DialogInterface dialog) {
        super.onDismiss(dialog);
        Logger.e(TAG, "onDismiss");
        Log.e("CallBack", "CallBack");
        if (getActivity() != null &&
                getActivity() instanceof LiveStreamingActivity) {
            ((YourActivity) getActivity()).hideSystemUI();
        }
    }

और अपनी गतिविधि में इस विधि को जोड़ें:

public void hideSystemUI() {
        // Set the IMMERSIVE flag.
        // Set the content to appear under the system bars so that the content
        // doesn't resize when the system bars hide and show.
        getWindow().getDecorView().setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar
                        | View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar
                        | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
    }

1
सबसे अधिक उपयोगी टिप्पणी, इसके साथ अच्छा काम करता है के रूप में AlertDialog.Builderऔर.setOnDismissListener()
Tomash

4

आप अपना खुद का डायलॉगफ्रैगमेंट बना रहे हैं जिसे आपको केवल इस विधि को ओवरराइड करने की आवश्यकता है।

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    Dialog dialog = super.onCreateDialog(savedInstanceState);

    dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);

    return dialog;
}

वास्तव में आदर्श के रूप में आप संवाद के अंदर प्रेस कुछ भी करने के लिए :( सकेंगे
slott

इसके प्रयोग से मेरे संवाद अनुत्तरदायी हो जाते हैं। लेकिन अगर आप जोर देकर कहते हैं कि यह काम करता है तो मैं इसे एक और रास्ता दूंगा।
slott

@ मुझे WindowManager.LayoutParams.FLAG_NOT_FOCUSABLEदिखाए गए संवाद के बाद खाली करने की आवश्यकता है
4emodan

2

मुझे पता है कि यह पुरानी पोस्ट है, लेकिन मेरा जवाब दूसरों की मदद कर सकता है।

नीचे संवाद में इमर्सिव प्रभाव के लिए हैकी फिक्स है:

public static void showImmersiveDialog(final Dialog mDialog, final Activity mActivity) {
        //Set the dialog to not focusable
        mDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
        mDialog.getWindow().getDecorView().setSystemUiVisibility(setSystemUiVisibility());

        mDialog.setOnShowListener(new DialogInterface.OnShowListener() {
            @Override
            public void onShow(DialogInterface dialog) {
                //Clear the not focusable flag from the window
                mDialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);

                //Update the WindowManager with the new attributes
                WindowManager wm = (WindowManager) mActivity.getSystemService(Context.WINDOW_SERVICE);
                wm.updateViewLayout(mDialog.getWindow().getDecorView(), mDialog.getWindow().getAttributes());
            }
        });

        mDialog.getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
            @Override
            public void onSystemUiVisibilityChange(int visibility) {
                if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
                    mDialog.getWindow().getDecorView().setSystemUiVisibility(setSystemUiVisibility());
                }

            }
        });
    }

    public static int setSystemUiVisibility() {
        return View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_FULLSCREEN
                | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
    }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.