【问题标题】:Implement DialogFragment interface in OnClickListener在 OnClickListener 中实现 DialogFragment 接口
【发布时间】:2015-04-21 14:29:00
【问题描述】:

我需要构建一个 DialogFragment,它将用户输入从对话框返回到活动。 对话框需要在 OnClickListener 中调用,当单击列表视图中的元素时调用该对话框。
DialogFragment的返回值(用户的输入)应该在activity的OnClickListener中直接可用。

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

我需要类似下面的东西,但它不起作用,因为我不知道如何让匿名 OnClickListener 实现 CustomNumberPicker 类的接口。
据我所知,实现接口对于将数据从 DialogFragment 获取回 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();
    }
}

【问题讨论】:

  • 您是否有正在使用的自定义布局?您要返回什么?您想返回什么?
  • 当对话框按钮被点击时你想返回什么?是字符串还是什么的?
  • @Droidekas 是的,它是一个显示 NumberPicker 的自定义 DialogFragment。我正在尝试返回一个整数,它是用户选择的数字。
  • 可以查看this answer.

标签: java android android-listview onclicklistener android-dialogfragment


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    这是如何完成的: 在显示 DiaogFragment 的 Activity 中,将 DialogFragment 的参数设置为所需的名称值对。 还要确保活动实现了 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 中的活动。 如果需要将数据发送回活动;设置 buddle 参数,然后调用 onClickListener.onClick

    公共类 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");
    
        }
    
    }
    

    }

    【讨论】:

      【解决方案3】:

      例如,您可以使用DatePickerDialog,其中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();
          }
      } 
      

      【讨论】:

        【解决方案4】:

        这样的?

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

        请阅读我添加的 cmets。它甚至可以进一步优化,因为您真的不需要整个对话框实例来获取所需的值。

        编辑可能的优化可能是:

        将监听器接口更改为:

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

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

        【讨论】:

        • 为什么不让适配器实现回调接口呢?在我看来,更清洁和易于管理。
        • 因为OP指定它应该是onClick监听器。基本上它可以在adapter,activity,ListView.onClick()或activity中的匿名监听器中实现。这取决于选择的OP.我刚刚修改了监听器,使它可以在任何地方插入
        • @Athena 在更合适的级别实现它需要您根据位置处理从列表中获取并修改它。在getView isnt 中处理它更加上下文/就地是吗?
        • 我在尝试添加自定义构造函数时在 AS 中收到错误/警告,并且文档中提到应该改用 setArguments(Bundle)。这个解决方案“安全”吗?我感觉屏幕旋转事件可能会破坏这一点?
        • 您也可以通过this answer 看到差异。至于您对屏幕旋转的疑问,this answer 表示您的观点的有效性。但是您可以通过实现onSaveInstance 轻松克服此问题和onRestoreInstance
        【解决方案5】:

        你应该有你的活动,实现你的接口 (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 并调用方法,这些方法现在已在活动中实现,代码将在那里执行。

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

        【讨论】:

        • 嗯,我想你误解了这个问题,或者我没有把它表述得足够清楚。我需要 ArrayAdapter 中 tvsets 对象的 OnClickListener 中的 NoticeDialogListener 接口。这是因为我必须将 DialogFragment 的返回值附加到 ArrayAdapter 中的元素。
        猜你喜欢
        • 2018-07-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-07
        • 2021-07-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多