1

I'm using a time picker to let the user enter his desired time to do a specific task, I'm using the DialogFragment class that's available in the support library for backward compatibility with older Android versions.

Here is my code to create the TimePickerFragment class, created in a seperate file, taken from: http://developer.android.com/guide/topics/ui/controls/pickers.html :

package com.calls.only;
import java.util.Calendar;
import android.app.Dialog;
import android.app.TimePickerDialog;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.widget.TimePicker;


public class TimePickerFragment extends DialogFragment
                            implements TimePickerDialog.OnTimeSetListener {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Use the current time as the default values for the picker
        final Calendar c = Calendar.getInstance();
        int hour = c.get(Calendar.HOUR_OF_DAY);
        int minute = c.get(Calendar.MINUTE);

        // Create a new instance of TimePickerDialog and return it
        return new TimePickerDialog(getActivity(), this, hour, minute, false);
    }

    public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
        // Do something with the time chosen by the user
    }
}

Main Activity:

package com.calls.only;


import java.util.Calendar;
import java.util.TimeZone;
import android.os.Bundle;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.DialogFragment;
import android.view.Menu;
import android.view.View;
import android.widget.RadioButton;
import android.widget.TextView;

public class MainActivity extends FragmentActivity {

public void InputStartTime(View v) {
    DialogFragment newFragment = new TimePickerFragment();
    newFragment.show(getSupportFragmentManager(), "timePicker");

}

private TimePickerDialog.OnTimeSetListener mTimeSetListener =
        new TimePickerDialog.OnTimeSetListener() {
                    //Overriding onTimeSet causes an error, see below
            public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
               Log.i("TimePicker", "Time picker set!");
            }
        };

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.activity_main, menu);
    return true;
}
}

the onTimeset method is not being called as I can see from the log, if I try to override this method, I get the error: "The method onTimeSet(TimePicker, int, int) of type new TimePickerDialog.OnTimeSetListener(){} must override a superclass method"

can anyone tell me what the problem is? I've been trying to figure it out and it left me with too much frustration!

naz89
  • 71
  • 5
  • 12
  • I tested the code and it is working fine with the `@override` added above `onTimeSet` in the `TimePickerFragment` class. The listener in the MainActivity will never be called because you have not used it any where. You can always create your own interface in `TimePickerFragment` class to send the time to `MainActivity` – Naveen Apr 10 '13 at 04:17

2 Answers2

4

the onTimeset method is not being called as I can see from the log

Well, that's because when you create the TimePickerDialog, you supply the fragment as OnTimeSetListener:

return new TimePickerDialog(getActivity(), this, hour, minute, false);
                                             ^

In other words: you're not seeing the log statement because the mTimeSetListener variable in your activity never gets set as listener to the dialog you create in the fragment.

You can easily fix this by either casting the activity the fragment is attached to to your MainActivity, or, if you prefer something more reusable, have it callback through an interface. In this case you could reuse the OnTimeSetListener interface, but you could also set up your own that, for instance, passes on a Calendar object back to the activity rather than the raw hour/minute values.

In it's most basic form, it would look somewhat like this:

public class TimePickerFragment extends DialogFragment {

    @Override public Dialog onCreateDialog(Bundle savedInstanceState) {
        // ... omitted

        if (!(getActivity() instanceof OnTimeSetListener)) throw new IllegalStateException("Activity should implement OnTimeSetListener!");
        OnTimeSetListener timeSetListener =  (OnTimeSetListener) getActivity();

        // Create a new instance of TimePickerDialog and return it
        return new TimePickerDialog(getActivity(), timeSetListener, hour, minute, false);
    }
}

And then have your MainActivity implement that same interface, in stead of using an anonymous innerclass:

public class MainActivity extends FragmentActivity implements TimePickerDialog.OnTimeSetListener { 

    @Override public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
        Log.i("TimePicker", "Time picker set!");
    }
}

Update: As mentioned in the comments, to enabled support for multiple pickers, you have several options. One is to keep track of what dialog is displayed in the hosting activity; a boolean would do fine if you only need to differentiate between two pickers, otherwise an enum would be an appropriate way to implement more than two states. You'll want to make sure that this piece of information is retained during configuration changes though...

However, what I'd prefer is to do, is to be able to identify the source of the result returned in onTimeSet(...) by supplying an id to every picker. That id is then included in the result so that we can tell where it came from. I'll outline the general idea below:

public class TimePickerFragment extends DialogFragment implements OnTimeSetListener {

    private int mId;
    private TimePickerDialogListener mListener;

    private static TimePickerFragment newInstance(int id) {
        Bundle args = new Bundle();
        args.putInt("picker_id", id);
        TimePickerFragment fragment = new TimePickerFragment();
        fragment.setArguments(args);
        return fragment;
    }

    @Override public Dialog onCreateDialog(Bundle savedInstanceState) {
        // ... omitted

        mId = getArguments().getInt("picker_id");
        mListener = getActivity() instanceof TimePickerDialogListener ? (TimePickerDialogListener) getActivity() : null;

        // Create a new instance of TimePickerDialog and return it
        return new TimePickerDialog(getActivity(), this, hour, minute, false);
    }

