Android DialogFragment เทียบกับ Dialog


244

Google ขอแนะนำให้เราใช้DialogFragmentแทนการที่เรียบง่ายDialogโดยใช้Fragments APIแต่มันเป็นเรื่องเหลวไหลที่จะใช้แยกDialogFragmentหาง่ายใช่ไม่มีกล่องข้อความยืนยัน แนวปฏิบัติที่ดีที่สุดในกรณีนี้คืออะไร?


5
ในระยะสั้นในสิ่งอื่น ๆ ที่เรียบง่ายDialogหรือAlertDialog.Builder::create()::show()จะสร้างโต้ตอบที่หายไปเมื่อคุณหมุนหน้าจอ
user1032613

คำตอบ:


83

ใช่ใช้งานDialogFragmentและในตัวonCreateDialogคุณสามารถใช้เครื่องมือสร้าง AlertDialog ได้เพื่อสร้างแบบง่าย ๆAlertDialogด้วยปุ่มยืนยันใช่ / ไม่ใช่ รหัสไม่มากเลย

ในการจัดการกับเหตุการณ์ในส่วนของคุณจะมีหลายวิธีในการทำมัน แต่ฉันเพียงแค่กำหนดข้อความHandlerในของฉันFragmentผ่านมันDialogFragmentผ่านทางคอนสตรัคเตอร์แล้วส่งข้อความกลับไปยังตัวจัดการส่วนของฉัน อีกหลายวิธีในการทำเช่นนั้น แต่การทำงานต่อไปนี้สำหรับฉัน

ในไดอะล็อกพักข้อความและอินสแตนซ์ของมันใน constructor:

private Message okMessage;
...
okMessage = handler.obtainMessage(MY_MSG_WHAT, MY_MSG_OK);

นำไปใช้onClickListenerในไดอะล็อกของคุณจากนั้นเรียกตัวจัดการตามความเหมาะสม:

public void onClick(.....
    if (which == DialogInterface.BUTTON_POSITIVE) {
        final Message toSend = Message.obtain(okMessage);
        toSend.sendToTarget();
    }
 }

แก้ไข

และเป็นMessageพัสดุที่คุณสามารถบันทึกไว้ในonSaveInstanceStateและเรียกคืนได้

outState.putParcelable("okMessage", okMessage);

จากนั้นใน onCreate

if (savedInstanceState != null) {
    okMessage = savedInstanceState.getParcelable("okMessage");
}

4
ปัญหาไม่ใช่ okMessage - ปัญหาคือ okMessage targetซึ่งจะเป็นโมฆะถ้าคุณโหลดจาก Bundle ถ้าเป้าหมายของข้อความนั้นเป็นโมฆะและคุณใช้sendToTargetคุณจะได้รับ NullPointerException - ไม่ใช่เพราะข้อความนั้นเป็นโมฆะ แต่เพราะเป้าหมายของมันคือ
hrnt

2
ข้อดีของการใช้ DialogFragment แทนที่จะเป็น Dialog คืออะไร
Raphael Petegrosso

80
ข้อดีของการใช้ DialogFragment คือวงจรชีวิตทั้งหมดของไดอะล็อกจะถูกจัดการให้คุณ คุณจะไม่ได้รับข้อความแสดงข้อผิดพลาด 'ข้อความแจ้งเตือนรั่วไหล ... ' อีกครั้ง ไปที่ DialogFragment และลืม Dialogs
Snicolas

6
ฉันคิดว่าควรใช้ setArguments () และ getArguments () แทนการส่ง okMessage ผ่านทาง Constructor
pjv

1
ดีฉันผู้ใช้ตัวสร้างสวยได้อย่างง่ายดายและฉันจัดการการจัดการกิจกรรมกับ Android นี้: configChanges = "locale | keyboardHidden | ปฐมนิเทศ | screenSize" และฉันไม่เห็นปัญหาใด ๆ ในการใช้งาน ...
Renetik

67

คุณสามารถสร้างคลาสย่อย DialogFragment ทั่วไปเช่น YesNoDialog และ OkDialog และส่งผ่านหัวเรื่องและข้อความหากคุณใช้กล่องโต้ตอบเป็นจำนวนมากในแอปของคุณ

public class YesNoDialog extends DialogFragment
{
    public static final String ARG_TITLE = "YesNoDialog.Title";
    public static final String ARG_MESSAGE = "YesNoDialog.Message";

