Android'de ekran döndürmede iletişim kutusunun kapatılmasını önleyin


94

Uyarı oluşturucu ile oluşturulmuş iletişim kutularının Etkinlik yeniden başlatıldığında kapatılmasını önlemeye çalışıyorum.

OnConfigurationChanged yöntemini aşırı yüklersem, bunu başarıyla yapabilir ve düzeni düzeltmek için düzeni sıfırlayabilirim ancak edittext'in yapışkan metin özelliğini kaybediyorum. Bu yüzden diyalog problemini çözerken bu edittext problemini yarattım.

Dizeleri edittext'den kaydedersem ve onları onCofiguration değişikliğinde yeniden atarsam, rotasyondan önce girilen değeri değil, başlangıç ​​değerine hala varsayılan olarak görünürler. Geçersiz kılmaya zorlasam bile onları güncelliyor gibi görünüyor.

Ya diyalog problemini ya da edittext problemini gerçekten çözmem gerekiyor.

Yardım için teşekkürler.


1
Düzenlenen EditText içeriğini nasıl kaydeder / geri yüklersiniz? Biraz kod gösterebilir misin?
Peter Knego

Bununla ilgili sorunu anladım, düzeni sıfırladıktan sonra Id ile görünümü tekrar almayı unutuyordum.
draksia

Yanıtlar:


134

Günümüzde bu sorunu önlemenin en iyi yolu bir DialogFragment.

Genişleyen yeni bir sınıf oluşturun DialogFragment. Geçersiz kılın onCreateDialogve eski Dialogveya bir AlertDialog.

O zaman ile gösterebilirsiniz DialogFragment.show(fragmentManager, tag).

İşte bir örnek Listener:

public class MyDialogFragment extends DialogFragment {

    public interface YesNoListener {
        void onYes();

        void onNo();
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        if (!(activity instanceof YesNoListener)) {
            throw new ClassCastException(activity.toString() + " must implement YesNoListener");
        }
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return new AlertDialog.Builder(getActivity())
                .setTitle(R.string.dialog_my_title)
                .setMessage(R.string.dialog_my_message)
                .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {

                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        ((YesNoListener) getActivity()).onYes();
                    }
                })
                .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {

                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        ((YesNoListener) getActivity()).onNo();
                    }
                })
                .create();
    }
}

Ve aradığınız Faaliyette:

new MyDialogFragment().show(getSupportFragmentManager(), "tag"); // or getFragmentManager() in API 11+

Bu cevap, diğer üç soruyu (ve cevaplarını) açıklamaya yardımcı olur:


Uygulamamda .show () 'u çağırmak için bir düğme var, uyarı iletişim kutusunun durumunu hatırlamam gerekiyor, gösterilen / reddedildi. .Show () çağırmadan diyaloğu tutmanın bir yolu var mı?
Alpha Huang

1
Artık onAttachkullanımdan kaldırıldığını söylüyor . Bunun yerine ne yapılmalı?
farahm

3
@faraz_ahmed_kamran, kullanmalısınız onAttach(Context context)ve android.support.v4.app.DialogFragment. onAttachYöntem alır contextyerine activityşimdi bir parametre.
Stan Mots

Muhtemelen buna gerek yok YesNoListener. Bu cevaba bakın .
Mygod

46
// Prevent dialog dismiss when orientation changes
private static void doKeepDialog(Dialog dialog){
    WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
    lp.copyFrom(dialog.getWindow().getAttributes());
    lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
    lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
    dialog.getWindow().setAttributes(lp);
}
public static void doLogout(final Context context){     
        final AlertDialog dialog = new AlertDialog.Builder(context)
        .setIcon(android.R.drawable.ic_dialog_alert)
        .setTitle(R.string.titlelogout)
        .setMessage(R.string.logoutconfirm)
        .setPositiveButton("Yes", new DialogInterface.OnClickListener()
        {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                ...   
            }

        })
        .setNegativeButton("No", null)      
        .show();    

        doKeepDialog(dialog);
    }

1
Kodumu yararlı
Chung IW

6
Nasıl çalışıyor bilmiyorum ama işe yarıyor! Temiz, basit ve soyut bir çözüm, teşekkürler.
nmvictor