    @Override public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
        if (mListener != null) mListener.onTimeSet(mId, view, hourOfDay, minute);
    }

    public static interface TimePickerDialogListener {
        public void onTimeSet(int id, TimePicker view, int hourOfDay, int minute);
    }

}

What we've changed above is to have the dialog itself be registered as OnTimeSetListener, for which it will then pass on data through the TimePickerDialogListener, provided the hosting activity implements that interface. So that's what we need to do in the next step. Also, note that I've added a static convenience method to create a new TimePickerFragment that takes an id. This value will be set as argument to the fragment, ensuring that it becomes part of the fragment's state, so you don't have to worry about configuration changes yourself - the framework will do that for you.

So let's change MainActivity to implement our custom interface, and have it use the newInstance method:

public class MainActivity extends FragmentActivity implements TimePickerFragment.TimePickerDialogListener { 

    private static final int START_TIME_PICKER_ID = 1;
    private static final int END_TIME_PICKER_ID = 2;

    public void InputStartTime(View v) {
        // supply the appropriate id - I'm assuming you'll be adding an InputEndTime method somewhere that will then supply END_TIME_PICKER_ID 
        DialogFragment newFragment = TimePickerFragment.newInstance(START_TIME_PICKER_ID);
        newFragment.show(getSupportFragmentManager(), "timePicker");
    }

    @Override public void onTimeSet(int id, TimePicker view, int hourOfDay, int minute) {
        Log.i("TimePicker", "Time picker set from id " + id + "!");

        // here you can compare the id value to figure out what picker this data came from
    }
}

Just one final note: I typed this directly into my browser, so be aware of any obvious typos.

MH.
  • 45,303
  • 10
  • 103
  • 116
  • Thanks alot! that worked, although the @Override still produced an error, removing it worked normally, thanks for the explanation as well, saved my life! – naz89 Apr 10 '13 at 07:12
  • Since I have two time pickers in my application, how can I differentiate between them? the code above detects onTimeSet for one of these time pickers, how can I extend it to support multiple pickers? can you please provide guidance on this? Thanks! – naz89 Apr 10 '13 at 07:18
  • @naz89: The `@Override` annotation may produce an error when implementing interface methods if you're building against Java 1.5. This was changed in Java 1.6. There's more than one way to support multiple pickers: you can supply some sort of 'id' when instantiating the dialog and set up your own interface callback that includes this id. Alternatively, just toggle a boolean in your activity based on the picker that gets displayed and check for its value in `onTimeSet(...)`. – MH. Apr 10 '13 at 07:31
  • I tried to supply the id to the onCreateDialog method the same way these answers do: http://stackoverflow.com/questions/3734981/multiple-datepickers-in-same-activity?lq=1 I tried the first answer but i'm getting alot of errors, I get the same Override error but inside the method, can you explain in code what I should do? Thanks again! – naz89 Apr 10 '13 at 12:52
  • @naz89: I wouldn't use the code the from the answer you're linking to, as that's basically the 'old' way of doing things. That's why `onCreateDialog()` is deprecated, which you may have noticed. In stead, have a look at the edit in my answer above. Hopefully it all makes sense. Let me know how you get on! – MH. Apr 10 '13 at 19:38
  • That is a top notch answer, well explained, clean coded, respect my friend! thanks alot for taking time to explain, it worked like a charm! there were two typos: 1. In TimePickerFragment: args.putInt("picker_id", id); instead of args.put("picker_id", id); 2. At the start of MainActivity: TimePickerFragment.TimePickerDialogListener instead of OnTimePickerFragment.TimePickerDialogListener – naz89 Apr 10 '13 at 20:26
  • @naz89: Glad to hear to worked out for you. Thanks for pointing out the typos - I've fixed those up. Happy coding. :) – MH. Apr 10 '13 at 20:53
1

I made the code use it.

public class TimePickerFragment extends DialogFragment{
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Use the current time as the default values for the picker
        final Calendar c = Calendar.getInstance();
        int hour = c.get(Calendar.HOUR_OF_DAY);
        int minute = c.get(Calendar.MINUTE);

        // Create a new instance of TimePickerDialog and return it
        return new TimePickerDialog(getActivity(), (MainActivity)getActivity(), hour, minute, false);
    }
}


public class MainActivity extends FragmentActivity  implements TimePickerDialog.OnTimeSetListener{

    public void InputStartTime(View v) {
        DialogFragment newFragment = new TimePickerFragment();
        newFragment.show(getSupportFragmentManager(), "timePicker");

    }

    @Override
    public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
        // TODO Auto-generated method stub
        Log.i("TimePicker", "Time picker set!");
    }

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }
}
Naveen
  • 1,703
  • 13
  • 22
  • Thanks alot! that worked, although the @Override still produced an error, removing it worked normally, thanks for the explanation as well, saved my life! – naz89 Apr 10 '13 at 07:12
  • Since I have two time pickers in my application, how can I differentiate between them? the code above detects onTimeSet for one of these time pickers, how can I extend it to support multiple pickers? can you please provide guidance on this? Thanks! – naz89 Apr 10 '13 at 07:18