在 OnClickListener 中实现 DialogFragment 接口

Implement DialogFragment interface in OnClickListener

我需要构建一个 DialogFragment,其中 return 用户从对话框输入到 activity。 该对话框需要在 OnClickListener 中调用,当单击列表视图中的元素时会调用该对话框。
DialogFragment 的 return 值(用户的输入)应该在 activity.

的 OnClickListener 中直接可用

我试图通过坚持官方文档来实现这一点:http://developer.android.com/guide/topics/ui/dialogs.html#PassingEvents

我需要像下面这样的东西,但它不起作用,因为我不知道如何让匿名 OnClickListener 实现 CustomNumberPicker 的接口 class。
据我所知,为了将数据从 DialogFragment 返回到 Activity.

,实现接口是必要的

主要Activity:

public class MainAcitivity extends ActionBarActivity {
    [...]

    // ArrayAdapter of the Listview
    private class ListViewArrayAdapter extends ArrayAdapter<Exercise> {
        public ListViewArrayAdapter(Context context, ArrayList<Exercise> exercises) {
            super(context, 0, exercises);
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            [...]

            if (convertView == null) {
                convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_workoutdetail, parent, false);
            }

            TextView tvSets = (TextView) convertView.findViewById(R.id.tvWorkoutExerciseSets);
            tvSets.setText(sets.toString());

            // OnClickListener for every element in the ListView
            tvSets.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // This is where the Dialog should be called and
                    // the user input from the Dialog should be returned
                    DialogFragment numberpicker = new CustomNumberPicker();
                    numberpicker.show(MainActivity.this.getSupportFragmentManager(), "NoticeDialogFragment");
                }

                // Here I would like to implement the interface of CustomNumberPicker
                // in order to get the user input entered in the Dialog
            });

            return convertView;
        }
    }
}

CustomNumberPicker(与文档中的基本相同):

public class CustomNumberPicker extends DialogFragment {

    public interface NoticeDialogListener {
        public void onDialogPositiveClick(DialogFragment dialog);
        public void onDialogNegativeClick(DialogFragment dialog);
    }

    // Use this instance of the interface to deliver action events
    NoticeDialogListener mListener;

    // Override the Fragment.onAttach() method to instantiate the NoticeDialogListener
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        // Verify that the host activity implements the callback interface
        try {
            // Instantiate the NoticeDialogListener so we can send events to the host
            mListener = (NoticeDialogListener) activity;
        } catch (ClassCastException e) {
            // The activity doesn't implement the interface, throw exception
            throw new ClassCastException(activity.toString()
                + " must implement NoticeDialogListener");
        }
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Use the Builder class for convenient dialog construction
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setMessage("Sets")
            .setPositiveButton("set", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        // Return stuff here to the activity?
                    }
                })
                .setNegativeButton("cancle", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        // User cancelled the dialog
                    }
                });
        // Create the AlertDialog object and return it
        return builder.create();
    }
}

您应该 activity,实现您的接口 (NoticeDialogListener)。

public class MainActivity extends ActionBarActivity implements
    NoticeDialogListener{

    @Override
    public void onDialogPositiveClick(DialogFragment dialog){
        //Do something
    }

    @Override
    public void onDialogNegativeClick(DialogFragment dialog){
        //Do some other things
    }

    [...]
}

然后在您的按钮单击对话框的侦听器中,您使用 mListener 并调用方法,现在在 activity 中实现,代码将在那里执行。

builder.setMessage("Sets")
            .setPositiveButton("set", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        if(mListener != null)
                            mListener.onDialogPositiveClick(CustomNumberPicker.this);
                    }
            });

另请注意,您应该在 DialogFragment 的 onDetach() 方法中将 mListener 设置为 null。

@Override
public void onDetach() {
    super.onDetach();
    mListener = null;
}

是这样的吗?

public class CustomNumberPicker extends DialogFragment {
    private NoticeDialogListener ndl;

    public interface NoticeDialogListener {
        public void onDialogPositiveClick(DialogFragment dialog);
        public void onDialogNegativeClick(DialogFragment dialog);
    }

