Android——弹出窗口中实现时间选择,文本输入,遇到Popupwindow不穿透与EditText输入法的矛盾,Dialog中弹出pop的显示层级问题
【需求】:弹出一个非全屏窗口,可以选择时间设置,文本输入; 看似简单确认折腾了一天多时间,下面把实战经历记录如下。
【实现思路及方法】:有两种,一种是通过Dialog实现,一种是通过Popupwindow实现,两者的实现思路差别不大,主要是遇到的问题各有千秋。
一、通过继承Dialog实现
1、编辑界面文件
<?xml version="1.0" encoding="utf-8"?>
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的顶层,而是在其下层,这样用户无法选中时间或者选项了,如果需求中没有用的这俩东西,只是普通的文本输入,用这个方法是没问题的。
关于无法解决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);
2)第一个问题解决了, setFocusable(false) 却带来了第二个问题,我的文本框没法输入了,输入法也不出现,因为没有聚焦。我尝试在文本点击时手动弹出输入法,在点击事件匿名类中调用上层类的方法,修改setFocusable属性,但是都不起作用,最后还是没有解决,这个水火不容的大BUG。
关于无法解决自定义Popup不穿透及EditText输入文本矛盾的问题,请路过的兄弟指点,多谢!
三、最终变相解决方法
基于Pop的方案的输入法问题实在没法解决,又把目标转向了基于Dialog的方案,这个方案的瑕疵在在于从Dialog弹出时间选择器的界面层级问题,Dialog都是显示在最顶层的,所以把TimerPickerView换成了TimerPickerDialog,最终完美解决,选项选择器换成combox了,就不再给出代码了。
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中 完美实现 计时 倒计时 时间间隔处理等功能实现 by Cou
- android 笔记 --- 让Android的输入框与文本框带滚动条ScrollView
- android 仿课程表,时间星期展示选择列表,简单易懂
- android中获得系统的时间
- android 时间1
- android之EditText文本监听(addTextChangedListener)
- android html超链接文本 点击跳转的两种实现
- android 自定义toast停留时间
- Android 输入法详解