Hiển thị hộp thoại từ phân mảnh?


119

Tôi có một số đoạn cần hiển thị hộp thoại thông thường. Trên các hộp thoại này, người dùng có thể chọn câu trả lời có / không và sau đó phân đoạn sẽ hoạt động tương ứng.

Bây giờ, Fragmentlớp không có onCreateDialog()phương thức để ghi đè, vì vậy tôi đoán tôi phải triển khai các hộp thoại bên ngoài, trong phần chứa Activity. Không sao cả, nhưng sau đó Activitycần phải báo cáo lại câu trả lời đã chọn bằng cách nào đó cho phân mảnh. Tất nhiên, tôi có thể sử dụng một mẫu gọi lại ở đây, do đó, phân đoạn tự đăng ký tại lớp Activityvới một người nghe và Activity sẽ báo cáo lại câu trả lời thông qua điều đó, hoặc tương tự như vậy.

Nhưng điều này có vẻ là một mớ hỗn độn đối với một nhiệm vụ đơn giản như hiển thị hộp thoại có-không "đơn giản" trong một phân đoạn. Ngoài ra, theo cách này, tôi Fragmentsẽ bớt khép kín hơn.

Có một số cách dễ dàng hơn để làm điều này?

Biên tập:

Câu trả lời cho câu hỏi này không thực sự giải thích chi tiết cách người ta nên sử dụng DialogFragment để hiển thị hộp thoại từ Fragment. Vì vậy, AFAIK, cách để đi là:

  1. Hiển thị một Fragment.
  2. Khi cần, hãy khởi tạo DialogFragment.
  3. Đặt Fragment ban đầu làm mục tiêu của DialogFragment này, với .setTargetFragment().
  4. Hiển thị DialogFragment với .show () từ Fragment ban đầu.
  5. Khi người dùng chọn một số tùy chọn trên DialogFragment này, thông báo cho Fragment ban đầu về lựa chọn này (ví dụ: người dùng nhấp vào 'có'), bạn có thể nhận được tham chiếu của Fragment ban đầu với .getTarget ().
  6. Loại bỏ DialogFragment.

1
Kỹ thuật của bạn hoạt động, trừ khi xảy ra xoay màn hình. Tôi nhận được một lực lượng gần sau đó. Có ý kiến ​​gì không?
Weston

@Weston xem câu trả lời đầu tiên của Zsombor: stackoverflow.com/questions/8235080/…
mightimaus,

Câu trả lời:


37

Bạn nên sử dụng DialogFragment để thay thế.


9
Thật không may, cách tiếp cận này dài dòng hơn một chút so với cách tiếp cận hộp thoại được quản lý cổ điển của các phiên bản Android trước đây, nhưng nó hiện là phương pháp được ưa thích. Bạn có thể tránh tham chiếu Activityhoàn toàn bằng cách sử dụng các phương thức putFragmentgetFragmentcủa FragmentManager, cho phép DialogFragmentbáo cáo trực tiếp trở lại phân đoạn đang gọi (ngay cả sau khi thay đổi hướng).
Dave

Điều gì nếu bạn có một ListFragment rằng nhu cầu để hiển thị Dialogs, không thể mở rộng cả hai
marchinram

16
Lớp con ListFragment sẽ sử dụng DialogFragment bằng cách khởi tạo những cái mới, chứ không phải bằng cách phân lớp con DialogFragment. (Một DialogFragment là một hộp thoại được triển khai dưới dạng Fragment, không phải Fragment có thể hiển thị các Hộp thoại.)
nmr

4
Vui lòng thêm một số đoạn mã để chúng tôi có thể hiểu rõ ràng
Arpit Patel

35

Tôi phải thận trọng nghi ngờ câu trả lời đã được chấp nhận trước đó rằng sử dụng DialogFragment là lựa chọn tốt nhất. Mục đích dự kiến ​​(chính) của DialogFragment dường như là hiển thị các đoạn chính hộp thoại, chứ không phải để hiển thị các đoạn hộp thoại để hiển thị.

Tôi tin rằng sử dụng hoạt động của phân đoạn để làm trung gian giữa hộp thoại và phân đoạn là tùy chọn thích hợp hơn.


10
onCreateDialogphương pháp tiếp cận hộp thoại được quản lý ( ) sắp không còn được dùng nữa, tôi không đồng ý và nói rằng đó DialogFragmentthực sự là cách để đi.
Dave

4
Câu trả lời được chấp nhận có nghĩa là sử dụng DialogFragment thay vì Dialog, chứ không phải thay vì ListFragment, AFAICS.
nmr

Thực sự là một đoạn hội thoại có thể được sử dụng như là cả một hộp thoại và một đoạn bình thường - xem nhúng developer.android.com/reference/android/app/DialogFragment.html
Clive Jefferies

@CliveJefferies Nó có thể, nhưng vẫn không được hiển thị các hộp thoại khác từ bên trong chính nó. Tôi nghĩ những người ở đây đang đặt câu hỏi sai.
Marcel Bro