    public YesNoDialog()
    {

    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState)
    {
        Bundle args = getArguments();
        String title = args.getString(ARG_TITLE);
        String message = args.getString(ARG_MESSAGE);

        return new AlertDialog.Builder(getActivity())
            .setTitle(title)
            .setMessage(message)
            .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener()
            {
                @Override
                public void onClick(DialogInterface dialog, int which)
                {
                    getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, null);
                }
            })
            .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener()
            {
                @Override
                public void onClick(DialogInterface dialog, int which)
                {
                    getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_CANCELED, null);
                }
            })
            .create();
    }
}

จากนั้นเรียกมันโดยใช้สิ่งต่อไปนี้:

    DialogFragment dialog = new YesNoDialog();
    Bundle args = new Bundle();
    args.putString(YesNoDialog.ARG_TITLE, title);
    args.putString(YesNoDialog.ARG_MESSAGE, message);
    dialog.setArguments(args);
    dialog.setTargetFragment(this, YES_NO_CALL);
    dialog.show(getFragmentManager(), "tag");

และจัดการกับผลลัพธ์ที่onActivityResultได้


ใช่ DialogFragment จัดการเหตุการณ์ระยะเวลาทั้งหมดให้คุณ
ashishduh

1
ผมคิดว่ามันไม่ได้เพราะหลังจากหมุนเก่าโต้ตอบยังคงมีอยู่และจะช่วย assignent เก่าส่วนไม่ได้ที่มีอยู่ (dialog.setTargetFragment (นี้ YES_NO_CALL);) ดังนั้นหลังจากการหมุน getTargetFragment () onActivityResult ไม่ทำงาน.
Malachiasz

7
สิ่งที่เป็นYES_NO_CALL, getFragmentManager()และonActivityResult?
msysmilu

2
YES_NO_CALLเป็น int ที่กำหนดเองที่เป็นรหัสคำขอ getFragmentManager()รับตัวจัดการส่วนสำหรับกิจกรรมและ onActivityResult()เป็นวิธีการเรียกกลับส่วนวงจรชีวิต
ashishduh

3
แทนที่ getFragmentManager () ด้วย getSupportFragmentManager ();
Avinash Verma

33

ใช้ DialogFragment ผ่าน AlertDialog:


  • ตั้งแต่เปิดตัว API ระดับ 13 :

    showDialogวิธีการจากกิจกรรมเลิก ไม่แนะนำให้เรียกใช้กล่องโต้ตอบที่อื่นในรหัสเนื่องจากคุณจะต้องจัดการกล่องโต้ตอบด้วยตนเอง (เช่นการเปลี่ยนการวางแนว)

  • การแยกความแตกต่างการจัดเรียงข้อมูล - AlertDialog

    พวกเขาแตกต่างกันมาก? จากการอ้างอิงเกี่ยวกับ Android DialogFragment :

    DialogFragment เป็นส่วนที่แสดงหน้าต่างโต้ตอบลอยอยู่ด้านบนของหน้าต่างกิจกรรม ส่วนนี้มีวัตถุโต้ตอบซึ่งจะแสดงตามความเหมาะสมขึ้นอยู่กับสถานะของส่วน การควบคุมไดอะล็อก (การตัดสินใจว่าเมื่อใดที่จะแสดง, ซ่อน, ยกเลิกมัน) ควรจะทำผ่าน API ที่นี่ไม่ใช่การโทรโดยตรงบนไดอะล็อก

  • บันทึกอื่น ๆ

    • แฟรกเมนต์เป็นวิวัฒนาการตามธรรมชาติในกรอบ Android เนื่องจากความหลากหลายของอุปกรณ์ที่มีขนาดหน้าจอที่แตกต่างกัน
    • DialogFragments and Fragments มีอยู่ในห้องสมุดสนับสนุนซึ่งทำให้ชั้นสามารถใช้งานได้ใน Android รุ่นที่ใช้อยู่ในปัจจุบันทั้งหมด

28

DialogFragmentฉันจะแนะนำให้ใช้

แน่นอนว่าการสร้างกล่องโต้ตอบ "ใช่ / ไม่ใช่" นั้นค่อนข้างซับซ้อนเนื่องจากควรเป็นงานที่ค่อนข้างเรียบง่าย แต่การสร้างกล่องโต้ตอบที่คล้ายกันซึ่งมีความDialogซับซ้อนอย่างน่าประหลาดใจเช่นกัน

