3

I have onItemClickListener for the ListView that opens DatePicker dialog when an item is clicked:

list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        showDatePickerDialog(view);
    }
});

public void showDatePickerDialog(View v) {
    DialogFragment newFragment = new DatePickerFragment();
    newFragment.show(getFragmentManager(), "datePicker");
}

After date in DatePicker gets set, I have onDateSet method which I want to use to update database and set date for item that was clicked in ListView:

public void onDateSet(DatePicker view, int year, int month, int day) {
    ...
    dataSource.updateDate(adapter.getItemId(position), month + "/" + day + "/" + year);
    ...
} 

How can I pass ListView position of the item that was clicked to onDateSet so that my database helper knows which record to update?


EDIT: Here's the DatePickerFragment class:

public static class DatePickerFragment extends DialogFragment
        implements DatePickerDialog.OnDateSetListener {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Use the current date as the default date in the picker
        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);

        // Create a new instance of DatePickerDialog and return it
        return new DatePickerDialog(getActivity(), this, year, month, day);
    }

    public void onDateSet(DatePicker view, int year, int month, int day) {
        ...
        dataSource.updateDate(adapter.getItemId(position), month + "/" + day + "/" + year);
        ...
    }
}

Thanks!

Alex
  • 739
  • 1
  • 6
  • 18

2 Answers2

1

Send the position to DatePickerFragment - I guess this is a class of your own. Send this parameter through arguments (as this is a best practice to protect your code in fragment recreations):

public class DatePickerFragment extends Fragment {

    private DatePickerListener datePickerListener;
    private int listPosition = -1;
    private static final String POSITION_ARG_KEY = "POSITION_ARG_KEY";

    public static DatePickerFragment newInstance(int position) {
        DatePickerFragment fragment = new DatePickerFragment();
        Bundle args = new Bundle();
        args.putInt(POSITION_ARG_KEY, position);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Bundle args = getArguments();
        if (args != null) {
            listPosition = args.getInt(POSITION_ARG_KEY);
        }
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        if (activity instanceof DatePickerListener) {
            datePickerListener = (DatePickerListener) activity;
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        datePickerListener = null;
    }

    public void methodThatCallsActivity() {
        if(datePickerListener) {
            datePickerListener.onDateSet(datePicker, year, month, day, this.listPosition);
        }
    }

    // / the rest of the code

    public static interface DatePickerListener {
        public void onDateSet(DatePicker view, int year, int month, int day, int listPosition);
    }
}

Then in the activity that displays the fragment, (it must implement DatePickerListener):

A. send the list position:

public void showDatePickerDialog(int listPosition) {
    DialogFragment newFragment = DatePickerFragment.newInstance(listPosition);
    newFragment.show(getFragmentManager(), "datePicker");
}

B. call this method from list item click:

list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        showDatePickerDialog(position);
    }
});

C. You have the list position in onDateSet implementation:

public void onDateSet(DatePicker view, int year, int month, int day, int listPosition) {
    // your relevant code
}

To brief: send the list position through arguments and parameters from listview to fragment and from there to parent activity.

gunar
  • 14,660
  • 7
  • 56
  • 87
  • When I try to pass position to **onDateSet**, I get message saying that I need to _implement abstract method onDateSet(DatePicker, int, int, int)_. It doesn't like me adding another argument to the method signature since I'm implementing **DatePickerDialog.OnDateSetListener**. Any way of doing it while **extending DialogFragment and implementing DatePickerDialog.OnDateSetListener**? I'll give your way a shot if I can't figure it out, thanks! – Alex Sep 22 '13 at 21:26
  • `onDateSet(DatePicker, int, int, int)` belongs to an interface you have defined? Or is it Android defined? – gunar Sep 23 '13 at 06:47
  • It's Android-defined in `DatePickerDialog.OnDateSetListener`. – Alex Sep 23 '13 at 11:57
  • then create a new interface `LocalDatePickerListener` and use the same approach I described. The difference is that `DatePickerListener` is implemented by the fragment, while `LocalDatePickerListener` will be implemented by host activity. – gunar Sep 23 '13 at 12:33
0

Solved it by doing the following:

Made DatePickerFragment it's own class (not nested inside MainActivity class):

public class DatePickerFragment extends DialogFragment {
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Use the current date as the default date in the picker
        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);

        // Create a new instance of DatePickerDialog and return it
        return new DatePickerDialog(getActivity(), (MainActivity)getActivity(), year, month, day);
    }
}

This is how my MainActivity class ended up looking:

public class MainActivity extends ListActivity implements DatePickerDialog.OnDateSetListener {
    private long listId = -1;
    private int listPosition = -1;
    private String date;
    ...

    protected void onCreate(Bundle savedInstanceState) {
        list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                listId = id;
                listPosition = position;
                showDatePickerDialog(view);
    ...

    // Show DatePicker when clicking on contact in ListView
    public void showDatePickerDialog(View view) {
        DialogFragment newFragment = new DatePickerFragment();
        newFragment.show(getFragmentManager(), "datePicker");
    }

    // Update database and ListView adapter with selected date
    public void onDateSet(DatePicker view, int year, int month, int day) {
        date = (month + 1) + " " + day + ", " + year;
        ...
        dataSource.updateDate(listId, date);
        adapter.setDate(listPosition, date);
        ...
        adapter.notifyDataSetChanged();
        dataSource.close();
    }

Side note: Pressing cancel button of DatePicker (and Android back button) still saves default date into database and updates the adapter. Here's a post to get around the issue: https://stackoverflow.com/a/15289569/1261256

Community
  • 1
  • 1
Alex
  • 739
  • 1
  • 6
  • 18
  • 1
    if the dialog is opened and at the same time you receive a phone call or any kind of action that makes your activity to go in the background then the activity is eligible to be destroyed. If it is destroyed and then your activity is recreated `listId` is reset to zero. Also, it's a bad practice to depend on the state of a field in a state flow as you have. Sustaining communicating of data through parameters and not through state-awareness. – gunar Sep 23 '13 at 06:45
  • If the activity is destroyed, why does it matter that the `listId` gets reset? When the user goes to select the ListView row again, it will get set appropriately, correct? I see your point about depending on field state being bad practice, though. Any thoughts on how to fix my current approach by passing position/id via arguments? Thank you. – Alex Sep 23 '13 at 12:03
  • I am talking about the case when the dialog is opened - actually the fragment that displays the dialog is added & visible. So at this point you have a listId, let's say 10. When the activity is destroyed at this state and then restored, the dialog will be opened (Android takes care of that) and listId is ... zero! The author of this question has to change current `onDateSet` so it will accept an extra int - the list position. – gunar Sep 23 '13 at 12:31