Android TimePicker hour field disappears after orientation change
I am using a TimePicker under API 1.5 and when the orientation is changed on my device (a stock G1 running 1.6 - though NOT on the 1.5/1.6 emulator), the hour field goes blank. It still remembers the hour, it just doesn't show it. Is there any workaround for this?
The same problem was described by someone else开发者_如何学Go here:
http://groups.google.com/group/android-beginners/browse_thread/thread/b4288004021b876/de5899a2bb291ab5
Nothing helpful was forthcoming - can StackOverflow do better?
Simply move invoking of methods TimePicker.setCurrentHour and TimePicker.setCurrentMinute to onResume method of your Activity.
As mentioned, overriding the onResume() method will sort this issue.
@Override
public void onResume() {
super.onResume();
TimePicker tmp = (TimePicker) this.findViewById(R.id.yourTimePicker);
tmp.setCurrentHour(7); // This will also set on rotate
}
Take a look at this: http://code.google.com/p/android/issues/detail?id=22824 No matter what you do you won't be able to fix the issue without destroying the Activity and recreating it when the rotate occurs. In order to not enter an endless loop simple check to see if the savedInstanceState != null in the onCreate. Something like this will work:
if (savedInstanceState!=null){
Intent myIntent =
new Intent(getApplicationContext(), CurrentClassName.class);
startActivityForResult(myIntent, 0);
finish();
}
In onSaveInstanceState()
for your activity, get the hour value out of the TimePicker
and place it in the Bundle
. In onRestoreInstanceState()
for your activity, get the hour out of the Bundle
and put it in the TimePicker
.
TimePicker
should handle that on its own, but this may work around the problem for you.
I've lost a ton of sleep over this and just figured it out. When you're using a DialogPreference you need to override the showDialog method and reset your currentHour and currentMinute there. Like this:
@Override
protected void showDialog(Bundle state) {
super.showDialog(state);
mBeginShiftTp.setCurrentHour(mBeginShiftTp.getCurrentHour());
mBeginShiftTp.setCurrentMinute(mBeginShiftTp.getCurrentMinute());
mEndShiftTp.setCurrentHour(mEndShiftTp.getCurrentHour());
mEndShiftTp.setCurrentMinute(mEndShiftTp.getCurrentMinute());
}
Based on the link that DanRoss provided, the issue is related to the picker not updating the hour if the hour has not changed. Regarding orientation changes, this behavior is faulty because the hour before the rotation will always equal the hour after the rotation.
For those using the TimePickerDialog
, a way around this is to extend the default implementation and force it to update the hour by updating the time (setting hour and minute to equal 0 in my example) and then update the hour to be the actual hour.
It may be possible to build something similar using only the TimePicker
control.
Here is a working example for the TimePickerDialog:
import android.content.Context;
import android.os.Bundle;
import android.widget.TimePicker;
public class TimePickerDialog extends android.app.TimePickerDialog
{
private final String HOUR_KEY = "Hour";
private final String MINUTE_KEY = "Minute";
private int hourOfDay;
private int minute;
public TimePickerDialog(Context context, OnTimeSetListener callBack, int hourOfDay, int minute, boolean is24HourView)
{
super(context, callBack, hourOfDay, minute, is24HourView);
this.hourOfDay = hourOfDay;
this.minute = minute;
}
@Override
public void onTimeChanged (TimePicker view, int hourOfDay, int minute)
{
super.onTimeChanged(view, hourOfDay, minute);
this.hourOfDay = hourOfDay;
this.minute = minute;
}
@Override
public Bundle onSaveInstanceState()
{
Bundle savedInstanceState = super.onSaveInstanceState();
if (savedInstanceState == null)
{
savedInstanceState = new Bundle();
}
savedInstanceState.putInt(HOUR_KEY, this.hourOfDay);
savedInstanceState.putInt(MINUTE_KEY, minute);
return savedInstanceState;
}
@Override
public void onRestoreInstanceState(Bundle savedInstanceState)
{
super.onRestoreInstanceState(savedInstanceState);
// The code for updating the hour is currently not doing anything
// if the hour has not changed.
// Force it to update by setting it to zero first
this.updateTime(0, 0);
this.hourOfDay = savedInstanceState.getInt(HOUR_KEY);
this.minute = savedInstanceState.getInt(MINUTE_KEY);
// Now it will update
this.updateTime(this.hourOfDay, this.minute);
}
}
This bug is related on Android Official Forum Bug 5483. Well, and that discussion, they suggest a simple solution and it works for me. I added the attribute android:onConfigurationChanges=orientation
on my activity on AndroidManifest.xml
.
Important: Depending of you activity(others widgets ...), it can have side effects
I realize I'm a little late to the party, but seeing as this problem still exists, I feel it deserves an actual answer.
From what I've read elsewhere, the issue has to do with the individual number fields of the TimePicker all having exactly the same ID. The reason this is a problem is because when you rotate the screen, android closes all of your dialogs, reopens all of your dialogs usings the typical onCreateDialog -> onPrepareDialog, and then, most importantly, after you have made your dialogs, it goes back through and restores what used to be there. This means that no matter what you do beforehand, android is going to come in and break your TimePicker.
The answer, although not the prettiest option, is to come in after android and set it again.
Here's how I did it, I simply made my own TimePicker:
public class MyTimePicker extends TimePicker {
public MyTimePicker(Context context) {
super(context);
}
public MyTimePicker(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyTimePicker(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
private final static String B_MIN = "bMin";
private final static String B_HOUR = "bHour";
private final static String B_SUPER = "bSuper";
// here's the first key, we're going to override how the TimePicker saves
// its state
@Override
public Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
bundle.putParcelable(B_SUPER, super.onSaveInstanceState());
bundle.putInt(B_MIN, getCurrentMinute());
bundle.putInt(B_HOUR, getCurrentHour());
return bundle;
}
// this function gets called when we are restoring, but before android
// does its thing
@Override
public void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
final Bundle bundle = (Bundle) state;
super.onRestoreInstanceState(bundle.getParcelable(B_SUPER));
// here it is, not the prettiest option, but it gets the job done
// we simply wait 0.1 sec and then update the TimePicker
// since all of this happens in the same thread, its guarenteed to
// be after the android reset
// also, this handler gets the HandlerLeak warning, I don't
// think this can leak though seeing as it's only called once
// with a 100 ms delay
(new Handler() {
@Override
public void handleMessage(Message msg) {
setCurrentMinute(bundle.getInt(B_MIN));
setCurrentHour(bundle.getInt(B_HOUR));
}
}).sendEmptyMessageDelayed(0, 100);
return;
}
super.onRestoreInstanceState(state);
}
}
And yes, the handler is necessary. If you simply run the setCurrent commands from onRestoreInstanceState android will simply override you.
I see this problem was active earlyer but I solved problem and if someone has this problem this is solve: Everything doing in onResume(), first get value for hour, then set hour on timePicker to hour+1 and then set to hour. That's working for me.
Crazy solution that works:
- Run AsynTask in onResume()
- Sleep for 50msec
- Switch back to UI thread, using Activity.runOnUiThread()
- In UI thread set min/hour to 0
- Then set min/hour to your values
Here is a gist with the code, ready for copy-paste :)
https://gist.github.com/sorrro/9415702
精彩评论