(วงจรชีวิตของกิจกรรมทำให้มันซับซ้อน - คุณต้องให้Activityการจัดการวงจรชีวิตของกล่องโต้ตอบ - และไม่มีทางที่จะผ่านพารามิเตอร์ที่กำหนดเองเช่นข้อความที่กำหนดเองไปActivity.showDialogหากใช้ระดับ API ต่ำกว่า 8)

สิ่งที่ดีคือคุณมักจะสามารถสร้างสิ่งที่เป็นนามธรรมของคุณเองDialogFragmentได้อย่างง่ายดาย


คุณจะจัดการกับการโทรกลับโต้ตอบการแจ้งเตือนได้อย่างไร (ใช่ไม่ใช่)
Alexey Zakharov

วิธีที่ง่ายที่สุดคือการใช้วิธีในการโฮสต์กิจกรรมที่ใช้Stringพารามิเตอร์ เมื่อผู้ใช้คลิก "ใช่" ตัวอย่างเช่นการโต้ตอบเรียกวิธีการของกิจกรรมที่มีพารามิเตอร์ "ตกลง" พารามิเตอร์เหล่านี้ถูกระบุเมื่อแสดงข้อความโต้ตอบเช่น AskDialog.ask ("คุณเห็นด้วยกับข้อตกลงเหล่านี้หรือไม่", "เห็นด้วย", "ไม่เห็นด้วย");
hrnt

5
แต่ฉันต้องการโทรกลับภายในชิ้นส่วนไม่ใช่กิจกรรม ฉันสามารถใช้ setTargetFragment และส่งไปยังอินเตอร์เฟส แต่มันคือนรก
Alexey Zakharov

นอกจากนี้คุณยังสามารถเรียกส่วนเป้าหมายโดยการตั้งค่าแท็กไปยังเป้าหมายและใช้'sFragmentManager findFragmentByTagแต่ใช่มันต้องใช้รหัสที่ยุติธรรม
hrnt

@AlexeyZakharov ฉันรู้ว่านี้เป็นเรื่องเกี่ยวกับ 5 ปีปลาย แต่คุณสามารถผ่านFragment this และมีคุณของคุณActivity extends Interfaceแม้ว่าการทำเธรดอย่างระมัดระวังคุณสามารถหยุดการโทรอินเทอร์เฟซเมื่อคุณไม่ต้องการให้พวกเขาหากไม่เห็นพ้องด้วย ไม่แน่ใจว่าสิ่งนี้จะมีอะไรกับหน่วยความจำและปาเก็ตตี้พึ่งพาแบบวงกลมแม้ว่าใครอยากจะพูดสอดใน? ตัวเลือกอื่นคือMessage/ Handlerแต่คุณยังอาจมีปัญหาการทำงานพร้อมกัน
เคล็ดลับ

8

Generic AlertDialogFragment ที่มีรูปแบบตัวสร้าง

ในโครงการของฉันฉันใช้AlertDialog.Builderไปมากแล้วก่อนที่ฉันจะพบว่ามันมีปัญหา อย่างไรก็ตามฉันไม่ต้องการเปลี่ยนรหัสจำนวนมากในแอพของฉัน นอกจากนี้ที่จริงผมเป็นแฟนของผมผ่านOnClickListenersชั้นเรียนที่ไม่ระบุชื่อที่พวกเขามีความจำเป็น (นั่นคือเมื่อใช้setPositiveButton(),setNegativeButton()ฯลฯ ) แทนที่จะต้องใช้หลายพันวิธีโทรกลับในการติดต่อสื่อสารระหว่างส่วนโต้ตอบและส่วนของผู้ถือซึ่งสามารถใน ความคิดของฉันนำไปสู่รหัสสับสนและซับซ้อนมาก โดยเฉพาะอย่างยิ่งหากคุณมีกล่องโต้ตอบที่แตกต่างกันหลายรายการในส่วนเดียวและต้องแยกความแตกต่างในการประยุกต์ใช้การติดต่อกลับระหว่างที่แสดงกล่องโต้ตอบ

ดังนั้นฉันจึงรวมวิธีการต่าง ๆ เพื่อสร้างAlertDialogFragmentคลาสตัวช่วยทั่วไปซึ่งสามารถใช้เหมือนกับ AlertDialog :


สารละลาย

