แสดงกล่องโต้ตอบจากส่วนย่อยหรือไม่


119

ฉันมีชิ้นส่วนบางอย่างที่ต้องแสดงกล่องโต้ตอบปกติ ในกล่องโต้ตอบเหล่านี้ผู้ใช้สามารถเลือกคำตอบใช่ / ไม่ใช่จากนั้นส่วนควรทำงานตามนั้น

ตอนนี้Fragmentชั้นไม่ได้มีวิธีการที่จะแทนที่ดังนั้นผมคิดว่าผมต้องใช้ไดอะล็อกนอกในที่มีonCreateDialog() Activityไม่เป็นไร แต่Activityจำเป็นต้องรายงานกลับคำตอบที่เลือกไปยังส่วนย่อย แน่นอนว่าฉันสามารถใช้รูปแบบการโทรกลับได้ที่นี่ดังนั้นแฟรกเมนต์จึงลงทะเบียนตัวเองที่Activityคลาสของผู้ฟังและกิจกรรมจะรายงานคำตอบกลับผ่านสิ่งนั้นหรืออะไรทำนองนั้น

แต่ดูเหมือนว่าจะค่อนข้างยุ่งยากสำหรับงานง่ายๆเนื่องจากการแสดงกล่องโต้ตอบใช่ไม่ใช่ "แบบธรรมดา" ในส่วนย่อย ด้วยวิธีนี้ฉันFragmentจะมีความเป็นตัวเองน้อยลง

มีวิธีที่สะอาดกว่านี้ไหม

แก้ไข:

คำตอบสำหรับคำถามนี้ไม่ได้อธิบายรายละเอียดว่าควรใช้ DialogFragments เพื่อแสดงกล่องโต้ตอบจาก Fragments อย่างไร AFAIK วิธีที่จะไปคือ:

  1. แสดงส่วนย่อย
  2. เมื่อจำเป็นให้สร้าง DialogFragment
  3. ตั้งค่า Fragment ดั้งเดิมเป็นเป้าหมายของ DialogFragment นี้ด้วย .setTargetFragment() .
  4. แสดง DialogFragment ด้วย. show () จาก Fragment ดั้งเดิม
  5. เมื่อผู้ใช้เลือกตัวเลือกบางอย่างใน DialogFragment นี้ให้แจ้ง Fragment ดั้งเดิมเกี่ยวกับการเลือกนี้ (เช่นผู้ใช้คลิก 'ใช่') คุณจะได้รับการอ้างอิงของ Fragment ดั้งเดิมด้วย .getTarget ()
  6. ปิด DialogFragment

1
เทคนิคของคุณใช้ได้ผลยกเว้นเมื่อเกิดการหมุนหน้าจอ ฉันได้รับแรงใกล้แล้ว ความคิดใด ๆ ?
Weston

@ เวสตันตรวจสอบคำตอบแรกโดย Zsombor: stackoverflow.com/questions/8235080/…
mightimaus

คำตอบ:


37

คุณควรใช้DialogFragmentแทน


9
น่าเสียดายที่วิธีนี้เป็นวิธีที่ละเอียดกว่าวิธีโต้ตอบที่มีการจัดการแบบคลาสสิกของการแก้ไข Android ก่อนหน้านี้เล็กน้อย แต่ตอนนี้เป็นวิธีที่ต้องการ คุณสามารถหลีกเลี่ยงการอ้างถึงActivityทั้งหมดได้โดยใช้putFragmentและgetFragmentวิธีการFragmentManagerอนุญาตให้DialogFragmentรายงานกลับโดยตรงไปยังส่วนการโทร (แม้หลังจากเปลี่ยนการวางแนวแล้ว)
Dave

จะเกิดอะไรขึ้นถ้าคุณมี ListFragment ที่ต้องการแสดง Dialogs ไม่สามารถขยายทั้งสองได้
marchinram

16
คลาสย่อย ListFragment จะใช้ DialogFragments โดยการสร้างอินสแตนซ์ใหม่ไม่ใช่โดยคลาสย่อย DialogFragment (DialogFragment คือกล่องโต้ตอบที่ใช้งานเป็น Fragment ไม่ใช่ Fragment ซึ่งอาจแสดง Dialogs)
nmr

4
โปรดเพิ่มตัวอย่างเพื่อให้เราเข้าใจ eaisly
Arpit Patel