3
Bu kodun kötü olduğunu düşünüyorum. doLogout (), aktiviteyi içeren / içeren bağlama bir referansa sahiptir. Etkinlik yok edilemez, bu da bellek sızıntısına neden olabilir. AlertDialog'u statik bağlamdan kullanma olasılığını arıyordum ama şimdi bunun imkansız olduğundan eminim. Sonuç sadece bence çöp olabilir.
İnanılmaz Ocak

2
İşe yarıyor gibi görünüyor. İletişim kutusu açık kalır, ancak yeni oluşturulan etkinlik veya parça ile bağlantısı yoktur (her yön değişikliğinde yeni oluşturulur). Bu nedenle Context, iletişim düğmelerinin içini gerektiren hiçbir şey yapamazsınız OnClickListener.
Udo Klimaschewski

2
Bu kod işe yarıyor ancak hiç tavsiye edilmiyor. Etkinlik referansını sızdırıyor, bu nedenle iletişim kutusu kalıcı olabilir. Bu, bellek sızıntısına yol açacak çok kötü bir uygulamadır.
Hexise

4

Oryantasyon değişikliğinde düzeni değiştiriyorsanız android:configChanges="orientation", manifestinize koymam çünkü yine de görünümleri yeniden oluşturuyorsunuz.

Aşağıdaki yöntemleri kullanarak faaliyetinizin mevcut durumunu (girilen metin, gösterilen iletişim kutusu, görüntülenen veriler vb.) Kaydedin:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
}

Bu şekilde etkinlik onCreate'den tekrar geçer ve ardından EditText değerinizi yeniden ayarlayabileceğiniz onRestoreInstanceState yöntemini çağırır.

Daha karmaşık Nesneleri depolamak istiyorsanız kullanabilirsiniz.

@Override
public Object onRetainNonConfigurationInstance() {
}

Burada herhangi bir nesneyi saklayabilirsiniz ve onCreate'te getLastNonConfigurationInstance();Object'i almak için aramanız yeterlidir .


4
OnRetainNonConfigurationInstance()Şimdi Doküman olarak kullanım dışı kalmıştır diyor ki: developer.android.com/reference/android/app/... setRetainInstance(boolean retain) yerine kullanımını olmalıdır: developer.android.com/reference/android/app/...
ForceMagic

@ForceMagic setRetainInstancetamamen farklıdır: Fragments içindir ve örneğin korunacağını garanti etmez.
Miha_x64

3

AndroidManifest.xml'deki etkinlik öğenizle birlikte android: configChanges = "oryantasyon" eklemeniz yeterlidir.

Misal:

<activity
            android:name=".YourActivity"
            android:configChanges="orientation"
            android:label="@string/app_name"></activity>

Bu, iletişim kutusunun bazı durumlarda yanlış görüntülenmesine neden olabilir.
Sam

1
android: configChanges = "orientation | screenSize" Not: Uygulamanız Android 3.2 (API düzeyi 13) veya üstünü hedefliyorsa, bir cihaz dikey ve yatay yönler arasında geçiş yaptığında da değiştiği için "screenSize" yapılandırmasını da bildirmelisiniz.
tsig

1

Yöntemden diyaloglar oluşturmak çok kolay bir yaklaşımdır onCreateDialog()(aşağıdaki nota bakın). Onlara yol göster showDialog(). Bu şekilde, Android sizin için dönmesini kolları ve aramak gerekmez dismiss()içinde onPause()bir WindowLeak önlemek ve sonra iletişim kutusunu geri yüklemek zorunda ne kadar. Dokümanlardan:

Bu aktivite tarafından yönetilen bir diyalog göster. Belirli bir id için ilk çağrıldığında aynı id ile onCreateDialog'a (int, Bundle) bir çağrı yapılacaktır. Bundan sonra, iletişim kutusu otomatik olarak kaydedilecek ve geri yüklenecektir.

Daha fazla bilgi için Android belgelerine bakın showDialog () . Umarım birine yardımcı olur!

