[Android] 更好的解决 "返回键或取消时自动回调DatePickerDialog的方法onDateSet()" 的问题

自从忙完工作变动的事情后好久没写博了, 内心愧疚啊.... 说好的坚持学习呢... TAT


好吧, 回归正题.  用过 Android自带的DatePickerDialog的默认样式是这样的:

[Android] 更好的解决


只有一个 "完成" 按钮...

如果将完成选择日期的触发事件放在方法onDateSet(), 那么无论是点击返回键或屏幕outSide的地方,  或者点击 "完成"按钮, 都会自动取消这个日期选择弹出框时并每次调用onDateSet(). 就是说, 这时, 放在onDateSet()里面的触发事件无论如何都会被调用啊, 简直不能忍...  要是弹出框的界面除了 "完成" 按钮, 还有 "取消" 按钮, 并且能控制什么时候才去调用完成日期选择的触发事件,  作为社会主义接班人的我们, 该会多开心啊~~~


那么问题来了, 除了自定一个DatePickerDialog, 比如有种做法是继承DatePickerDialog, 然后在onStop() 里面去注释里面的super.onStop() 达到不调用 onDateSet() 的目的.  但自带的DatePickerDialog究竟支不支持我们上面的需求呢?       真相只有一个:


阔以的~


我们翻开DatePickerDialog所继承的AlertDialog类源码,  其实发现里面提供了setButton(...)这样的方法, 

/**
     * Set a listener to be invoked when the positive button of the dialog is pressed.
     * 
     * @param whichButton Which button to set the listener on, can be one of
     *            {@link DialogInterface#BUTTON_POSITIVE},
     *            {@link DialogInterface#BUTTON_NEGATIVE}, or
     *            {@link DialogInterface#BUTTON_NEUTRAL}
     * @param text The text to display in positive button.
     * @param listener The {@link DialogInterface.OnClickListener} to use.
     */
    public void setButton(int whichButton, CharSequence text, OnClickListener listener) {
        mAlert.setButton(whichButton, text, listener, null);
    }
貌似只需要传入三个参数 控件ID, 控件显示名称, 和回调的接口方法就行了.


然后, 用代码去见证"奇迹的一刻"吧. 哈哈哈~

final Calendar c = Calendar.getInstance();
        mYear = c.get(Calendar.YEAR);
        mMonth = c.get(Calendar.MONTH);
        mDay = c.get(Calendar.DAY_OF_MONTH);

        final DatePickerDialog dpd = new DatePickerDialog(context,
                new DatePickerDialog.OnDateSetListener() {
                    @Override
                    public void onDateSet(DatePicker view, int year, int month,
                                          int day) {
                    }
                }, mYear, mMonth, mDay);

        Button btnConfirm = new Button(getActivity());
        Button btnCancel = new Button(getActivity());


        dpd.setButton(btnConfirm.getId(), "btnConfirm", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Toast.makeText(getActivity(), "点击\nbtnConfirm", Toast.LENGTH_SHORT).show();
            }
        });

        dpd.setButton(btnCancel.getId(), "btnCancel", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Toast.makeText(getActivity(), "点击\nbtnCancel", Toast.LENGTH_SHORT).show();
            }
        });
        dpd.setTitle("选择日期");
        dpd.show();
信心满满的运行, 看到弹出框, 还是只有一个按钮, 按钮显示的名称为 "btnCancel" .  纳尼 什么鬼? 这真的是我想要的吗?


这里我们先来看看 setButton() 是如何添加按钮和回调事件的.

点进去 setButton() 方法, 发现调用的是AlertController 的 setButton() 方法:

/** 
     * Sets a click listener or a message to be sent when the button is clicked. 
     * You only need to pass one of {@code listener} or {@code msg}. 
     *  
     * @param whichButton Which button, can be one of 
     *            {@link DialogInterface#BUTTON_POSITIVE}, 
     *            {@link DialogInterface#BUTTON_NEGATIVE}, or 
     *            {@link DialogInterface#BUTTON_NEUTRAL} 
     * @param text The text to display in positive button. 
     * @param listener The {@link DialogInterface.OnClickListener} to use. 
     * @param msg The {@link Message} to be sent when clicked. 
     */  
    public void setButton(int whichButton, CharSequence text,  
            DialogInterface.OnClickListener listener, Message msg) {  
  
        if (msg == null && listener != null) {  
            msg = mHandler.obtainMessage(whichButton, listener);  
        }  
          
        switch (whichButton) {  
  
            case DialogInterface.BUTTON_POSITIVE:  
                mButtonPositiveText = text;  
                mButtonPositiveMessage = msg;  
                break;  
                  
            case DialogInterface.BUTTON_NEGATIVE:  
                mButtonNegativeText = text;  
                mButtonNegativeMessage = msg;  
                break;  
                  
            case DialogInterface.BUTTON_NEUTRAL:  
                mButtonNeutralText = text;  
                mButtonNeutralMessage = msg;  
                break;  
                  
            default:  
                throw new IllegalArgumentException("Button does not exist");  
        }  
    }
