Android TimePicker AM / PM按钮未调用onTimeChanged

时间:2012-01-28 17:32:09

标签: android timepicker

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

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

方案:

  • 用户只需点击上午/下午按钮:上午/下午更新
  • 用户点击小时/分钟:时间已更新
  • 用户点击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>

11 个答案:

答案 0 :(得分:31)

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

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

请投票,评论&amp; 明星错误报告,以提高其优先级,并尽快由开发团队修复。

答案 1 :(得分:6)

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

在3.2(及以上?)中,时间选择器没有上午/下午的按钮。相反,它有一个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));
                    }
                }
            } 
        }); 

答案 2 :(得分:5)

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.
        }


    }
}

答案 3 :(得分:3)

this链接显示正在调用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();
        }
    });

答案 4 :(得分:3)

请使用以下代码,它对我来说没问题。

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);
                            }
                    }
                }

            }
        });
    }

答案 5 :(得分:3)

在代码中创建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.
}

答案 6 :(得分:1)

使用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());
    }
}
}

答案 7 :(得分:0)

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

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

答案 8 :(得分:0)

我使这个方法在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;
    }

}

答案 9 :(得分:0)

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

    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上尝试过这种情况,所以我不确定那个。{1}}我正在测试最低sdk 8目标api 22。

HTH

答案 10 :(得分:0)

我也使用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();
}