Not: AlertDialog.Builder kullanılıyorsa, demiyorlar show()gelen onCreateDialog(), çağrı create()yerine. ProgressDialog kullanıyorsanız, sadece nesneyi oluşturun, ihtiyacınız olan parametreleri ayarlayın ve geri döndürün. Sonuç olarak, show()içeride onCreateDialog()sorunlara neden olur, sadece bir de Dialog örneği oluşturun ve geri gönderin. Bu çalışmalı! (OnCreate () 'den showDialog ()' u kullanırken -aslında iletişim kutusunu göstermeyen- sorunlar yaşadım, ancak onu onResume () veya bir dinleyici geri aramasında kullanırsanız, iyi çalışıyor).


Hangi durumda bir koda ihtiyacınız var? OnCreateDialog () veya oluşturucu ile birlikte gösteriliyor ve show () 'a mı çağırılıyor?
Caumons

Bunu yapmayı başardım .. ama sorun şu ki, onCreateDialog () artık kullanımdan kaldırıldı: - \
neteinstein

TAMAM! Android cihazların çoğunun hala 2.X sürümleriyle çalıştığını unutmayın, böylece yine de kullanabilirsiniz! Android platform sürümlerinin kullanımına
Caumons

Yine de, onCreateDialog değilse diğer seçenek nedir?
neteinstein

1
Oluşturucu sınıflarını örneğin kullanabilirsiniz AlertDialog.Builder. İçeride onCreateDialog()kullanırsanız show(), sonucunu döndürmek yerine create(). Aksi takdirde, bir WindowLeak'ten kaçınmak show()için döndürülen AlertDialog'u Aktivitenin bir özniteliğine çağırın ve saklayın onPause() dismiss(). Umarım yardımcı olur!
Caumons

1

Bu soru uzun zaman önce cevaplandı.

Yine de bu, kendim için kullandığım hack olmayan ve basit bir çözüm.

Bu yardımcı sınıfı kendim için yaptım , böylece uygulamanızda da kullanabilirsiniz.

Kullanım:

PersistentDialogFragment.newInstance(
        getBaseContext(),
        RC_REQUEST_CODE,
        R.string.message_text,
        R.string.positive_btn_text,
        R.string.negative_btn_text)
        .show(getSupportFragmentManager(), PersistentDialogFragment.TAG);

Veya

 PersistentDialogFragment.newInstance(
        getBaseContext(),
        RC_EXPLAIN_LOCATION,
        "Dialog title", 
        "Dialog Message", 
        "Positive Button", 
        "Negative Button", 
        false)
    .show(getSupportFragmentManager(), PersistentDialogFragment.TAG);





public class ExampleActivity extends Activity implements PersistentDialogListener{

        @Override
        void onDialogPositiveClicked(int requestCode) {
                switch(requestCode) {
                  case RC_REQUEST_CODE:
                  break;
                }
        }

        @Override
        void onDialogNegativeClicked(int requestCode) {
                switch(requestCode) {
                  case RC_REQUEST_CODE:
                  break;
                }          
        }
}

1

Dialog'un durumunu korumak için Dialog'un onSave / onRestore yöntemlerini Activity'nin onSave / onRestore yöntemleriyle birleştirebilirsiniz .

Not: Bu yöntem, bir uyarı mesajı görüntülemek gibi "basit" Diyaloglar için çalışır. Bir İletişim Kutusuna gömülü bir Web Görünümü içeriğini yeniden üretmez. Rotasyon sırasında karmaşık bir iletişim kutusunun kapatılmasını gerçekten önlemek istiyorsanız, Chung IW'nin yöntemini deneyin.

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
     super.onRestoreInstanceState(savedInstanceState);
     myDialog.onRestoreInstanceState(savedInstanceState.getBundle("DIALOG"));
     // Put your codes to retrieve the EditText contents and 
     // assign them to the EditText here.
}

@Override
protected void onSaveInstanceState(Bundle outState) {
     super.onSaveInstanceState(outState);
     // Put your codes to save the EditText contents and put them 
     // to the outState Bundle here.
     outState.putBundle("DIALOG", myDialog.onSaveInstanceState());
}

1

Kesinlikle, en iyi yaklaşım DialogFragment kullanmaktır.