看到这个方法的四个参数, 大家都会表示很熟悉吧. 但是这和上面添加 btnConfirm 和 btnCancel 两个按钮只显示最后一个, 有什么关系?

我们回去看Button的构造方法, 从Button -> ... -> 追溯到View类的源码, 就会发现, new出来的控件, 如果没有设置控件ID, 是默认赋予值为 -1 的控件ID:

/**
     * The view's identifier.
     * {@hide}
     *
     * @see #setId(int)
     * @see #getId()
     */
    @ViewDebug.ExportedProperty(resolveId = true)
    int mID = NO_ID;

/**
     * Used to mark a View that has no ID.
     */
    public static final int NO_ID = -1;
这个mID 就是控件的ID值.



再次看上面AlertController 的 setButton() 方法中用于判断的几个参数的值

/**
     * The identifier for the positive button.
     */
    public static final int BUTTON_POSITIVE = -1;

    /**
     * The identifier for the negative button. 
     */
    public static final int BUTTON_NEGATIVE = -2;

    /**
     * The identifier for the neutral button. 
     */
    public static final int BUTTON_NEUTRAL = -3;
BUTTON_POSITIVE 的值是 -1 . 而我们新建的两个按钮控件的默认ID也都是 -1.  所以代码中即使两次调用 setButton() 方法去给DatePickerDialog 添加两个按钮, 却只显示后者  btnCancel 按钮.  SOGA ~

来到这里, 问题好像还没解决, 却又好像想到怎么解决了.

上面不是提供了三个参数吗? BUTTON_POSITIVE, BUTTON_NEGATIVE, BUTTON_NEUTRAL . 继续试试看!

final Calendar c = Calendar.getInstance();
        mYear = c.get(Calendar.YEAR);
        mMonth = c.get(Calendar.MONTH);
        mDay = c.get(Calendar.DAY_OF_MONTH);

        final DatePickerDialog dpd = new DatePickerDialog(context,
                new DatePickerDialog.OnDateSetListener() {
                    @Override
                    public void onDateSet(DatePicker view, int year, int month,
                                          int day) {
                    }
                }, mYear, mMonth, mDay);


        dpd.setButton(DatePickerDialog.BUTTON_POSITIVE, "取消", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        });

        dpd.setButton(DatePickerDialog.BUTTON_NEGATIVE, "确定", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {

            }
        });

        dpd.setButton(DatePickerDialog.BUTTON_NEUTRAL, "中间按钮", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        });

        dpd.setTitle(R.string.str_select_dob);
        dpd.setOnDismissListener(new DialogInterface.OnDismissListener() {
            @Override
            public void onDismiss(DialogInterface dialog) {
                dialog.dismiss();
            }
        });

        dpd.setCanceledOnTouchOutside(true);
        dpd.show();

显示效果:

[Android] 更好的解决


POSITIVE 对应默认的确定键, NEGATIVE 是取消. 但是DatePickerDialog 对这个两个按钮的位置是将取消键放在左边, 确定键放在右边.→_→ 为了好看点, 我将它们显示的名称互换了, 反正它们各自有对应回调的接口, 所以毫无影响.  如果将添加中间按钮 setButton(DatePickerDialog.BUTTON_NEUTRAL, ...) 的方法去掉,  顺便把POSITIVE  的传入参数改为"确定", NEGATIVE 改为 "取消",  就会看到我们预期的界面: (果然, 确定键放在右边是有点奇葩吧 == )

[Android] 更好的解决


到了这里, 显示的问题, 我们搞掂了. 就剩下如何获取日期, 在哪里触发完成选择日期的事件.  你这么聪明, 肯定猜到啦~  没错, 就这样:

dpd.setButton(DatePickerDialog.BUTTON_NEGATIVE, "确定", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                int year = dpd.getDatePicker().getYear();
                int month = dpd.getDatePicker().getMonth();
                int day = dpd.getDatePicker().getDayOfMonth();

                // 获取日期后,进行处理...
                dialog.dismiss();
            }
        });

OK, onDateSet() 方法里面甚至可以不去写一行代码. 并且只在需要的时候才去获取日期.  这样我们就完成了, 利用DatePickerDialog 自身的方法不但实现了日期选择, 还解决了自动回调 onDateSet() 方法的问题. 



你可能感兴趣的:(安卓开发)