我在应用程序中实现TimePicker时遇到一些问题,允许用户在插入数据库记录之前更改数据库记录的时间。
问题是当按下AM / PM按钮时,不会调用onTimeChanged(View,int,int)方法。每当我更改TimePicker的小时或分钟值时,都会调用onTimeChanged()。
方案:
我认为应该能够点击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>
答案 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,无论用户操纵哪个垂直微调器。
答案 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();
}