Android——弹出窗口中实现时间选择,文本输入,遇到Popupwindow不穿透与EditText输入法的矛盾,Dialog中弹出pop的显示层级问题

【需求】:弹出一个非全屏窗口,可以选择时间设置,文本输入; 看似简单确认折腾了一天多时间,下面把实战经历记录如下。

【实现思路及方法】:有两种,一种是通过Dialog实现,一种是通过Popupwindow实现,两者的实现思路差别不大,主要是遇到的问题各有千秋。

一、通过继承Dialog实现

1、编辑界面文件




    

        

        

        

    

    

        

        

        

    

    

        

        

    

    

        

2、自定义Dialog类,继承自Dialog

主要思路:

1)可以看到界面中定义了两个按钮,外层创建Dialog对象时,构造函数中直接传入点击事件的监听器,并开启监听。监听器可以在外层自己定义,监听两个按键的动作,外层直接执行不同 的操作;

2)时间选择/模式选择分别使用TimePickerView 、OptionsPickerView,点击各自的文本时会在底部弹出时间选择器,选项选择器;

public class MineClockEditPop extends Dialog {

    private Activity mContext;
    private View.OnClickListener mClickListener;

    private Button buttonCancel;
    private Button buttonSave;

    public EditText editTime;
    public EditText editMode;
    public EditText editNote;


    public MineClockEditPop(Activity context) {
        super(context);
        this.mContext = mContext;
    }

    public MineClockEditPop(Activity context, int theme, View.OnClickListener clickListener) {
        super(context, theme);
        this.mContext = context;
        this.mClickListener = clickListener;
    }

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

        editTime = (EditText) findViewById(R.id.clock_edit_time);
        editMode = (EditText) findViewById(R.id.clock_edit_mode);
        editNote = (EditText) findViewById(R.id.clock_edit_note);

        buttonCancel =  (Button) findViewById(R.id.clock_edit_cancel);
        buttonSave = (Button) findViewById(R.id.clock_edit_save);

        // 设置弹出窗体的宽和高
        /*
         * 获取窗口对象及参数对象以修改对话框的布局设置, 可以直接调用getWindow(),表示获得这个Activity的Window
         * 对象,这样这可以以同样的方式改变这个Activity的属性.
         */
        Window dialogWindow = this.getWindow();

        WindowManager m = mContext.getWindowManager();
        Display d = m.getDefaultDisplay(); // 获取屏幕宽、高用
        WindowManager.LayoutParams p = dialogWindow.getAttributes(); // 获取对话框当前的参数值
        p.width = (int) (d.getWidth() * 0.8);
        dialogWindow.setAttributes(p);
        dialogWindow.setGravity(Gravity.CENTER_VERTICAL);
        // 设置按钮监听
        buttonCancel.setOnClickListener(mClickListener);
        buttonSave.setOnClickListener(mClickListener);

        this.setCancelable(false);

         //自定义时间选择器
        editTime.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                TimePickerView mTimePickerView = new TimePickerView(mContext, TimePickerView.Type.HOURS_MINS);
                // 设置是否循环
                mTimePickerView.setCyclic(true);
                // 设置滚轮文字大小
                mTimePickerView.setTitle("选择时间");
                mTimePickerView.setTextSize(TimePickerView.TextSize.SMALL);
                // 设置时间可选范围(结合 setTime 方法使用,必须在)
//                Calendar calendar = Calendar.getInstance();
//                mTimePickerView.setRange(calendar.get(Calendar.YEAR) - 100, calendar.get(Calendar.YEAR));
                // 设置选中时间
                mTimePickerView.setTime(new Date());
                mTimePickerView.setOnTimeSelectListener(new TimePickerView.OnTimeSelectListener() {
                    @Override
                    public void onTimeSelect(Date date) {
                        SimpleDateFormat format = new SimpleDateFormat("HH:mm", Locale.CHINA);
//                Toast.makeText(MineInformationActivity.this, format.format(date), Toast.LENGTH_SHORT).show();
                        editTime.setText(format.format(date));
                    }
                });

                mTimePickerView.show();
            }
        });

        //自定义模式选择器
        editMode.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                OptionsPickerView mOptionsPickerView = new OptionsPickerView<>(mContext);
                final ArrayList list = new ArrayList<>();
                list.add("仅一次");
                list.add("每天");

                mOptionsPickerView.setTitle("选择模式");
                // 设置数据
                mOptionsPickerView.setPicker(list);
                // 设置选项单位
                mOptionsPickerView.setOnOptionsSelectListener(new OptionsPickerView.OnOptionsSelectListener() {
                    @Override
                    public void onOptionsSelect(int option1, int option2, int option3) {
                        String mode = list.get(option1);
//                Toast.makeText(MineInformationActivity.this, sex, Toast.LENGTH_SHORT).show();
                        editMode.setText(mode);
                    }
                });
                mOptionsPickerView.show();

            }
        });

    }

}