( โปรดทราบว่าฉันกำลังใช้นิพจน์แลมบ์ดาของ Java 8 ในรหัสของฉันดังนั้นคุณอาจต้องเปลี่ยนบางส่วนของรหัสหากคุณยังไม่ได้ใช้นิพจน์แลมบ์ดา )

/**
 * Helper class for dialog fragments to show a {@link AlertDialog}. It can be used almost exactly
 * like a {@link AlertDialog.Builder}
 * <p />
 * Creation Date: 22.03.16
 *
 * @author felix, http://flx-apps.com/
 */
public class AlertDialogFragment extends DialogFragment {
    protected FragmentActivity activity;
    protected Bundle args;
    protected String tag = AlertDialogFragment.class.getSimpleName();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        activity = getActivity();
        args = getArguments();
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        Dialog dialog = setDialogDefaults(new AlertDialog.Builder(getActivity())).create();

        if (args.containsKey("gravity")) {
            dialog.getWindow().getAttributes().gravity = args.getInt("gravity");
        }

        dialog.setOnShowListener(d -> {
            if (dialog != null && dialog.findViewById((android.R.id.message)) != null) {
                ((TextView) dialog.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance());
            }
        });
        return dialog;
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return super.onCreateView(inflater, container, savedInstanceState);
    }

    @Override
    public void onDismiss(DialogInterface dialog) {
        super.onDismiss(dialog);

        if (args.containsKey("onDismissListener")) {
            Parcelable onDismissListener = args.getParcelable("onDismissListener");
            if (onDismissListener != null && onDismissListener instanceof ParcelableOnDismissListener) {
                ((ParcelableOnDismissListener) onDismissListener).onDismiss(this);
            }
        }
    }

    /**
     * Sets default dialog properties by arguments which were set using {@link #builder(FragmentActivity)}
     */
    protected AlertDialog.Builder setDialogDefaults(AlertDialog.Builder builder) {
        args = getArguments();
        activity = getActivity();

        if (args.containsKey("title")) {
            builder.setTitle(args.getCharSequence("title"));
        }

        if (args.containsKey("message")) {
            CharSequence message = args.getCharSequence("message");
            builder.setMessage(message);
        }

        if (args.containsKey("viewId")) {
            builder.setView(getActivity().getLayoutInflater().inflate(args.getInt("viewId"), null));
        }

        if (args.containsKey("positiveButtonText")) {
            builder.setPositiveButton(args.getCharSequence("positiveButtonText"), (dialog, which) -> {
                onButtonClicked("positiveButtonListener", which);
            });
        }

        if (args.containsKey("negativeButtonText")) {
            builder.setNegativeButton(args.getCharSequence("negativeButtonText"), (dialog, which) -> {
                onButtonClicked("negativeButtonListener", which);
            });
        }

        if (args.containsKey("neutralButtonText")) {
            builder.setNeutralButton(args.getCharSequence("neutralButtonText"), (dialog, which) -> {
                onButtonClicked("neutralButtonListener", which);
            });
        }

        if (args.containsKey("items")) {
            builder.setItems(args.getStringArray("items"), (dialog, which) -> {
                onButtonClicked("itemClickListener", which);
            });
        }

        // @formatter:off
        // FIXME this a pretty hacky workaround: we don't want to show the dialog if onClickListener of one of the dialog's button click listener were lost
        //       the problem is, that there is no (known) solution for parceling a OnClickListener in the long term (only for state changes like orientation change,
        //       but not if the Activity was completely lost)
        if (
                (args.getParcelable("positiveButtonListener") != null && !(args.getParcelable("positiveButtonListener") instanceof ParcelableOnClickListener)) ||
                (args.getParcelable("negativeButtonListener") != null && !(args.getParcelable("negativeButtonListener") instanceof ParcelableOnClickListener)) ||
                (args.getParcelable("neutralButtonListener") != null && !(args.getParcelable("neutralButtonListener") instanceof ParcelableOnClickListener)) ||
                (args.getParcelable("itemClickListener") != null && !(args.getParcelable("itemClickListener") instanceof ParcelableOnClickListener))
        ) {
            new DebugMessage("Forgot onClickListener. Needs to be dismissed.")
                    .logLevel(DebugMessage.LogLevel.VERBOSE)
                    .show();
            try {
                dismissAllowingStateLoss();
            } catch (NullPointerException | IllegalStateException ignored) {}
        }
        // @formatter:on

        return builder;
    }

    public interface OnDismissListener {
        void onDismiss(AlertDialogFragment dialogFragment);
    }

    public interface OnClickListener {
        void onClick(AlertDialogFragment dialogFragment, int which);
    }

    protected void onButtonClicked(String buttonKey, int which) {
        ParcelableOnClickListener parcelableOnClickListener = getArguments().getParcelable(buttonKey);
        if (parcelableOnClickListener != null) {
            parcelableOnClickListener.onClick(this, which);
        }
    }

    // region Convenience Builder Pattern class almost similar to AlertDialog.Builder
    // =============================================================================================

    public AlertDialogFragment builder(FragmentActivity activity) {
        this.activity = activity;
        this.args = new Bundle();
        return this;
    }

    public AlertDialogFragment addArguments(Bundle bundle) {
        args.putAll(bundle);
        return this;
    }

    public AlertDialogFragment setTitle(int titleStringId) {
        return setTitle(activity.getString(titleStringId));
    }

    public AlertDialogFragment setTitle(CharSequence title) {
        args.putCharSequence("title", title);
        return this;
    }

    public AlertDialogFragment setMessage(int messageStringId) {
        return setMessage(activity.getString(messageStringId));
    }

    public AlertDialogFragment setMessage(CharSequence message) {
        args.putCharSequence("message", message);
        return this;
    }

    public AlertDialogFragment setPositiveButton(int textStringId, OnClickListener onClickListener) {
        return setPositiveButton(activity.getString(textStringId), onClickListener);
    }

    public AlertDialogFragment setPositiveButton(CharSequence text, AlertDialogFragment.OnClickListener onClickListener) {
        args.putCharSequence("positiveButtonText", text);
        args.putParcelable("positiveButtonListener", createParcelableOnClickListener(onClickListener));
        return this;
    }

    public AlertDialogFragment setNegativeButton(int textStringId, AlertDialogFragment.OnClickListener onClickListener) {
        return setNegativeButton(activity.getString(textStringId), onClickListener);
    }

    public AlertDialogFragment setNegativeButton(CharSequence text, AlertDialogFragment.OnClickListener onClickListener) {
        args.putCharSequence("negativeButtonText", text);
        args.putParcelable("negativeButtonListener", createParcelableOnClickListener(onClickListener));
        return this;
    }

    public AlertDialogFragment setNeutralButton(int textStringId, AlertDialogFragment.OnClickListener onClickListener) {
        return setNeutralButton(activity.getString(textStringId), onClickListener);
    }

    public AlertDialogFragment setNeutralButton(CharSequence text, AlertDialogFragment.OnClickListener onClickListener) {
        args.putCharSequence("neutralButtonText", text);
        args.putParcelable("neutralButtonListener", createParcelableOnClickListener(onClickListener));
        return this;
    }

    public AlertDialogFragment setOnDismissListener(OnDismissListener onDismissListener) {
        if (onDismissListener == null) {
            return this;
        }

        Parcelable p = new ParcelableOnDismissListener() {
            @Override
            public void onDismiss(AlertDialogFragment dialogFragment) {
                onDismissListener.onDismiss(dialogFragment);
            }
        };
        args.putParcelable("onDismissListener", p);
        return this;
    }

    public AlertDialogFragment setItems(String[] items, AlertDialogFragment.OnClickListener onClickListener) {
        args.putStringArray("items", items);
        args.putParcelable("itemClickListener", createParcelableOnClickListener(onClickListener));
        return this;
    }

    public AlertDialogFragment setView(int viewId) {
        args.putInt("viewId", viewId);
        return this;
    }

    public AlertDialogFragment setGravity(int gravity) {
        args.putInt("gravity", gravity);
        return this;
    }

    public AlertDialogFragment setTag(String tag) {
        this.tag = tag;
        return this;
    }

    public AlertDialogFragment create() {
        setArguments(args);
        return AlertDialogFragment.this;
    }

    public AlertDialogFragment show() {
        create();
        try {
            super.show(activity.getSupportFragmentManager(), tag);
        }
        catch (IllegalStateException e1) {

            /**
             * this whole part is used in order to attempt to show the dialog if an
             * {@link IllegalStateException} was thrown (it's kinda comparable to
             * {@link FragmentTransaction#commitAllowingStateLoss()} 
             * So you can remove all those dirty hacks if you are sure that you are always
             * properly showing dialogs in the right moments
             */

            new DebugMessage("got IllegalStateException attempting to show dialog. trying to hack around.")
                    .logLevel(DebugMessage.LogLevel.WARN)
                    .exception(e1)
                    .show();

            try {
                Field mShownByMe = DialogFragment.class.getDeclaredField("mShownByMe");
                mShownByMe.setAccessible(true);
                mShownByMe.set(this, true);
                Field mDismissed = DialogFragment.class.getDeclaredField("mDismissed");
                mDismissed.setAccessible(true);
                mDismissed.set(this, false);
            }
            catch (Exception e2) {
                new DebugMessage("error while showing dialog")
                        .exception(e2)
                        .logLevel(DebugMessage.LogLevel.ERROR)
                        .show();
            }
            FragmentTransaction transaction = activity.getSupportFragmentManager().beginTransaction();
            transaction.add(this, tag);
            transaction.commitAllowingStateLoss(); // FIXME hacky and unpredictable workaround
        }
        return AlertDialogFragment.this;
    }

    @Override
    public int show(FragmentTransaction transaction, String tag) {
        throw new NoSuchMethodError("Please use AlertDialogFragment.show()!");
    }

    @Override
    public void show(FragmentManager manager, String tag) {
        throw new NoSuchMethodError("Please use AlertDialogFragment.show()!");
    }

    protected ParcelableOnClickListener createParcelableOnClickListener(AlertDialogFragment.OnClickListener onClickListener) {
        if (onClickListener == null) {
            return null;
        }

        return new ParcelableOnClickListener() {
            @Override
            public void onClick(AlertDialogFragment dialogFragment, int which) {
                onClickListener.onClick(dialogFragment, which);
            }
        };
    }

    /**
     * Parcelable OnClickListener (can be remembered on screen rotation)
     */
    public abstract static class ParcelableOnClickListener extends ResultReceiver implements AlertDialogFragment.OnClickListener {
        public static final Creator<ResultReceiver> CREATOR = ResultReceiver.CREATOR;

        ParcelableOnClickListener() {
            super(null);
        }

        @Override
        public abstract void onClick(AlertDialogFragment dialogFragment, int which);
    }

    /**
     * Parcelable OnDismissListener (can be remembered on screen rotation)
     */
    public abstract static class ParcelableOnDismissListener extends ResultReceiver implements AlertDialogFragment.OnDismissListener {
        public static final Creator<ResultReceiver> CREATOR = ResultReceiver.CREATOR;

        ParcelableOnDismissListener() {
            super(null);
        }

        @Override
        public abstract void onDismiss(AlertDialogFragment dialogFragment);
    }


    // =============================================================================================
    // endregion
}

