Implement a DatePicker inside a Fragment

Viewed 20961

I have below codes to display a date picker inside an Activity, but when I tried to convert my activity to Fragment, I am getting error. I can't really see any difference in code compare to solutions in SO to implement a DialogFragment inside a Fragment.

Can you please point out what am I missing?

MainFragment:

public class AddEntryFragment extends Fragment implements DatePickerFragment.TheListener{
    EditText datpurchased;
    public AddEntryFragment(){}

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.activity_addentry, container, false);
        return rootView;
    }

    @Override
    public void onStart() {
        super.onStart();
        datpurchased = (EditText) getView().findViewById(R.id.datepurchased);
        datpurchased.setOnClickListener(onClickListener);
    }

    private OnClickListener onClickListener = new OnClickListener() {
        @Override
        public void onClick(View v) {
            switch(v.getId()){
                case R.id.datepurchased:
                    showdate(v);
                break;
                default:
                break;
            }

      }
    };

    public void showdate(View v) {
        DialogFragment newFragment = new DatePickerFragment();
        Bundle bundle = new Bundle();
        bundle.putString("dateAsText",datpurchased.getText().toString());
        newFragment.setArguments(bundle); 
        newFragment.show(getFragmentManager(), "datePicker");
    }

    public void returnDate(String date) {
        datpurchased.setText(date);
    }
}

DatePickerFragment:

public class DatePickerFragment extends DialogFragment implements DatePickerDialog.OnDateSetListener {
    TheListener listener;

    public interface TheListener{
        public void returnDate(String date);
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState){
        Bundle bundle = getArguments();
        String date = bundle.getString("dateAsText");
        int year, month, day;
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd",Locale.US);
        df.setLenient(false);
        Date d = null;
        Calendar c=Calendar.getInstance();
        try {
            d = df.parse(date);
            c.setTime(d);
        } catch (ParseException e) {
            //e.printStackTrace();
        }
        year=c.get(Calendar.YEAR);
        month=c.get(Calendar.MONTH);
        day=c.get(Calendar.DAY_OF_MONTH);
        listener = (TheListener) getActivity();
        return new DatePickerDialog(getActivity(), this, year, month, day);
    }

    public void onDateSet(DatePicker view, int year, int month, int day){
        Calendar c = Calendar.getInstance();
        c.set(year, month, day);

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd",Locale.US);
        String formattedDate = sdf.format(c.getTime());
        if (listener != null) 
        {
          listener.returnDate(formattedDate); 

        }
    }
}

LogCat:

12-18 23:35:41.094: D/AndroidRuntime(1058): Shutting down VM
12-18 23:35:41.094: W/dalvikvm(1058): threadid=1: thread exiting with uncaught exception (group=0xb4a65b90)
12-18 23:35:41.124: E/AndroidRuntime(1058): FATAL EXCEPTION: main
12-18 23:35:41.124: E/AndroidRuntime(1058): Process: com.migrationdesk.mylibman, PID: 1058
12-18 23:35:41.124: E/AndroidRuntime(1058): java.lang.ClassCastException: com.migrationdesk.mylibman.NavContainer cannot be cast to com.migrationdesk.mylibman.DatePickerFragment$TheListener
12-18 23:35:41.124: E/AndroidRuntime(1058):     at com.migrationdesk.mylibman.DatePickerFragment.onCreateDialog(DatePickerFragment.java:41)
12-18 23:35:41.124: E/AndroidRuntime(1058):     at android.app.DialogFragment.getLayoutInflater(DialogFragment.java:398)
12-18 23:35:41.124: E/AndroidRuntime(1058):     at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:890)
12-18 23:35:41.124: E/AndroidRuntime(1058):     at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1062)
12-18 23:35:41.124: E/AndroidRuntime(1058):     at android.app.BackStackRecord.run(BackStackRecord.java:684)
12-18 23:35:41.124: E/AndroidRuntime(1058):     at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1447)
12-18 23:35:41.124: E/AndroidRuntime(1058):     at android.app.FragmentManagerImpl$1.run(FragmentManager.java:443)
12-18 23:35:41.124: E/AndroidRuntime(1058):     at android.os.Handler.handleCallback(Handler.java:733)
12-18 23:35:41.124: E/AndroidRuntime(1058):     at android.os.Handler.dispatchMessage(Handler.java:95)
12-18 23:35:41.124: E/AndroidRuntime(1058):     at android.os.Looper.loop(Looper.java:137)
12-18 23:35:41.124: E/AndroidRuntime(1058):     at android.app.ActivityThread.main(ActivityThread.java:4998)
12-18 23:35:41.124: E/AndroidRuntime(1058):     at java.lang.reflect.Method.invokeNative(Native Method)
12-18 23:35:41.124: E/AndroidRuntime(1058):     at java.lang.reflect.Method.invoke(Method.java:515)
12-18 23:35:41.124: E/AndroidRuntime(1058):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:777)
12-18 23:35:41.124: E/AndroidRuntime(1058):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:593)
12-18 23:35:41.124: E/AndroidRuntime(1058):     at dalvik.system.NativeStart.main(Native Method)
4 Answers

I got that exact same casting error, and after looking for a solution, but not finding one that worked well, I decided to come up with one of my own.

My approach is to use a little trick. Instead of trying to get the class that extends DialogFragment to accept my Fragment as a DatePickerDialog.OnDateSetListener, I allow the DialogFragment class to implement the interface and receive the callback. When the callback is invoked, it will then pass it along to my Fragment, which also implements the same interface. In effect, the DialogFragment class forwards the callback. Here is the DialogFragment class, which you might have seen in many other stackoverflow posts, with the exception it has a public method, setListeningActivity(). setListeningActivity() takes a fragment, an activity, or really any class that implements DatePickerDialog.OnDateSetListener. This is the class to which any callbacks to the DialogFragment class will be forwarded.

public class DatePickerFragment extends DialogFragment implements DatePickerDialog.OnDateSetListener  {

    private DatePickerDialog.OnDateSetListener m_listener = null;

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        final Calendar c = Calendar.getInstance();
        int year = c.get(Calendar.YEAR);
        int month = c.get(Calendar.MONTH);
        int day = c.get(Calendar.DAY_OF_MONTH);

        return new DatePickerDialog(getActivity(),
                this,
                year, month, day);
    }

    public void setListeningActivity(DatePickerDialog.OnDateSetListener listener)
    {
        m_listener = listener;
    }

    @Override
    public void onDateSet(DatePicker view, int year, int month, int day) {
        if (m_listener != null) {
            m_listener.onDateSet(view, year, month, day);
        }
    }

Next is the code that uses it:

public class AppPatientInfoFragment extends Fragment implements DatePickerDialog.OnDateSetListener {
...
    private void setDate(final Calendar calendar) {
        final DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM);
        ((TextView) m_activity.findViewById(R.id.dob)).setText(dateFormat.format(calendar.getTime()));
    }

    public void onDateSet(DatePicker view, int year, int month, int day) {
        Calendar c = Calendar.getInstance();
        c.set(year, month, day);
        setDate(c);
    }

...

DatePickerFragment fragment = new DatePickerFragment();
                    fragment.setListeningActivity(AppPatientInfoFragment.this);
                    fragment.show(m_activity.getFragmentManager(), "date");

Basically, what happens is DatePickerFragment's onDateSet() gets called, because it is the actually callback registered with the DatePicker, and then DatePickerFragment's onDateSet() will forward it to AppPatientInfoFragment's onDateSet().

Related