3、外部调用层 自定义窗口中按钮点击事件监听器

//自定义按钮监听事件
    private View.OnClickListener onClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            switch (v.getId()) {
                case R.id.clock_edit_cancel:

                    break;

                case R.id.clock_edit_save:
                    String time = clockEditPop.editTime.getText().toString().trim();
                    String mode = clockEditPop.editMode.getText().toString().trim();
                    String note = clockEditPop.editNote.getText().toString().trim();
                    LogUtil.d(TAG,time+"---"+mode+"---"+note);
                    clockEditPop.dismiss();
                    break;
            }
        }
    };

4、调用:
        clockEditPop = new MineClockEditPop(this,1,onClickListener);
        clockEditPop.show();

5、总结

使用这个方法存在的问题是,我在Dialog中使用了TimePickerView 、OptionsPickerView,这俩本质上是个Popupwindow,点击出现时没有出现在Dialog的顶层,而是在其下层,这样用户无法选中时间或者选项了,如果需求中没有用的这俩东西,只是普通的文本输入,用这个方法是没问题的。

Android——弹出窗口中实现时间选择,文本输入,遇到Popupwindow不穿透与EditText输入法的矛盾,Dialog中弹出pop的显示层级问题_第1张图片

关于无法解决POP 窗口在dialog下面的问题,请路过的兄弟帮忙指点下,多谢!

二、通过继承PopupWindow实现

1、编辑界面,如一,不再赘述

2、Popup窗口类,继承自PopupWindow,外部调用层 自定义窗口中按钮点击事件监听器

思路与一中基本一致,只是构造函数处有略微差别。不再赘述。


public class MineClockEditPop extends PopupWindow {

    private Context mContext;
    private View view;

    private Button buttonCancel;
    private Button buttonSave;

    public EditText editTime;
    public EditText editMode;
    public EditText editNote;

    //构造函数,外部调用时传入自定义的监听器
    public MineClockEditPop(final Activity mContext, View.OnClickListener itemsOnClick) {

        this.mContext = mContext;
        this.view = LayoutInflater.from(mContext).inflate(R.layout.tab_mine_clock_edit, null);

        editTime = (EditText) view.findViewById(R.id.clock_edit_time);
        editMode = (EditText) view.findViewById(R.id.clock_edit_mode);
        editNote = (EditText) view.findViewById(R.id.clock_edit_note);

        buttonCancel =  (Button) view.findViewById(R.id.clock_edit_cancel);
        buttonSave = (Button) view.findViewById(R.id.clock_edit_save);

        // 设置按钮监听
        buttonCancel.setOnClickListener(itemsOnClick);
        buttonSave.setOnClickListener(itemsOnClick);
        // 设置外部可点击,即点击pop窗口外pop消失
        this.setOutsideTouchable(false);

        /* 设置弹出窗口特征 */
        // 设置视图
        this.setContentView(this.view);

        // 设置弹出窗体的宽和高
        /*
         * 获取窗口对象及参数对象以修改对话框的布局设置, 可以直接调用getWindow(),表示获得这个Activity的Window
         * 对象,这样这可以以同样的方式改变这个Activity的属性.
         */
        Window dialogWindow = mContext.getWindow();

        WindowManager m = mContext.getWindowManager();
        Display d = m.getDefaultDisplay(); // 获取屏幕宽、高用
        WindowManager.LayoutParams p = dialogWindow.getAttributes(); // 获取对话框当前的参数值

        this.setHeight(RelativeLayout.LayoutParams.WRAP_CONTENT);
        this.setWidth((int) (d.getWidth() * 0.8));

        // 设置聚焦,否则EditText输入法无响应
        this.setFocusable(false);

        //自定义时间选择器
        editTime.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                TimePickerView mTimePickerView = new TimePickerView(view.getContext(), TimePickerView.Type.HOURS_MINS);
                // 设置是否循环
                mTimePickerView.setCyclic(true);
                // 设置滚轮文字大小
                mTimePickerView.setTitle("选择时间");
                mTimePickerView.setTextSize(TimePickerView.TextSize.SMALL);
                // 设置时间可选范围(结合 setTime 方法使用,必须在)
//                Calendar calendar = Calendar.getInstance();
//                mTimePickerView.setRange(calendar.get(Calendar.YEAR) - 100, calendar.get(Calendar.YEAR));
                // 设置选中时间
                mTimePickerView.setTime(new Date());
                mTimePickerView.setOnTimeSelectListener(new TimePickerView.OnTimeSelectListener() {
                    @Override
                    public void onTimeSelect(Date date) {
                        SimpleDateFormat format = new SimpleDateFormat("HH:mm", Locale.CHINA);
//                Toast.makeText(MineInformationActivity.this, format.format(date), Toast.LENGTH_SHORT).show();
                        editTime.setText(format.format(date));
                    }
                });
                mTimePickerView.show();
            }
        });

        //自定义模式选择器
        editMode.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                OptionsPickerView mOptionsPickerView = new OptionsPickerView<>(view.getContext());
                final ArrayList list = new ArrayList<>();
                list.add("仅一次");
                list.add("每天");

                mOptionsPickerView.setTitle("选择模式");
                // 设置数据
                mOptionsPickerView.setPicker(list);
                // 设置选项单位
                mOptionsPickerView.setOnOptionsSelectListener(new OptionsPickerView.OnOptionsSelectListener() {
                    @Override
                    public void onOptionsSelect(int option1, int option2, int option3) {
                        String mode = list.get(option1);
//                Toast.makeText(MineInformationActivity.this, sex, Toast.LENGTH_SHORT).show();
                        editMode.setText(mode);
                    }
                });
                mOptionsPickerView.show();
            }
        });

        editNote.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                //设置Popupwindow的setFocusable  为true,可以输入文本
                //MineClockEditPop.this.setFocusable(false);

                //手动弹出输入法
                InputMethodManager inputMethodManager = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
                inputMethodManager.toggleSoftInput(0,InputMethodManager.HIDE_NOT_ALWAYS);