İşte, farklı diyalogların bir Fragment (veya küçük yeniden düzenleme ile Activity) içinde atılmasını önlemeye yardımcı olan sarmalayıcı sınıfının benim çözümü. Ayrıca, bazı nedenlerden dolayı AlertDialogs, eylemler, görünüm veya başka bir şey açısından aralarında küçük farklılıklar bulunan kod arasında çok fazla dağınıklık varsa, kitlesel kod yeniden düzenlemesinden kaçınmaya yardımcı olur .

public class DialogWrapper extends DialogFragment {
    private static final String ARG_DIALOG_ID = "ARG_DIALOG_ID";

    private int mDialogId;

    /**
     * Display dialog fragment.
     * @param invoker  The fragment which will serve as {@link AlertDialog} alert dialog provider
     * @param dialogId The ID of dialog that should be shown
     */
    public static <T extends Fragment & DialogProvider> void show(T invoker, int dialogId) {
        Bundle args = new Bundle();
        args.putInt(ARG_DIALOG_ID, dialogId);
        DialogWrapper dialogWrapper = new DialogWrapper();
        dialogWrapper.setArguments(args);
        dialogWrapper.setTargetFragment(invoker, 0);
        dialogWrapper.show(invoker.getActivity().getSupportFragmentManager(), null);
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mDialogId = getArguments().getInt(ARG_DIALOG_ID);
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return getDialogProvider().getDialog(mDialogId);
    }

    private DialogProvider getDialogProvider() {
        return (DialogProvider) getTargetFragment();
    }

    public interface DialogProvider {
        Dialog getDialog(int dialogId);
    }
}

Etkinlik söz konusu olduğunda , getContext()içeriden çağırabilir onCreateDialog(), DialogProviderarayüze aktarabilir ve belirli bir iletişim kutusu talep edebilirsiniz mDialogId. Bir hedef parçayla ilgilenmek için tüm mantık silinmelidir.

Parçadan kullanım:

public class MainFragment extends Fragment implements DialogWrapper.DialogProvider {
    private static final int ID_CONFIRMATION_DIALOG = 0;

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        Button btnHello = (Button) view.findViewById(R.id.btnConfirm);
        btnHello.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                DialogWrapper.show(MainFragment.this, ID_CONFIRMATION_DIALOG);
            }
        });
    }

    @Override
    public Dialog getDialog(int dialogId) {
        switch (dialogId) {
            case ID_CONFIRMATION_DIALOG:
                return createConfirmationDialog(); //Your AlertDialog
            default:
                throw new IllegalArgumentException("Unknown dialog id: " + dialogId);
        }
    }
}

Yazının tamamını blogumda okuyabilirsiniz Dialog'un kapatılmasını nasıl önleyebilirim? ve kaynak kodla oynayın .


1

Görünüşe göre bu, "her şeyi doğru yapmak" ve kullanmak DialogFragmentvb. Durumlarda bile hala bir sorun .

Google Sorun İzleyici'de , mesaj kuyruğunda kalan eski bir kapatma mesajından kaynaklandığını iddia eden bir konu var . Sağlanan geçici çözüm oldukça basittir:

    @Override
    public void onDestroyView() {
        /* Bugfix: https://issuetracker.google.com/issues/36929400 */
        if (getDialog() != null && getRetainInstance())
            getDialog().setDismissMessage(null);

        super.onDestroyView();
    }

Bu sorunun ilk bildirilmesinden 7 yıl sonra hala buna ihtiyaç duyulması inanılmaz.



0

Benzer bir sorun yaşadım: Ekran yönü değiştiğinde, kullanıcı iletişim kutusunu onDismisskapatmasa bile iletişim kutusunun dinleyicisi çağrıldı. Bunun yerine onCancel, hem kullanıcı geri düğmesine bastığında hem de iletişim kutusunun dışına dokunduğunda tetiklenen dinleyiciyi kullanarak bu sorunu çözmeyi başardım .


-3

Sadece kullan

ConfigurationChanges = Android.Content.PM.ConfigChanges.Orientation | Android.Content.PM.ConfigChanges.ScreenSize

ve uygulama, döndürme ve ekran boyutunun nasıl işleneceğini bilir.

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.