35

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

ฉันเชื่อว่าการใช้กิจกรรมของแฟรกเมนต์เพื่อเป็นสื่อกลางระหว่างไดอะล็อกและแฟรกเมนต์เป็นตัวเลือกที่ดีกว่า


10
เนื่องจากแนวทางโต้ตอบที่มีการจัดการ ( onCreateDialog) กำลังจะถูกยกเลิกในไม่ช้าฉันจึงไม่เห็นด้วยและบอกว่านั่นDialogFragmentเป็นวิธีที่จะไป
Dave

4
คำตอบที่ยอมรับหมายถึงใช้ DialogFragment แทน Dialog ไม่ใช่แทน ListFragment, AFAICS
nmr

จริงๆแล้วสามารถใช้ส่วนของไดอะล็อกเป็นทั้งไดอะล็อกและ
Clive Jefferies

@CliveJefferies ทำได้ แต่ยังไม่ควรแสดงกล่องโต้ตอบอื่นจากภายในตัวเอง ฉันคิดว่าพวกที่นี่ตั้งคำถามผิด
Marcel Bro

@anoniim ขึ้นอยู่กับวิธีการใช้ส่วนโต้ตอบ หากใช้เป็นชิ้นส่วนปกติก็ใช้ได้ หากกำลังใช้เป็นกล่องโต้ตอบแสดงว่าใช่คุณไม่ควรแสดงกล่องโต้ตอบอื่น
Clive Jefferies

24

นี่คือตัวอย่างทั้งหมดของใช่ / ไม่ใช่ DialogFragment:

ห้องเรียน:

public class SomeDialog extends DialogFragment {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return new AlertDialog.Builder(getActivity())
            .setTitle("Title")
            .setMessage("Sure you wanna do this!")
            .setNegativeButton(android.R.string.no, new OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    // do nothing (will close dialog)
                }
            })
            .setPositiveButton(android.R.string.yes,  new OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    // do something
                }
            })
            .create();
    }
}

ในการเริ่มกล่องโต้ตอบ:

        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        // Create and show the dialog.
        SomeDialog newFragment = new SomeDialog ();
        newFragment.show(ft, "dialog");

คุณยังสามารถให้ชั้นเรียนใช้ onClickListener และใช้สิ่งนั้นแทนตัวฟังแบบฝังได้

ติดต่อกลับไปที่กิจกรรม

หากคุณต้องการใช้การโทรกลับนี่คือวิธีการทำในกิจกรรมของคุณ:

YourActivity extends Activity implements OnFragmentClickListener

และ

@Override
public void onFragmentClick(int action, Object object) {
    switch(action) {
        case SOME_ACTION:
        //Do your action here
        break;
    }
}

คลาสโทรกลับ:

public interface OnFragmentClickListener {
    public void onFragmentClick(int action, Object object);
}

จากนั้นในการดำเนินการโทรกลับจากชิ้นส่วนคุณต้องตรวจสอบให้แน่ใจว่ามีการแนบผู้ฟังดังนี้:

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    try {
        mListener = (OnFragmentClickListener) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString() + " must implement listeners!");
    }
}

และการโทรกลับจะดำเนินการดังนี้:

mListener.onFragmentClick(SOME_ACTION, null); // null or some important object as second parameter.

4
สิ่งนี้ไม่ได้อธิบายวิธีการเริ่มต้นจาก Fragment
akohout

@raveN คุณเพียงแค่โทรกลับไปที่กิจกรรมของคุณซึ่งจะเริ่มต้นส่วน
Warpzit

@Warpzit ขอบคุณสำหรับคำตอบที่ครอบคลุม ฉันกลัวที่จะสัมผัส FragmentManager ซึ่งฉันกำลังทำสิ่งที่ไม่เคารพนับถือด้วย ... ฉันจะส่งต่อเหตุการณ์คลิกกลับไปยัง Fragment (ที่ไม่ใช่ Dialog) ซึ่งจำเป็นต้องป้อนข้อมูลของผู้ใช้ได้อย่างไร BTW ไม่ควรเพิ่มธุรกรรมลงใน backstack หรือไม่?
kaay

@kaay จากกิจกรรมคุณสามารถเรียกใช้เมธอดสาธารณะใดก็ได้ในส่วนที่กำหนดซึ่งต้องการอินพุตใหม่ จากนั้นการอัปเดตเนื้อหาใหม่ควรทำได้ง่ายมาก
Warpzit

