Android में स्क्रीन रोटेशन पर डायलॉग बर्खास्तगी को रोकें


94

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

अगर मैं onConfigurationChanged विधि को ओवरलोड करता हूं तो मैं इसे सफलतापूर्वक कर सकता हूं और लेआउट को सही ओरिएंटेशन के लिए रीसेट कर सकता हूं लेकिन मैं edittext का चिपचिपा टेक्स्ट फीचर खो देता हूं। तो संवाद समस्या को हल करने में मैंने इस संपादन समस्या को बनाया है।

अगर मैं एडिटेक्स से स्ट्रिंग्स को बचाता हूं और उन्हें ऑन-कॉफिगेशन परिवर्तन में पुन: असाइन करता हूं तो वे अभी भी प्रारंभिक मूल्य के लिए डिफ़ॉल्ट लगते हैं न कि रोटेशन से पहले दर्ज किया गया था। यहां तक ​​कि अगर मैं किसी अमान्य को मजबूर करता हूं तो उन्हें अद्यतन करने के लिए लगता है।

मुझे वास्तव में संवाद समस्या या संपादन समस्या को हल करने की आवश्यकता है।

सहायता के लिए धन्यवाद।


1
आप संपादित किए गए EditText की सामग्री को कैसे सहेजते / पुनर्स्थापित करते हैं? क्या आप कुछ कोड दिखा सकते हैं?
पीटर स्नेगो

मुझे इस समस्या का पता चला, मैं लेआउट को रीसेट करने के बाद आईडी द्वारा फिर से देखने के लिए भूल रहा था।
ड्रेक्सिया

जवाबों:


134

आजकल इस समस्या से बचने का सबसे अच्छा तरीका है एक का उपयोग करना DialogFragment

एक नया वर्ग बनाएँ जो विस्तार करे DialogFragment। ओवरराइड करें onCreateDialogऔर अपना पुराना Dialogया वापस लौटा दें AlertDialog

फिर आप इसके साथ दिखा सकते हैं DialogFragment.show(fragmentManager, tag)

यहाँ एक उदाहरण के साथ है 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();
    }
}

और आपके द्वारा की गई गतिविधि में:

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

यह उत्तर इन अन्य तीन प्रश्नों (और उनके उत्तर) की व्याख्या करने में मदद करता है:


कॉल करने के लिए मेरे ऐप में एक बटन है .show (), मुझे अलर्ट डायलॉग की स्थिति को याद रखना है, दिखा रहा है / खारिज कर दिया गया है। क्या बिना बुलाए संवाद को बनाए रखने का एक तरीका है .show ()?
अल्फा हुआंग

1
यह कहता है कि onAttachअब पदावनत हो गया है। इसके बजाय क्या किया जाना चाहिए?
फ़राहम

3
@faraz_ahmed_kamran, आपको उपयोग करना चाहिए onAttach(Context context)और android.support.v4.app.DialogFragmentonAttachविधि लेता है contextके बजाय activityके रूप में एक पैरामीटर अब।
स्टेन मॉट्स

YesNoListenerहालांकि इसके लिए शायद कोई जरूरत नहीं है । इस जवाब को देखें ।
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
जिस पर मेरा कोड उपयोगी नहीं है, उसे क्लिक करने से पहले आजमाएं :-)
चुंग IW

6
काम करता है, पता नहीं कैसे लेकिन यह काम करता है! साफ सरल और सार समाधान, धन्यवाद।
nmvictor

3
मुझे लगता है कि यह कोड खराब है। doLogout () में संदर्भ का संदर्भ है जो गतिविधि है / है। गतिविधि को नष्ट नहीं किया जा सकता है जो स्मृति रिसाव का कारण बन सकता है। मैं स्थिर संदर्भ से AlertDialog का उपयोग करने की संभावना तलाश रहा था लेकिन अब मुझे यकीन है कि यह असंभव है। परिणाम केवल कचरा हो सकता है मुझे लगता है।
अविश्वसनीय जन

2
ऐसा लगता है कि यह काम करता है। संवाद खुला रहता है, लेकिन इसका नव निर्मित गतिविधि या टुकड़े से कोई संबंध नहीं है (यह प्रत्येक अभिविन्यास परिवर्तन पर नव निर्मित होता है)। इसलिए आप ऐसा कुछ नहीं कर सकते जिसके Contextलिए संवाद बटन के अंदर की आवश्यकता हो OnClickListener
उडो क्लीमाशेवस्की

2
यह कोड काम करता है लेकिन बिल्कुल भी अनुशंसित नहीं है। यह गतिविधि संदर्भ को लीक करता है, जिसके कारण संवाद लगातार बना रह सकता है। यह एक बहुत ही बुरा अभ्यास है जो स्मृति रिसाव को जन्म देगा।
हेक्सेज

