Кнопка Android TimePicker AM/PM не вызывает onTimeChanged
У меня возникают некоторые проблемы с внедрением TimePicker в моем приложении, которое позволяет пользователю изменять время записи базы данных до ее вставки.
Проблема заключается в том, что при нажатии кнопки AM/PM метод onTimeChanged (View, int, int) не вызывается. Всякий раз, когда я изменяю либо часовое, либо минутное значение TimePicker, вызывается onTimeChanged().
Сценарии:
- Пользователь просто нажимает кнопку AM/PM: 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
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fillViewport="true">
<TimePicker android:id="@+id/timepicker"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="5dip"
android:layout_marginRight="5dip"/>
</LinearLayout>
Ответы
Ответ 1
Я проверил код. Да, вы правы, кнопка TimePicker AM/PM не вызывает метод onTimeChanged, который он должен.
Собственно, это ошибка. Об этом сообщается в google. Вы можете найти его здесь
http://code.google.com/p/android/issues/detail?id=18982
Проголосуйте, комментируйте и Звездный отчет об ошибке, чтобы повысить его приоритет и как можно скорее зафиксировать его командой разработчиков.
Ответ 2
Я нашел этот ответ, но для меня это не сработало, поэтому я решил обновить его.
В 3.2 (и выше?), сборщик времени не имеет кнопки для am/pm. Вместо этого он имеет NumberPicker. Следующий код работал у меня. Последний бит существует, потому что мне нужно ограничить время, выбранное не раньше, чем "мин" и не больше, чем "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));
}
}
}
});
Ответ 3
Решение Velval - единственное решение, которое работает и совместимо с различными версиями Android. Я попробовал это на api 17 и 23. но у него есть проблема, которая мешает оригинальному прослушиванию кликов на Android 6. так вот что со мной работало. использовать touch вместо щелчка:
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.
}
}
}
Ответ 4
эта ссылка показывает, что 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();
}
});
Ответ 5
Пожалуйста, используйте приведенный ниже код, он отлично работает для меня.
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);
}
}
}
}
});
}
Ответ 6
Была та же проблема при создании 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.
}
Ответ 7
С 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());
}
}
}
Ответ 8
Вот быстрый проект, который я собрал вместе, поддерживающий Android 4.0+. В основном, он показывает TextView, который всегда синхронизируется с TimePicker, независимо от того, какой вертикальный счетчик манипулирует пользователем.
https://gist.github.com/danialgoodwin/5694256
Ответ 9
Я делаю этот метод true, если TimePicker выбирает AM и false, если TimePicker выбирает PM. note: suppresLint для Api 8 не работает .getvalue() statement
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;
}
}
Ответ 10
Я хотел бы поделиться своим решением, так как другие обходные пути, опубликованные здесь, не работают для меня. Но я могу понять это. Эта строка:
View amPmView = ((ViewGroup)tp.getChildAt(0)).getChildAt(2);
Кажется, не возвращается колесо выбора для AM_PM.
NumberPicker amPmView = (NumberPicker ((ViewGroup)tp.getChildAt(0)).getChildAt(3);
Ни то, ни другое, так как этот возвращает a TextView
. Кажется, это ярлык, обозначенный на сборщике.
Но используя последнее, получение четвертого дочернего элемента возвращает мне колесо выбора для 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
, поэтому я не уверен в этом. Я тестирую на min sdk 8, нацеливая api 22.
НТН
Ответ 11
Я также столкнулся с такой же проблемой, используя API 21, и проблема еще не решена.
Я заменил представление TimePicker на TimePickerDialog, и он сработал.
Следующий код устанавливает TextView с временем, выбранным из TimePickerDialog. Вы можете заменить 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();
}