@anoniim nó phụ thuộc vào cách phân đoạn hộp thoại đang được sử dụng. Nếu nó được sử dụng như một mảnh thông thường thì nó là tốt. Nếu nó đang được sử dụng như một hộp thoại, thì có, bạn sẽ không hiển thị một hộp thoại khác.
Clive Jefferies

24

Dưới đây là một ví dụ đầy đủ về DialogFragment có / không:

Lớp:

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

Để bắt đầu hộp thoại:

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

Bạn cũng có thể cho phép lớp triển khai onClickListener và sử dụng nó thay vì các trình nghe được nhúng.

Gọi lại hoạt động

Nếu bạn muốn triển khai callback, đây là cách nó được thực hiện Trong hoạt động của bạn:

YourActivity extends Activity implements OnFragmentClickListener

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

Lớp gọi lại:

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

Sau đó, để thực hiện gọi lại từ một phân đoạn, bạn cần đảm bảo rằng người nghe được đính kèm như sau:

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

Và một cuộc gọi lại được thực hiện như thế này:

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

4
Điều này không giải thích làm thế nào để bắt đầu nó từ một Fragment
akohout

@raveN, bạn chỉ cần thực hiện một cuộc gọi lại hoạt động của mình mà sau đó sẽ bắt đầu phân đoạn.
Warpzit

@Warpzit Cảm ơn bạn đã trả lời toàn diện. Tôi sợ chạm vào FragmentManager, cái mà tôi đang làm những việc vô duyên với ... Làm cách nào để chuyển các sự kiện onClick trở lại Fragment (không phải Dialog) mà người dùng cần nhập? BTW, giao dịch có nên được thêm vào backstack không?
kaay

@kaay từ hoạt động, bạn có thể gọi bất kỳ phương thức công khai nào trong phân đoạn đã cho cần đầu vào mới. Từ đó, nó sẽ khá dễ dàng cập nhật với nội dung mới.
Warpzit,

1
@RichardLeMesurier Thật vậy, những mảnh vỡ là những thăng trầm.
Warpzit

13

Đối với tôi, nó là như sau-

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

Hoạt động:

public class MyActivity extends ActionBarActivity implements ShowDialog
{
    ..

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

Bố cục 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>

Như tên dialogfragment_heightfixhiddenbtncho thấy, tôi không thể tìm ra cách để khắc phục rằng chiều cao của nút dưới cùng bị cắt một nửa mặc dù đã nói wrap_content, vì vậy tôi đã thêm một nút ẩn để được "cắt" làm đôi. Xin lỗi vì vụ hack.


1
+1 tập tham chiếu với setTargetFragment()được tạo lại chính xác bởi hệ thống khi nó khởi động lại tập hợp Hoạt động / Phân mảnh sau khi xoay. Vì vậy, tham chiếu sẽ tự động trỏ đến mục tiêu mới.
Richard Le Mesurier

Mặc dù vậy, tôi đã tự đấm vào mũi mình vì đã không sử dụng ButterKnife trong ngày hôm đó. Sử dụng ButterKnife, nó làm cho việc xử lý chế độ xem của bạn đẹp hơn nhiều.
EpicPandaForce

Và bạn có thể sử dụng Otto để gửi sự kiện từ Fragment sang Activity, do đó bạn không cần giao diện đính kèm ma thuật. Ví dụ về Otto ở đây: stackoverflow.com/a/28480952/2413303
EpicPandaForce

@EpicPandaForce Làm ơn, bạn có thể thêm Giao diện / Lớp "ShowDialog" không? Đó là điều duy nhất còn thiếu trong ví dụ của bạn.
ntrch

@ntrch nó làpublic 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();

}

trong đó .test_dialog thuộc xml tùy chỉnh


2

Bản thân tôi là người mới bắt đầu và thành thật mà nói, tôi không thể tìm thấy câu trả lời thỏa đáng mà tôi có thể hiểu hoặc thực hiện.

Vì vậy, đây là một liên kết bên ngoài mà tôi thực sự đã giúp tôi đạt được những gì tôi muốn. Nó rất thẳng về phía trước và dễ theo dõi.

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

ĐIỀU NÀY TÔI ĐÃ THỬ ĐỂ ĐẠT ĐƯỢC VỚI MÃ:

Tôi có một MainActivity lưu trữ một Fragment. Tôi muốn một hộp thoại xuất hiện trên cùng của bố cục để yêu cầu người dùng nhập và sau đó xử lý đầu vào tương ứng. Xem ảnh chụp màn hình

Đây là giao diện của onCreateView trong phân đoạn của tôi

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

        }
    });

Tôi hy vọng nó sẽ giúp bạn

Hãy cho tôi biết nếu có bất kỳ sự nhầm lẫn nào. :)


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

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

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

3
xin vui lòng thêm một số Giải thích câu trả lời của bạn
RealCheeseLord
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.