Кнопка 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();
}