İletişim Kutularında Sürükleyici Modu nasıl sürdürürüm?


104

Etkinliklerim özel bir İletişim Kutusu görüntülediğinde yeni Sürükleyici Modu nasıl sürdürürüm?

İletişim Kutularında Sürükleyici Modu korumak için aşağıdaki kodu kullanıyorum, ancak bu çözümle, NavBar özel İletişim Kutumu başlattığımda bir saniyeden daha kısa bir süre görünüyor ve sonra kayboluyor.

Aşağıdaki video sorunu daha iyi açıklıyor (NavBar göründüğünde ekranın altına bakın): http://youtu.be/epnd5ghey8g

Bu davranıştan nasıl kaçınırım?

KOD

Başvurumdaki tüm faaliyetlerin babası:

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

}


Tüm özel iletişim kutularım, onCreate(. . .)yöntemlerinde bu yöntemi çağırır :

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


DÜZENLEME - ÇÖZÜM (Beaver6813 sayesinde, daha fazla ayrıntı için cevabına bakın):

Tüm özel iletişim kutularım, şu şekilde göster yöntemini geçersiz kılar:

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

Diyalogları nasıl gösterirsiniz? DialogFragments kullanıyor musunuz?
Tadeas Kriz

DialogFragments kullanmıyorum ama özel Dialog alt sınıfları kullanıyorum. developer.android.com/reference/android/app/Dialog.html Diyalogları sadece show () yöntemlerini çağırarak gösteriyorum.
VanDir

İletişim kutusu onWindowFocusChanged'da göründüğünde çağrılıyor. İletişim kutusu göründüğünde etkinleştirilmesinin değeri nedir? Doğru mu yoksa bir şeyler ters gitti mi ve yanlış mı?
Kevin van Mierlo

Dialog sınıfının (Activity sınıfının değil) onWindowFocusChanged (boolean hasFocus) yöntemini mi kastediyorsunuz? Bu durumda "hasFocus" bayrağı doğrudur.
VanDir

5
Diyalog parçalarıyla sürükleyici modu kullanan oldu mu?
gbero

Yanıtlar:


167

Sorunla ilgili çok fazla araştırmadan sonra, bunun için, bulmak için Dialog sınıfını parçalara ayırmayı içeren karmaşık bir düzeltme var . Yöneticiye eklemeden önce kullanıcı arabirimi görünürlüğünü ayarlasanız bile iletişim penceresi Pencere Yöneticisine eklendiğinde gezinti çubuğu gösterilir. In Android Sürükleyici Örneğin bu yorum yazdığını:

// * 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.

Sanırım burada gördüğümüz şey bu (yöneticiye yeni, odaklanılabilir bir pencere görünümü eklendiğinde bir kullanıcı etkileşimi tetikleniyor).

Bunu nasıl aşabiliriz? Diyaloğu oluşturduğumuzda odaklanamaz hale getirin (böylece bir kullanıcı etkileşimini tetiklemeyiz) ve ardından görüntülendikten sonra odaklanabilir hale getirin.

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

Açıkçası bu ideal değil, ancak bir Android hatası gibi görünüyor, Pencerenin sürükleyici ayarı olup olmadığını kontrol etmeleri gerekiyor.

Çalışma testi kodumu (karışıklığı affet) Github'a güncelledim . Nexus 5 emülatöründe test ettim, muhtemelen KitKat'tan daha az bir şeyle patlayacak, ancak sadece konsept kanıtı için.


3
Örnek proje için teşekkürler ama işe yaramadı. Nexus 5'imde
VanDir

Soruyla ilgili biraz araştırma yaptıktan sonra cevabımı güncelledim, zor bir soruydu! Nexus 5 emülatöründe test edildi, umarım bu çalışır.
Beaver6813

2
'RequestFeature () içerik eklemeden önce çağrılmalıdır' mesajı alıyorum (sanırım etkinliğe bağlı aktif özelliklere bağlıdır). Çözüm: dialog.show () öğesini bir satır yukarı taşıyın, böylece show () SystemUiVisibility kopyalanmadan önce çağrılır (ancak odaklanılamaz ayarlandıktan sonra). O zaman gezinme çubuğunu atlamadan hala çalışır.
arberg

5
@ Beaver6813 EditText kullanıldığında ve softkeyboard DialogFragment içinde göründüğünde çalışmaz. Bunu halletmek için herhangi bir öneriniz var mı?
androidcodehunter

1
Merhaba Beaver6813. İletişim kutusunda bir düzenleme metnim olduğunda çalışmıyor. Herhangi bir çözüm var mı?
Navin Gupta

33

Bilginiz olsun, @ Beaver6813'ün cevabı sayesinde bunu DialogFragment kullanarak çalıştırabildim.

DialogFragment'ımın onCreateView yönteminde, aşağıdakileri ekledim:

    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
Bu, onCreateDialog () içindeki Builder kalıbı ile çalışır mı? Bu hack'i DialogFragments'ımla çalışacak hale getirmedim, iletişim kutusu uygulamamı "requestFeature () içerik eklemeden önce çağrılmalıdır" ile
kilitliyor

1
Bu harika ve DialogFragments kullanımım için tek çözümdü. Çok teşekkür ederim.
se22as

Harika! Mükemmel çalışıyor. Çok şey denedim, şimdi mükemmel, sürükleyici modda devam ediyor.
Peterdk

16

OnCreateDialog () kullanmak istiyorsanız , bu sınıfı deneyin. Benim için oldukça iyi çalışıyor ...

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

    }

}

Diyaloğu göstermek için aktivitenizde aşağıdakileri yapın ...

new ImmersiveDialogFragment().showImmersive(this);

Bir ActionBar menüsü gösterildiğinde NavBar'ın görüntülenmesini nasıl durduracağınıza dair bir fikriniz var mı?
William

GetDialog () null döndürdüğü için hala NPE alıyorum. Bunu nasıl başardın?
Anton Shkurenko

8

Buradaki cevapları birleştirerek her durumda işe yarayan soyut bir sınıf yaptım:

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 yöntemi kısıtlı bir api haline geldiğinden, görmezden gelmek dışında bununla nasıl başa çıkabiliriz?
Mingkang Pan

5

Bu aynı zamanda, diyalog parçanızın onDismiss yönteminin üzerinde çalışır. Ve bu yöntem içinde, tam ekran bayraklarını yeniden ayarlamak için eklendiği etkinliğin yöntemini çağırın.

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

Ve aktivitenize şu yöntemi ekleyin:

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
En yararlı yorum, onunla kusursuz çalışması gibi AlertDialog.Builderve.setOnDismissListener()
Tomash

4

Kendi DialogFragment'ınızı oluştururken sadece bu yöntemi geçersiz kılmanız gerekir.

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

İletişim kutusundaki herhangi bir şeye basamayacağınız için gerçekten ideal değil :(
slott

Bunu kullanarak diyaloglarım yanıt vermiyor. Ama işe yaradığında ısrar ederseniz, bir kez daha denerim.
slott,

@slott WindowManager.LayoutParams.FLAG_NOT_FOCUSABLEgösterilen sonraki iletişim kutusunu temizlemeniz gerekiyor
4emodan

2

Bunun eski bir gönderi olduğunu biliyorum, ancak cevabım başkalarına yardımcı olabilir.

Aşağıda, Diyaloglarda Sürükleyici efekt için hacky düzeltmesi verilmiştir:

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;
    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.