การใช้งาน

// showing a normal alert dialog with state loss on configuration changes (like device rotation)
new AlertDialog.Builder(getActivity())
        .setTitle("Are you sure? (1)")
        .setMessage("Do you really want to do this?")
        .setPositiveButton("Yes", (dialog, which) -> Toast.makeText(getContext(), "Yes clicked", Toast.LENGTH_SHORT).show())
        .setNegativeButton("Cancel", null)
        .show();

// showing a dialog fragment using the helper class with no state loss on configuration changes
new AlertDialogFragment.builder(getActivity())
        .setTitle("Are you sure? (2)")
        .setMessage("Do you really want to do this?")
        .setPositiveButton("Yes", (dialog, which) -> Toast.makeText(getContext(), "Yes clicked", Toast.LENGTH_SHORT).show())
        .setNegativeButton("Cancel", null)
        .show();

ฉันโพสต์สิ่งนี้ที่นี่ไม่เพียง แต่จะแบ่งปันวิธีการแก้ปัญหาของฉัน แต่ยังเพราะฉันต้องการที่จะขอให้คุณคนสำหรับความคิดเห็นของคุณ: วิธีการนี้ถูกกฎหมายหรือเป็นปัญหาบ้างหรือไม่?


3
นี่เป็นแนวคิดที่น่าสนใจมาก แต่ฉันไม่คิดว่าการออกแบบ API จะใช้งานได้ หากคุณผ่าน OnClickListener ไปยัง setPositiveButton () เมื่ออุปกรณ์หมุนและชิ้นส่วนถูกสร้างขึ้นใหม่จาก Bundle args OnClickListeners จะไม่ถูกสร้างขึ้นใหม่จาก Parcelable อย่างถูกต้อง ปัญหาพื้นฐานคือคุณไม่สามารถสร้างผู้ฟังระหว่างการหมุนเวียนได้ แต่อินเทอร์เฟซ API (ซึ่งใช้ส่วนต่อประสาน) ต้องการมัน ฉันหวังว่านี่ไม่ใช่กรณี (ตามที่ฉันชอบความคิด)
Xargs