//                editNote.setFocusableInTouchMode(true);
//                editNote.setFocusable(true);
//                editNote.requestFocus();
            }
        });
    }



}

3、调用:

//调用
        clockEditPop = new MineClockEditPop(this,onClickListener);
        clockEditPop.showAtLocation(findViewById(R.id.tab_mine_clock), Gravity.CENTER, 0, 0);

4、总结

1)使用这个方法存在的问题是,我想让自定义的Popup窗口不穿透,即点击窗口外时,这个Popup窗口不消失(弹出实现选择器和选项选择器的需求),这时要设置的两个属性,关于为什么要设置第二个可以看下第一个属性起作用的条件,如下截图,那就是setFocusable 为false。

// 设置外部可点击,即点击pop窗口外pop消失
        this.setOutsideTouchable(false);

// 设置聚焦,否则EditText输入法无响应
        this.setFocusable(false);

Android——弹出窗口中实现时间选择,文本输入,遇到Popupwindow不穿透与EditText输入法的矛盾,Dialog中弹出pop的显示层级问题_第2张图片 

2)第一个问题解决了, setFocusable(false) 却带来了第二个问题,我的文本框没法输入了,输入法也不出现,因为没有聚焦。我尝试在文本点击时手动弹出输入法,在点击事件匿名类中调用上层类的方法,修改setFocusable属性,但是都不起作用,最后还是没有解决,这个水火不容的大BUG。

关于无法解决自定义Popup不穿透及EditText输入文本矛盾的问题,请路过的兄弟指点,多谢!

三、最终变相解决方法

基于Pop的方案的输入法问题实在没法解决,又把目标转向了基于Dialog的方案,这个方案的瑕疵在在于从Dialog弹出时间选择器的界面层级问题,Dialog都是显示在最顶层的,所以把TimerPickerView换成了TimerPickerDialog,最终完美解决,选项选择器换成combox了,就不再给出代码了。

Android——弹出窗口中实现时间选择,文本输入,遇到Popupwindow不穿透与EditText输入法的矛盾,Dialog中弹出pop的显示层级问题_第3张图片Android——弹出窗口中实现时间选择,文本输入,遇到Popupwindow不穿透与EditText输入法的矛盾,Dialog中弹出pop的显示层级问题_第4张图片

Calendar calendar = Calendar.getInstance();
                //create a datePickerDialog and then shoe it on your screen
                new TimePickerDialog(mContext,
                        new TimePickerDialog.OnTimeSetListener() {
                            @Override
                            public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
                                DecimalFormat df = new DecimalFormat("00");
                                df.setRoundingMode(RoundingMode.HALF_DOWN);
                                editTime.setText(df.format(hourOfDay)+":"+df.format(minute));
                            }
                        }
                        , calendar.get(Calendar.HOUR_OF_DAY)
                        , calendar.get(Calendar.MINUTE)
                        , true).show();

你可能感兴趣的:(Android)