4

यदि आप ओरिएंटेशन परिवर्तन पर लेआउट बदल रहे हैं तो मैं नहीं डालूंगा android:configChanges="orientation" आपके प्रकट में क्योंकि आप वैसे भी विचारों को फिर से बना रहे हैं।

इन तरीकों का उपयोग करके अपनी गतिविधि की वर्तमान स्थिति (जैसे पाठ में प्रवेश, दिखाया गया संवाद, डेटा प्रदर्शित आदि) को सहेजें:

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

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

इस तरह से गतिविधि फिर से चालू होती है और बाद में onRestoreInstanceState विधि को कॉल करती है जहाँ आप अपना EditText मान फिर से सेट कर सकते हैं।

यदि आप अधिक जटिल वस्तुओं को संग्रहीत करना चाहते हैं, तो आप उपयोग कर सकते हैं

@Override
public Object onRetainNonConfigurationInstance() {
}

यहां आप किसी भी ऑब्जेक्ट को स्टोर कर सकते हैं और ऑनक्रीट में आपको सिर्फ getLastNonConfigurationInstance();ऑब्जेक्ट प्राप्त करने के लिए कॉल करना होगा।


4
OnRetainNonConfigurationInstance()अब डॉक्टर के रूप में पदावनत किया गया है: developer.android.com/reference/android/app/… setRetainInstance(boolean retain) इसके बजाय उपयोग किया जाना चाहिए: developer.android.com/reference/android/app/…
ForceMagic

@ForceMagic setRetainInstanceपूरी तरह से अलग है: यह Fragments के लिए है और यह आपको गारंटी नहीं देता है कि उदाहरण बरकरार रहेगा।
Miha_x64

3

बस Android जोड़ें: AndroidManifest.xml में अपनी गतिविधि तत्व के साथ configChanges = "अभिविन्यास"

उदाहरण:

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

यह कुछ परिस्थितियों में संवाद को गलत तरीके से प्रदर्शित करने का कारण बन सकता है।
सैम

1
Android: configChanges = "ओरिएंटेशन | स्क्रीनसाइज़" नोट: यदि आपका एप्लिकेशन Android 3.2 (API स्तर 13) या उच्चतर को लक्षित करता है, तो आपको "स्क्रीनाइज़" कॉन्फ़िगरेशन भी घोषित करना चाहिए, क्योंकि यह तब भी बदलता है जब चित्र और लैंडस्केप अभिविन्यास के बीच डिवाइस स्विच हो जाता है।
tsig

1

एक बहुत ही आसान तरीका है कि विधि से संवाद बनाएं onCreateDialog()(नीचे नोट देखें)। आप उनके माध्यम से दिखाएं showDialog()। इस तरह, एंड्रॉयड आप के लिए रोटेशन हैंडल और आप कॉल करने के लिए की जरूरत नहीं है dismiss()में onPause()एक WindowLeak से बचने के लिए और फिर आप न संवाद बहाल करने के लिए है। डॉक्स से:

इस गतिविधि द्वारा प्रबंधित एक संवाद दिखाएं। OnCreateDialog (int, बंडल) पर कॉल उसी आईडी के साथ किया जाएगा जब पहली बार यह किसी दिए गए आईडी के लिए कहा जाता है। इसके बाद से, संवाद स्वचालित रूप से सहेजा जाएगा और पुनर्स्थापित किया जाएगा।

अधिक जानकारी के लिए Android डॉक्स showDialog () देखें । आशा है कि यह किसी की मदद करता है!

नोट: यदि AlertDialog.Builder का उपयोग कर, कॉल नहीं करते show()से onCreateDialog(), फोन create()के बजाय। यदि ProgressDialog का उपयोग कर रहे हैं, तो बस ऑब्जेक्ट बनाएं, आपके द्वारा आवश्यक पैरामीटर सेट करें और इसे वापस करें। निष्कर्ष में, show()अंदर onCreateDialog()समस्याएं पैदा करती हैं, बस डी डायलॉग उदाहरण बनाएं और इसे वापस करें। यह काम करना चाहिए! (मैं onDreate से showDialog () का उपयोग करके मुद्दों का अनुभव किया है () -वास्तविक रूप से संवाद नहीं दिखा रहा है-, लेकिन अगर आप इसे onResume () में उपयोग करते हैं या श्रोता कॉलबैक में यह अच्छी तरह से काम करता है)।


किस मामले के लिए आपको कुछ कोड की आवश्यकता होगी? OnCreateDialog () या इसे बिल्डर और कॉलिंग शो () के साथ दिखा रहा है?
कैमुन्स

मैं इसे करने में कामयाब रहा हूँ .. लेकिन बात यह है, onCreateDialog () अब पदावनत: - \
neteinstein

