Android TimePicker AM / PM按钮不调用onTimeChanged

我在应用程序中实现TimePicker时遇到一些问题,允许用户在插入数据库记录之前更改数据库记录的时间。

问题是当按下AM / PM按钮时,不会调用onTimeChanged(View,int,int)方法。 每当我更改TimePicker的小时或分钟值时,都会调用onTimeChanged()。

场景:

  • 用户只需单击AM / PM按钮:AM / PM 不会更新
  • 用户单击“小时/分钟”:时间已更新
  • 用户单击AM / PM按钮然后更改小时/分钟:时间和AM / PM更新

我错误地认为应该能够点击AM / PM按钮来更新时间,而不必在上午/下午按钮之后更改时间值吗?

我已经整理了一个小测试项目来复制它,这里是代码:

活动

public class TestActivity extends Activity implements OnTimeChangedListener { private Calendar mCalendar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.timepicker); mCalendar = Calendar.getInstance(); TimePicker tp = (TimePicker)findViewById(R.id.timepicker); tp.setIs24HourView(false); tp.setOnTimeChangedListener(this); } @Override public void onTimeChanged(TimePicker view, int hourOfDay, int minute) { Log.d("TAG", "In onTimeChanged"); mCalendar.set(mCalendar.get(Calendar.YEAR), mCalendar.get(Calendar.MONTH), mCalendar.get(Calendar.DAY_OF_MONTH), hourOfDay, minute); setCalendarTime(); } private void setCalendarTime() { Date date = mCalendar.getTime(); if (date != null) { SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yy '@' h:mm a"); String dateTime = formatter.format(date); Toast.makeText(this, dateTime, Toast.LENGTH_LONG).show(); } } } 

timepicker.xml

    

我测试了代码。 是的,你是对的,TimePicker AM / PM按钮没有调用onTimeChanged方法。

实际上,这是一个错误。 据报道谷歌。 你可以在这里find它
http://code.google.com/p/android/issues/detail?id=18982

请对错误报告进行投票,评论和加注,以提高其优先级,并尽快由开发团队修复。

我find了这个答案,但它对我不起作用,所以我想我会更新。

在3.2(及以上?)中,时间选择器没有am / pm的按钮。 相反,它有一个NumberPicker。 以下代码对我有用。 最后一点是因为我需要将所选时间限制为不早于’min’日期且不大于’max’日期,所以我必须检查匹配的DatePicker控件中选择的日期:

  NumberPicker amPmView = (NumberPicker)((ViewGroup)tp.getChildAt(0)).getChildAt(3); amPmView.setOnValueChangedListener(new OnValueChangeListener() { @Override public void onValueChange(NumberPicker arg0, int arg1, int arg2) { if(arg0.getValue()== 1){ if (tp.getCurrentHour() < 12) tp.setCurrentHour(tp.getCurrentHour() + 12); } else{ if (tp.getCurrentHour() >= 12) tp.setCurrentHour(tp.getCurrentHour() - 12); } int year = dp.getYear(); int month = dp.getMonth(); int dayOfMonth = dp.getDayOfMonth(); int hourOfDay = tp.getCurrentHour(); int minute = tp.getCurrentMinute(); if (year == min.get(Calendar.YEAR) && month == min.get(Calendar.MONTH) && dayOfMonth == min.get(Calendar.DAY_OF_MONTH)){ if ((hourOfDay < min.get(Calendar.HOUR_OF_DAY))|| (hourOfDay == min.get(Calendar.HOUR_OF_DAY) && (minute < min.get(Calendar.MINUTE)))){ tp.setCurrentHour(min.get(Calendar.HOUR_OF_DAY)); tp.setCurrentMinute(min.get(Calendar.MINUTE)); } }else if (year == max.get(Calendar.YEAR) && month == max.get(Calendar.MONTH) && dayOfMonth == max.get(Calendar.DAY_OF_MONTH)){ if ((hourOfDay > max.get(Calendar.HOUR_OF_DAY))|| (hourOfDay == max.get(Calendar.HOUR_OF_DAY) && (minute > max.get(Calendar.MINUTE)))){ tp.setCurrentHour(max.get(Calendar.HOUR_OF_DAY)); tp.setCurrentMinute(max.get(Calendar.MINUTE)); } } } }); 

Velval解决方案是唯一可以兼容不同Android版本的解决方案。 我在api 17和23上尝试过它。但它有一个问题,它在Android 6上阻止原始的点击监听器。所以这里有什么对我有用。 使用触摸而不是单击:

 public class MyTimePicker extends TimePicker { private OnTimeChangedListener onTimeChangedListener; public MyTimePicker(Context context) { super(context); // init(); } public MyTimePicker(Context context, AttributeSet attrs) { super(context, attrs); //init(); } public MyTimePicker(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // init(); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public MyTimePicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { // Stop ScrollView from getting involved once you interact with the View if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) { ViewParent p = getParent(); if (p != null) p.requestDisallowInterceptTouchEvent(true); } return false; } @Override public void setOnTimeChangedListener(OnTimeChangedListener onTimeChangedListener) { super.setOnTimeChangedListener(onTimeChangedListener); this.onTimeChangedListener = onTimeChangedListener; } @Override protected void onFinishInflate() { super.onFinishInflate(); init(); } private void init() { try { ViewGroup amPmView; ViewGroup v1 = (ViewGroup) getChildAt(0); ViewGroup v2 = (ViewGroup) v1.getChildAt(0); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { ViewGroup v3 = (ViewGroup) v2.getChildAt(0); amPmView = (ViewGroup) v3.getChildAt(3); } else { amPmView = (ViewGroup) v2.getChildAt(3); } View.OnTouchListener listener = new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { onTimeChangedListener.onTimeChanged(MyTimePicker.this, getCurrentHour(), getCurrentMinute()); } else { int hour = getCurrentHour(); if (hour >= 12) { hour -= 12; } else { hour += 12; } onTimeChangedListener.onTimeChanged(MyTimePicker.this, hour, getCurrentMinute()); } return false; } }; View am = amPmView.getChildAt(0); View pm = amPmView.getChildAt(1); am.setOnTouchListener(listener); pm.setOnTouchListener(listener); } catch (Exception e) { // DO nothing... just ignore the workaround if this fails. } } } 

请使用以下代码,它对我来说很好。

 final TimePicker tp=(TimePicker)findViewById(R.id.timePicker1); View amPmView = ((ViewGroup)tp.getChildAt(0)).getChildAt(2); if(amPmView instanceof Button) { amPmView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Log.d("OnClickListener", "OnClickListener called"); if(v instanceof Button) { if(((Button) v).getText().equals("AM")) { ((Button) v).setText("PM"); if (tp.getCurrentHour() < 12) { tp.setCurrentHour(tp.getCurrentHour() + 12); } } else{ ((Button) v).setText("AM"); if (tp.getCurrentHour() >= 12) { tp.setCurrentHour(tp.getCurrentHour() - 12); } } } } }); } 

此链接显示onTimeChanged(); 被调用,触发事件调度程序。

如果您没有获得所需的事件(即使它似乎正在发送),您必须这样做

  • 扩展默认的TimerPicker,

  • 覆盖mAmPmButton.setOnClickListener,

  • 并在视图中包含您的版本。


 mAmPmButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { requestFocus(); if (mIsAm) { // Currently AM switching to PM if (mCurrentHour < 12) { mCurrentHour += 12; } } else { // Currently PM switching to AM if (mCurrentHour >= 12) { mCurrentHour -= 12; } } mIsAm = !mIsAm; mAmPmButton.setText(mIsAm ? mAmText : mPmText); onTimeChanged(); } }); 

在代码中创建TimePicker时遇到同样的问题,并且所提供的解决方案都不适用于我。 以下是我最终做的事情。 测试API级别18,21和23

 final TimePicker tp = new TimePicker(getContext()); tp.setOnTimeChangedListener(this); try { ViewGroup amPmView; ViewGroup v1 = (ViewGroup)tp.getChildAt(0); ViewGroup v2 = (ViewGroup)v1.getChildAt(0); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { ViewGroup v3 = (ViewGroup)v2.getChildAt(0); amPmView = (ViewGroup)v3.getChildAt(3); } else { amPmView = (ViewGroup)v2.getChildAt(3); } View.OnClickListener listener = new View.OnClickListener() { @Override public void onClick(View v) { tp.setCurrentHour((tp.getCurrentHour() + 12) % 24); } }; View am = amPmView.getChildAt(0); View pm = amPmView.getChildAt(1); am.setOnClickListener(listener); pm.setOnClickListener(listener); } catch (Exception e) { // DO nothing... just ignore the workaround if this fails. } 

使用API​​ 25 Nougat,Velval / Hisham的代码是唯一有效但不在横向模式下的代码。 我改变了代码,它工作正常:):

 public class AMPMTimePicker extends TimePicker { private static final String TAG = "AMPMTimePicker"; private OnTimeChangedListener onTimeChangedListener; public AMPMTimePicker(Context context) { super(context); } public AMPMTimePicker(Context context, AttributeSet attrs) { super(context, attrs); } public AMPMTimePicker(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public AMPMTimePicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { // Stop ScrollView from getting involved once you interact with the View if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) { ViewParent p = getParent(); if (p != null) p.requestDisallowInterceptTouchEvent(true); } return false; } @Override public void setOnTimeChangedListener(OnTimeChangedListener onTimeChangedListener) { super.setOnTimeChangedListener(onTimeChangedListener); this.onTimeChangedListener = onTimeChangedListener; } @Override protected void onFinishInflate() { super.onFinishInflate(); init(); } @SuppressWarnings("deprecation") private void init() { try { ViewGroup amPmView; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // LinearLayout (LOLLIPOP) // GridLayout (M-LANDSCAPE) // LinearLayout (M-PORTRAIT) ViewGroup v1 = (ViewGroup) getChildAt(0); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { // FrameLayout (LOLLIPOP-LANDSCAPE) // FrameLayout - id:time_header (LOLLIPOP-PORTRAIT) ViewGroup v2 = (ViewGroup) v1.getChildAt(0); // FrameLayout - id:TimeHeader (LOLLIPOP-LANDSCAPE) // LinearLayout (LOLLIPOP-PORTRAIT) ViewGroup v3 = (ViewGroup) v2.getChildAt(0); if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { ViewGroup v4 = (ViewGroup) v3.getChildAt(0); // LinearLayout (LOLLIPOP) amPmView = (ViewGroup) v4.getChildAt(3); // LinearLayout - id:ampm_layout (LOLLIPOP) } else { // PORTRAIT amPmView = (ViewGroup) v3.getChildAt(3); // LinearLayout - id:ampm_layout (LOLLIPOP) } } else { // M and after if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { ViewGroup v2 = (ViewGroup) v1.getChildAt(1); // RelativeLayout (M) amPmView = (ViewGroup) v2.getChildAt(1); // LinearLayout - id:ampm_layout (M) } else { ViewGroup v2 = (ViewGroup) v1.getChildAt(0); // RelativeLayout - id:time_header (M) amPmView = (ViewGroup) v2.getChildAt(3); // LinearLayout - id:ampm_layout (M) } } View am = amPmView.getChildAt(0); // AppCompatCheckedTextView - id:am_label View pm = amPmView.getChildAt(1); // AppCompatCheckedTextView - id:pm_label View.OnTouchListener listener = new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { int hour; int minute; if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { hour = getCurrentHour(); minute = getCurrentMinute(); } else { hour = getHour(); minute = getMinute(); } hour = (hour >= 12) ? hour - 12 : hour + 12; onTimeChangedListener.onTimeChanged(AMPMTimePicker.this, hour, minute); return false; } }; am.setOnTouchListener(listener); pm.setOnTouchListener(listener); } } catch (Exception e) { Log.e(TAG, "TimePicker is not defined for this Android version : " + e.getMessage()); } } } 

这是一个我投入的快速项目,支持Android 4.0+。 基本上,它显示一个始终与TimePicker同步的TextView,无论用户操纵哪个垂直微调器。

https://gist.github.com/danialgoodwin/5694256

我使这个方法在TimePicker选择AM时返回true,如果TimePicker选择PM则返回false。 注意:suppresLint用于Api 8不起作用.getvalue()语句

Saludos! 🙂

 @SuppressLint("NewApi") private boolean isAM(TimePicker timePicker){ NumberPicker numberPickerAmPm = (NumberPicker)((ViewGroup) timePicker.getChildAt(0)).getChildAt(3); if(numberPickerAmPm.getValue()==0){ //sendToast("es am"); return true; }else{ //sendToast("es pm"); return false; } } 

我也想分享我的解决方案,因为这里发布的其他解决方法对我不起作用。 但我能弄清楚。 这一行:

  View amPmView = ((ViewGroup)tp.getChildAt(0)).getChildAt(2); 

似乎没有返回AM_PM的选择轮。

  NumberPicker amPmView = (NumberPicker ((ViewGroup)tp.getChildAt(0)).getChildAt(3); 

这也不是,因为这个返回TextView 。 似乎它似乎是拣货员指定的标签。

但是使用后者,获得第4个子元素会返回AM_PM的选择轮。 这是我到目前为止所得到的:

  NumberPicker amPmView = (NumberPicker)((ViewGroup) mTimePicker.getChildAt(0)).getChildAt(4); amPmView.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() { @Override public void onValueChange(NumberPicker picker, int oldVal, int newVal) { Log.i(NoteApplication.TAG, "AM_PM selected..."); } }); 

现在我能够检测到AM_PM中的变化。 我希望这可以帮助其他一些无法通过其他答案所描述的getChildAt(2)getChildAt(3)检索它的人。

另请注意,这是TimePicker类的情况,我还没有在TimePickerDialog上尝试过这个,所以我不确定那个。 我正在测试最低sdk 8目标api 22。

HTH

我也遇到了使用API​​ 21的同样问题,问题还没有解决。

我为TimePickerDialog替换了TimePicker视图并且它工作正常。

以下代码使用从TimePickerDialog中选取的时间设置TextView。 你可以用任何逻辑替换tv.setText():

 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final TextView tv = (TextView)findViewById(R.id.textView); Calendar calendar = Calendar.getInstance(); int hour = calendar.get(Calendar.HOUR_OF_DAY); int minute = calendar.get(Calendar.MINUTE); TimePickerDialog timePickerDialog = new TimePickerDialog(this, new TimePickerDialog.OnTimeSetListener() { @Override public void onTimeSet(TimePicker view, int hourOfDay, int minute) { tv.setText(String.format(Locale.getDefault(),"Hour: %d, Minute: %d ",hourOfDay,minute)); } }, hour, minute, true); timePickerDialog.show(); }