1
เป็นความคิดที่ดี แต่อย่างที่ @Xargs กล่าวว่าใช้งานไม่ได้ ผู้ฟังที่ส่งผ่านจะไม่ถูกสร้างขึ้นใหม่อย่างถูกต้องในการหมุน
Graham Borland

ผลลัพธ์ของฉันคือมันใช้งานได้จริงในการหมุนและกลับมาทำงานที่แอพ (ตัวอย่างเช่นหลังจากไปที่หน้าจอหลัก) แต่ไม่ใช่เมื่อกิจกรรมถูกกู้คืนหลังจากที่มันถูกทำลายอย่างสมบูรณ์ (จากนั้น OnClickListeners จะหายไป) (ทดสอบบน Android 4.4.4 และ Android 5.1.1)
flxapps

ฉันยังไม่ได้ทดสอบการใช้งานที่แน่นอน แต่จากสิ่งที่ฉันทดสอบผู้ฟังที่ส่งผ่าน parcelable บันเดิลแฟรกเมนต์จะถูกเรียกอย่างถูกต้องเมื่อสร้างใหม่ ฉันไม่รู้ว่าทำไม แต่ดูเหมือนว่าจะทำงาน
Saad Farooq

@flxapps ในกรณีของมุมมองที่กำหนดเองคุณจะรับมุมมองลูกและเปลี่ยนคุณสมบัติหรือใช้ผู้ฟังได้อย่างไร ในชั้นเรียนของคุณคุณจะไม่ส่งคืนอินสแตนซ์ของการโต้ตอบใด ๆ และอาจทำให้เกิดข้อยกเว้นหากมีคนพยายามที่จะรับมุมมองของเด็ก
Zubair Rehman