ठीक है! ध्यान रखें कि अधिकांश एंड्रॉइड डिवाइस अभी भी 2.X संस्करणों के साथ काम करते हैं, इसलिए आप इसे वैसे भी उपयोग कर सकते हैं! एंड्रॉइड प्लेटफ़ॉर्म संस्करण के उपयोग
कॉमन

फिर भी, क्या अन्य विकल्प है अगर onCreateDialog नहीं है?
neteinstein

1
आप बिल्डर वर्गों का उपयोग कर सकते हैं जैसे AlertDialog.Builder। यदि आप इसका उपयोग करते हैं onCreateDialog(), तो बदले के show()परिणाम का उपयोग करें create()। एल्स, कॉल show()और स्टोर किए गए एलर्टडायलॉग को एक्टिविटी की एक विशेषता में और onPause() dismiss()इसमें एक विंडोलाक से बचने के लिए दिखाते हुए स्टोर करें। आशा करता हूँ की ये काम करेगा!
Caumons

1

इस सवाल का जवाब बहुत समय पहले दिया गया था।

फिर भी यह गैर-हैकैपी और सरल समाधान है जो मैं अपने लिए उपयोग करता हूं।

मैंने अपने लिए यह सहायक कक्षा की थी , इसलिए आप इसे अपने आवेदन में भी उपयोग कर सकते हैं।

उपयोग है:

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

या

 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

डायलॉग की स्थिति को बनाए रखने के लिए आप गतिविधि के onSave / onRestore विधियों के साथ संवाद के onSave / onRestore विधियों को जोड़ सकते हैं ।

नोट: यह विधि उन "सरल" संवादों के लिए काम करती है, जैसे कि एक चेतावनी संदेश प्रदर्शित करना। यह एक संवाद में एम्बेडेड WebView की सामग्री को पुन: पेश नहीं करेगा। यदि आप वास्तव में रोटेशन के दौरान एक जटिल संवाद को खारिज करने से रोकना चाहते हैं, तो चुंग आईडब्ल्यू की विधि का प्रयास करें।

@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

निश्चित रूप से, सबसे अच्छा दृष्टिकोण डायलॉगफ्रैगमेंट का उपयोग करके है।

यहाँ मेरा रैपर वर्ग का समाधान है जो विभिन्न संवादों को एक फ्रैगमेंट (या छोटे रिफैक्टीशन के साथ गतिविधि) के भीतर खारिज होने से रोकने में मदद करता है। इसके अलावा, यह बड़े पैमाने पर कोड से बचने से बचने में मदद करता है यदि कुछ कारणों AlertDialogsसे कोड के बीच में क्रियाओं, उपस्थिति या कुछ और के संदर्भ में थोड़े अंतर के साथ बिखरे हुए हैं।

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

जब गतिविधि की बात आती है, तो आप इसे getContext()अंदर ले जा सकते हैं onCreateDialog(), इसे DialogProviderइंटरफ़ेस में डाल सकते हैं और इसके द्वारा एक विशिष्ट संवाद का अनुरोध कर सकते हैंmDialogId । लक्ष्य विखंडन से निपटने के सभी तर्क को हटा दिया जाना चाहिए।

टुकड़े से उपयोग:

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

आप मेरे ब्लॉग पर पूरा लेख पढ़ सकते हैं कि डायलॉग को खारिज करने से कैसे रोका जाए? और स्रोत कोड के साथ खेलते हैं ।


1

ऐसा लगता है कि यह अभी भी एक मुद्दा है, तब भी जब "सब कुछ सही कर रहे हैं" और उपयोग कर रहे हैं DialogFragmentआदि।

Google समस्या ट्रैकर पर एक थ्रेड है, जो दावा करता है कि यह एक पुराने खारिज संदेश के कारण संदेश कतार में छोड़ दिया गया है। प्रदान किया गया वर्कअराउंड काफी सरल है:

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

        super.onDestroyView();
    }

अविश्वसनीय यह है कि इस मुद्दे की पहली रिपोर्ट के 7 साल बाद भी इसकी जरूरत है।



0

मुझे एक समान समस्या थी: जब स्क्रीन ओरिएंटेशन बदल गया, onDismissतो उपयोगकर्ता द्वारा डायलॉग को खारिज नहीं करने पर भी डायलॉग के श्रोता को बुलाया गया। मैं onCancelश्रोता का उपयोग करके इसके चारों ओर काम करने में सक्षम था , जिसने उपयोगकर्ता को बैक बटन दबाए जाने पर और उपयोगकर्ता द्वारा संवाद के बाहर छूने पर दोनों को ट्रिगर किया था।


-3

महज प्रयोग करें

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

और एप्लिकेशन को पता चल जाएगा कि रोटेशन और स्क्रीन का आकार कैसे संभालना है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.