1
@RichardLeMesurier อันที่จริงเศษก็ขึ้น ๆ ลง ๆ
Warpzit

13

สำหรับฉันมันเป็นดังต่อไปนี้ -

MyFragment:

public class MyFragment extends Fragment implements MyDialog.Callback
{
    ShowDialog activity_showDialog;

    @Override
    public void onAttach(Activity activity)
    {
        super.onAttach(activity);
        try
        {
            activity_showDialog = (ShowDialog)activity;
        }
        catch(ClassCastException e)
        {
            Log.e(this.getClass().getSimpleName(), "ShowDialog interface needs to be     implemented by Activity.", e);
            throw e;
        }
    }

    @Override
    public void onClick(View view) 
    {
        ...
        MyDialog dialog = new MyDialog();
        dialog.setTargetFragment(this, 1); //request code
        activity_showDialog.showDialog(dialog);
        ...
    }

    @Override
    public void accept()
    {
        //accept
    }

    @Override
    public void decline()
    {
        //decline
    }

    @Override
    public void cancel()
    {
        //cancel
    }

}

MyDialog:

public class MyDialog extends DialogFragment implements View.OnClickListener
{
    private EditText mEditText;
    private Button acceptButton;
    private Button rejectButton;
    private Button cancelButton;

    public static interface Callback
    {
        public void accept();
        public void decline();
        public void cancel();
    }

    public MyDialog()
    {
        // Empty constructor required for DialogFragment
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        View view = inflater.inflate(R.layout.dialogfragment, container);
        acceptButton = (Button) view.findViewById(R.id.dialogfragment_acceptbtn);
        rejectButton = (Button) view.findViewById(R.id.dialogfragment_rejectbtn);
        cancelButton = (Button) view.findViewById(R.id.dialogfragment_cancelbtn);
        acceptButton.setOnClickListener(this);
        rejectButton.setOnClickListener(this);
        cancelButton.setOnClickListener(this);
        getDialog().setTitle(R.string.dialog_title);
        return view;
    }

    @Override
    public void onClick(View v)
    {
        Callback callback = null;
        try
        {
            callback = (Callback) getTargetFragment();
        }
        catch (ClassCastException e)
        {
            Log.e(this.getClass().getSimpleName(), "Callback of this class must be implemented by target fragment!", e);
            throw e;
        }

        if (callback != null)
        {
            if (v == acceptButton)
            {   
                callback.accept();
                this.dismiss();
            }
            else if (v == rejectButton)
            {
                callback.decline();
                this.dismiss();
            }
            else if (v == cancelButton)
            {
                callback.cancel();
                this.dismiss();
            }
        }
    }
}

กิจกรรม:

public class MyActivity extends ActionBarActivity implements ShowDialog
{
    ..

    @Override
    public void showDialog(DialogFragment dialogFragment)
    {
        FragmentManager fragmentManager = getSupportFragmentManager();
        dialogFragment.show(fragmentManager, "dialog");
    }
}

เค้าโครง DialogFragment:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/dialogfragment_textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="10dp"
        android:text="@string/example"/>

    <Button
        android:id="@+id/dialogfragment_acceptbtn"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:layout_centerHorizontal="true"
        android:layout_below="@+id/dialogfragment_textview"
        android:text="@string/accept"
        />

    <Button
        android:id="@+id/dialogfragment_rejectbtn"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_alignLeft="@+id/dialogfragment_acceptbtn"
        android:layout_below="@+id/dialogfragment_acceptbtn"
        android:text="@string/decline" />

     <Button
        android:id="@+id/dialogfragment_cancelbtn"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="20dp"
        android:layout_alignLeft="@+id/dialogfragment_rejectbtn"
        android:layout_below="@+id/dialogfragment_rejectbtn"
        android:text="@string/cancel" />

     <Button
        android:id="@+id/dialogfragment_heightfixhiddenbtn"
        android:layout_width="200dp"
        android:layout_height="20dp"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="20dp"
        android:layout_alignLeft="@+id/dialogfragment_cancelbtn"
        android:layout_below="@+id/dialogfragment_cancelbtn"
        android:background="@android:color/transparent"
        android:enabled="false"
        android:text=" " />
</RelativeLayout>