    //add a custom constructor so that you have an initialised NoticeDialogListener
    public CustomNumberPicker(NoticeDialogListener ndl){
        super();
            this.ndl=ndl;
    }

    //make sure you maintain an empty constructor
    public CustomNumberPicker( ){
        super();
    }

    // Use this instance of the interface to deliver action events
    NoticeDialogListener mListener;

    // Override the Fragment.onAttach() method to instantiate the NoticeDialogListener
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        //remove the check that verfis if your activity has the DialogListener Attached because you want to attach it into your list view onClick()
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Use the Builder class for convenient dialog construction
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setMessage("Sets")
            .setPositiveButton("set", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        ndl.onDialogPositiveClick(dialog);
                    }
                })
                .setNegativeButton("cancle", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                       ndl.onDialogNegativeClick(dialog);
                    }
                });
        // Create the AlertDialog object and return it
        return builder.create();
    }
}

然后您的 listView onClick 变为:

tvSets.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // This is where the Dialog should be called and
                    // the user input from the Dialog should be returned
                    // 
                    // 


                    DialogFragment numberpicker = new CustomNumberPicker(new NoticeDialogListener() {

            @Override
            public void onDialogPositiveClick(DialogFragment dialog) {
                //What you want to do incase of positive click

            }

            @Override
            public void onDialogNegativeClick(DialogFragment dialog) {
               //What you want to do incase of negative click

            }
        };);
                    numberpicker.show(MainActivity.this.getSupportFragmentManager(), "NoticeDialogFragment");
                }

                // Here I would like to implement the interface of CustomNumberPicker
                // in order to get the user input entered in the Dialog
            });

请阅读我的评论 added.And 它甚至可以进一步优化,因为您真的不需要整个对话框实例来获取您需要的值。

编辑 可能的优化可能是:

将侦听器接口更改为:

public interface NoticeDialogListener {
        public void onDialogPositiveClick(String output);
        public void onDialogNegativeClick(String output);
       //or whatever form of output that you want
    }

然后相应地修改实现的方法。

例如,您可以使用 DatePickerDialog where DatePickerDialog.OnDateSetListener 来传递结果。

或者这是我的实现之一,允许保持对话框屏幕打开,直到用户未完成某些操作或未输入有效数据。使用为该对话框提供准确接口的自定义回调。

public class ConfirmPasswordDialog extends DialogFragment {
    private OnPaswordCheckResult resultListener;
    private TextView passwordView;

    public ConfirmPasswordDialog(OnPaswordCheckResult resultListener){
        this.resultListener = resultListener;
    }

    @Override
    public android.app.Dialog onCreateDialog(Bundle savedInstanceState) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
            LayoutInflater inflater = getActivity().getLayoutInflater();
            View dialogView  = inflater.inflate(R.layout.dialog_layout, null);
            builder.setView(dialogView);
            passwordView = (TextView) dialogView.findViewById(R.id.password);
            passwordView.addTextChangedListener(new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {/*do nothing*/}

                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {/*do nothing*/}

                @Override
                public void afterTextChanged(Editable s) {
                    if(passwordView != null){
                        passwordView.setError(null);
                    }
                }
            });
            builder.setView(dialogView);
            builder.setMessage("Please enter password to finish with action");
            builder.setPositiveButton("Confirm", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    /* do something when click happen, in this case mostly like dummy because data return later
                    * after validation or immediately if required*/
                }
            });
            builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    dialog.cancel();
                }
            });
            builder.setTitle("Confirm password");
        final AlertDialog dialog = builder.create();
        dialog.setOnShowListener(new DialogInterface.OnShowListener() {
            @Override
            public void onShow(final DialogInterface dialogInterface) {
                Button positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
                positiveButton.setOnClickListener(new View.OnClickListener(){

                    @Override
                    public void onClick(View view) {
                        if(passwordView == null || !isAdded()){
                            return;
                        }
                        String password = passwordView.getText().toString();
                        if(PrefUtils.isPasswordValid(getActivity(), password)){
                            if(resultListener == null){
                                return;
                            }
                            /* Return result and dismiss dialog*/
                            resultListener.onValidPassword();
                            dialog.dismiss();
                        } else {
                            /* Show an error if entered password is invalid and keep dialog
                            * shown to the user*/
                            String error = getActivity().getString(R.string.message_password_not_valid);
                            passwordView.setError(error);
                        }
                    }
                });
            }
        });
        return dialog;
    }

    /**
     * Custom callback to return result if entered password is valid
     */
    public static interface OnPaswordCheckResult{
        void onValidPassword();
    }
} 