5

ฉันขอแนะนำคำตอบของ @ ashishduh ให้เข้าใจง่ายขึ้นไหม:

public class AlertDialogFragment extends DialogFragment {
public static final String ARG_TITLE = "AlertDialog.Title";
public static final String ARG_MESSAGE = "AlertDialog.Message";

public static void showAlert(String title, String message, Fragment targetFragment) {
    DialogFragment dialog = new AlertDialogFragment();
    Bundle args = new Bundle();
    args.putString(ARG_TITLE, title);
    args.putString(ARG_MESSAGE, message);
    dialog.setArguments(args);
    dialog.setTargetFragment(targetFragment, 0);
    dialog.show(targetFragment.getFragmentManager(), "tag");
}

public AlertDialogFragment() {}

@NonNull
@Override
public AlertDialog onCreateDialog(Bundle savedInstanceState)
{
    Bundle args = getArguments();
    String title = args.getString(ARG_TITLE, "");
    String message = args.getString(ARG_MESSAGE, "");

    return new AlertDialog.Builder(getActivity())
            .setTitle(title)
            .setMessage(message)
            .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener()
            {
                @Override
                public void onClick(DialogInterface dialog, int which)
                {
                    getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, null);
                }
            })
            .create();
}

มันไม่จำเป็นสำหรับผู้ใช้ (ของคลาส) เพื่อทำความคุ้นเคยกับ internals ของส่วนประกอบและทำให้การใช้งานง่ายมาก:

AlertDialogFragment.showAlert(title, message, this);

ป.ล. ในกรณีของฉันฉันต้องการกล่องโต้ตอบการแจ้งเตือนที่เรียบง่ายนั่นคือสิ่งที่ฉันสร้างขึ้น คุณสามารถใช้แนวทางนี้กับ Yes / No หรือประเภทอื่น ๆ ที่คุณต้องการ


1

ใช้ไดอะล็อกสำหรับกล่องโต้ตอบใช่หรือไม่ใช่ง่าย

เมื่อคุณต้องการมุมมองที่ซับซ้อนมากขึ้นซึ่งคุณต้องได้รับวงจรชีวิตเช่น oncreate, ร้องขอสิทธิ์, แทนที่วงจรชีวิตใด ๆ ที่ฉันจะใช้ส่วนโต้ตอบ ดังนั้นคุณจึงแยกสิทธิ์และรหัสอื่น ๆ ที่ไดอะล็อกต้องการใช้งานโดยไม่ต้องสื่อสารกับกิจกรรมการโทร

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.