ตามชื่อที่dialogfragment_heightfixhiddenbtnแสดงฉันไม่สามารถหาวิธีแก้ไขได้ว่าความสูงของปุ่มด้านล่างถูกลดลงครึ่งหนึ่งแม้จะมีคำพูดwrap_contentก็ตามดังนั้นฉันจึงเพิ่มปุ่มที่ซ่อนอยู่เพื่อให้ "ตัด" ลงครึ่งหนึ่งแทน ขออภัยสำหรับการแฮ็ค


1
+1 ชุดการอ้างอิงsetTargetFragment()จะถูกสร้างขึ้นใหม่อย่างถูกต้องโดยระบบเมื่อรีสตาร์ทชุดกิจกรรม / ส่วนหลังจากการหมุน ดังนั้นการอ้างอิงจะชี้ไปที่เป้าหมายใหม่โดยอัตโนมัติ
Richard Le Mesurier

ตอนนี้ฉันจะเจาะจมูกตัวเองเพราะไม่ได้ใช้ ButterKnife ในตอนกลางวัน ใช้ ButterKnife ทำให้มุมมองของคุณสวยขึ้นมาก
EpicPandaForce

และคุณสามารถใช้ Otto เพื่อส่งเหตุการณ์จาก Fragment ไปยัง Activity เพื่อที่คุณจะได้ไม่ต้องใช้อินเทอร์เฟซแนบเวทย์มนตร์ ตัวอย่าง Otto ที่นี่: stackoverflow.com/a/28480952/2413303
EpicPandaForce

@EpicPandaForce ได้โปรดเพิ่มอินเทอร์เฟซ / คลาส "ShowDialog" ได้ไหม สิ่งเดียวที่ขาดหายไปในตัวอย่างของคุณ
ntrch

@ntrchpublic interface ShowDialog { void showDialog(DialogFragment dialogFragment); }
EpicPandaForce

3
 public void showAlert(){


     AlertDialog.Builder alertDialog = new AlertDialog.Builder(getActivity());
     LayoutInflater inflater = getActivity().getLayoutInflater();
     View alertDialogView = inflater.inflate(R.layout.test_dialog, null);
     alertDialog.setView(alertDialogView);

     TextView textDialog = (TextView) alertDialogView.findViewById(R.id.text_testDialogMsg);
     textDialog.setText(questionMissing);

     alertDialog.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
         public void onClick(DialogInterface dialog, int which) {
             dialog.cancel();
         }
     });
     alertDialog.show();

}

โดยที่. test_dialog เป็นของ xml ที่กำหนดเอง


2

ฉันเป็นมือใหม่และฉันไม่สามารถหาคำตอบที่น่าพอใจที่ฉันสามารถเข้าใจหรือนำไปใช้ได้จริง

นี่คือลิงค์ภายนอกที่ช่วยให้ฉันบรรลุสิ่งที่ต้องการได้จริงๆ ตรงไปตรงมามากและง่ายต่อการติดตามเช่นกัน

http://www.helloandroid.com/tutorials/how-display-custom-dialog-your-android-application

นี่คือสิ่งที่ฉันพยายามที่จะบรรลุด้วยรหัส:

ฉันมี MainActivity ที่โฮสต์ Fragment ฉันต้องการให้กล่องโต้ตอบปรากฏที่ด้านบนของเค้าโครงเพื่อขอข้อมูลจากผู้ใช้จากนั้นประมวลผลอินพุตตามนั้น ดูภาพหน้าจอ

นี่คือลักษณะของ onCreateView ของส่วนของฉัน

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

    View rootView = inflater.inflate(R.layout.fragment_home_activity, container, false);

    Button addTransactionBtn = rootView.findViewById(R.id.addTransactionBtn);

    addTransactionBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Dialog dialog = new Dialog(getActivity());
            dialog.setContentView(R.layout.dialog_trans);
            dialog.setTitle("Add an Expense");
            dialog.setCancelable(true);

            dialog.show();

        }
    });

ฉันหวังว่ามันจะช่วยคุณได้

โปรดแจ้งให้เราทราบหากมีความสับสน :)


0
    public static void OpenDialog (Activity activity, DialogFragment fragment){

    final FragmentManager fm = ((FragmentActivity)activity).getSupportFragmentManager();

    fragment.show(fm, "tag");
}

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