这是如何完成的: 在显示 DiaogFragment 的 Activity 中,使用所需的名称值对设置 DialogFragment 的参数。 还要确保 activity 实现了 DialogInterface.OnClickListener 在覆盖的 onClick 中,从上述名称值对中获取值

public class MainActivity extends AppCompatActivity implements DialogInterface.OnClickListener {

        private static SettingsFragment settingsFragment;
        private Button btnSettings;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main_activity);

            btnSettings = (Button) findViewById(R.id.btnSettings);

            btnSettings.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {

                    settingsFragment = new SettingsFragment();
                    Bundle bundle = new Bundle();
                    bundle.putString("myKey", null);
                    settingsFragment.setArguments(bundle);
                    //Use the commented out line below if you want the click listener to return to a fragment instead of an activity
                    //assuming that this class in a fragment and not an activity
                    //rotateSettingsFragment.setTargetFragment(getActivity().getSupportFragmentManager().findFragmentByTag("TagForThisFragment"), 0);
                    settingsFragment.setTargetFragment(settingsFragment, 0);
                    settingsFragment.setCancelable(true);
                    settingsFragment.show(getSupportFragmentManager(), "SettingsFragment");

                }
            });

        }

        @Override
        public void onClick(DialogInterface dialog, int which) {

            if(getResources().getResourceEntryName(which).equals("btnSettingFragmentClose")) {

                String myValue = settingsFragment.getArguments().getString("myKey");
                dialog.dismiss();

            }

        }

    }

在您的 DialogFragment 中声明一个 DialogInterface.OnClickListener 并将其转换为 onAttach 中的 activity。 如果需要将数据发回activity;设置 buddle 参数,然后调用 onClickListener.onClick

public class SettingsFragment 扩展 DialogFragment {

private View rootView;
private Button btnSettingFragmentClose;
private DialogInterface.OnClickListener onClickListener;

public SettingsFragment() {}

/* Uncomment this and comment out on onAttach when you want to return to a fragment instead of an activity.
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    onClickListener = (DialogInterface.OnClickListener) getTargetFragment();

}
*/

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

    rootView = inflater.inflate(R.layout.fragment_settings, container, false);
    btnSettingFragmentClose = (Button) rootView.findViewById(R.id.btnSettingFragmentClose);

    btnSettingFragmentClose.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            getArguments().putString("myKey", "Hello World!");
            onClickListener.onClick(getDialog(), v.getId());

        }
    });

    return rootView;

}


@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);

    try {

        onClickListener = (DialogInterface.OnClickListener) activity;

    }
    catch (ClassCastException e) {

        throw new ClassCastException(activity.toString() + " must implement mainFragmentCallback");

    }

}

}

这个简单的解决方案对我有用:

public class MyActivity implements MyDialogFragment.Listener {

    // ...

    @Override
    public void onMyEvent() {
        // do something here
    }
}

public class MyDialogFragment extends DialogFragment {

    private Listener mCallback;
    public interface Listener {
        void onMyEvent();
    }

    @SuppressLint("RestrictedApi")
    @Override
    public void setupDialog(final Dialog dialog, int style) {
        super.setupDialog(dialog, style);
        View contentView = View.inflate(getContext(), R.layout.dialog_fragment_custom, null);
        dialog.setContentView(contentView);

        mCallback = (Listener) getActivity();

        Button myBtn = (Button) dialog.findViewById(R.id.btn_custom);
        myBtn.setOnClickListener(v -> {
            mCallback.onMyEvent();
            dismiss();
        });
    }
}