Search code examples
androidlistviewfilterandroid-cursorloader

Reloading ListView contents from DB based on datepicker changes


I will start by saying that this is a 2-part question. I have a an activity with 3 buttons and a listview. One button opens a dialogfragment with a datepicker, and the button text displays the selected date. The other 2 buttons allow you to go to the previous/next days respectively. The ListView should show the the records that correspond to the selected date.

Currently, when loading the activity, the button shows today's date and the listview shows today's records from the database. I've managed to create a method that restarts the loader and call it within the previous/next buttons, so when navigating one day at a time, the listview updates to show the correct records, but when I change the date by calling the datepicker dialog, I can't seem to find a way to get it to update correctly - This is the first part of the question.

The second part is a little harder to explain. When I select a date with 4 records (4 lines in the listview) all 4 records show, but then for example, if I go to the next day which only has 2 records (2 lines in the listview) it will correctly show the first 2 records, but then the next 2 rows still show the data from the previous date. To explain this better, let's assume each row in the listview shows the DB records _id number. On date 1 (which has 4 records) I have ID 1,2,3,4 showing, but when changing to date 2 (which has 2 records) it shows ID 5,6,3,4.

I assume by incorporating a method that clears the listview contents completely and then repopulates with the new date, I can solve both issues at once. I'm just not sure how to do this. My code is below:

public class FuncLogbook extends ListActivity implements
LoaderManager.LoaderCallbacks<Cursor>, OnItemClickListener {        

Button btn_logbook_date;

LogDateDialogFragment fragDate;
Calendar now;
Date nowD, newD, PrevDate, NextDate;

int lMonth;
String strDate;

private SimpleCursorAdapter adapter;        

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView (R.layout.dash_logbook);     
    this.getListView().setDividerHeight(2);         

    ListView list = getListView();
    list.setOnItemClickListener(this);

    btn_logbook_date = (Button) findViewById(R.id.btn_logbook_date);
    now = Calendar.getInstance();
    nowD = now.getTime();

    SimpleDateFormat dFormatter = new SimpleDateFormat("dd MMM yyyy");
    strDate = dFormatter.format(nowD);
    btn_logbook_date.setText(strDate);              

    fillData();             
}       

public void modifyDate(View v) {
    int id = v.getId ();
    switch (id) {
    case R.id.btn_logbook_date :
        showDialogDate();
        break;
    case R.id.btn_logbook_prev :
        updateDatePrev();
        break;
    case R.id.btn_logbook_next :
        updateDateNext();
        break;
    }   
}

public interface LogDateDialogFragmentListener {
    public void logbookChangedDate(int year, int month, int day);
}

public void showDialogDate() {
    FragmentTransaction ftLogDate = getFragmentManager().beginTransaction();
    fragDate = LogDateDialogFragment.newInstance(this, new LogDateDialogFragmentListener() {
        public void logbookChangedDate(int year, int month, int day) {          
            now.set(Calendar.YEAR, year);
            now.set(Calendar.MONTH, month);
            now.set(Calendar.DAY_OF_MONTH, day);
            newD = now.getTime();
            SimpleDateFormat newDFormatter = new SimpleDateFormat("dd MMM yyyy");
            strDate = newDFormatter.format(newD);
            btn_logbook_date.setText(strDate);
        }}, now);
    fragDate.show(ftLogDate, "DateDialogFragment");     
}

public void updateDatePrev() {
    String prevVar[] = btn_logbook_date.getText().toString().split(" ");

    if (prevVar[1].equalsIgnoreCase("Jan")) {
        lMonth = 0;
    } else if (prevVar[1].equalsIgnoreCase("Feb")) {
        lMonth = 1;
    } else if (prevVar[1].equalsIgnoreCase("Mar")) {
        lMonth = 2;
    } else if (prevVar[1].equalsIgnoreCase("Apr")) {
        lMonth = 3;
    } else if (prevVar[1].equalsIgnoreCase("May")) {
        lMonth = 4;
    } else if (prevVar[1].equalsIgnoreCase("Jun")) {
        lMonth = 5;
    } else if (prevVar[1].equalsIgnoreCase("Jul")) {
        lMonth = 6;
    } else if (prevVar[1].equalsIgnoreCase("Aug")) {
        lMonth = 7;
    } else if (prevVar[1].equalsIgnoreCase("Sep")) {
        lMonth = 8;
    } else if (prevVar[1].equalsIgnoreCase("Oct")) {
        lMonth = 9;
    } else if (prevVar[1].equalsIgnoreCase("Nov")) {
        lMonth = 10;
    } else if (prevVar[1].equalsIgnoreCase("Dec")) {
        lMonth = 11;
    }

    int lYear = Integer.parseInt(prevVar[2]);
    int lDay = Integer.parseInt(prevVar[0]);
    now = Calendar.getInstance();
    now.set(Calendar.YEAR, lYear);
    now.set(Calendar.MONTH, lMonth);
    now.set(Calendar.DAY_OF_MONTH, lDay - 1);
    PrevDate = now.getTime();
    SimpleDateFormat prevDFormatter = new SimpleDateFormat("dd MMM yyyy");
    strDate = prevDFormatter.format(PrevDate);
    btn_logbook_date.setText(strDate);

    refillData();       
}

public void updateDateNext() {
// Same as updateDatePrev() method, but moves forward 1 day, instead of backward
}

protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
    super.onActivityResult(requestCode, resultCode, intent);
}

private void fillData() {       
    String[] from = new String[] { AddDBHelper.KEY_BGL, AddDBHelper.KEY_ROWID,
            AddDBHelper.KEY_CATEG, AddDBHelper.KEY_TIME };
    int[] to = new int[] {R.id.logBGL, R.id.logRowID, R.id.logCateg, R.id.logTime };

    getLoaderManager().initLoader(0, null, this);
    adapter = new SimpleCursorAdapter(this, R.layout.logbook_item, null, from, to, 0);

    setListAdapter(adapter);
}

private void refillData() { 
    getLoaderManager().restartLoader(0, null, this);        
}

public Loader<Cursor> onCreateLoader(int id, Bundle args) { 
     String selection = AddDBHelper.KEY_DATE + "=?";
     String[] selectionArgs = { String.valueOf(btn_logbook_date.getText().toString()) };

    CursorLoader cl = new CursorLoader(this, DBProvider.CONTENT_URI,
            null, selection, selectionArgs, null);
    return cl;
}

public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    adapter.swapCursor(data);
}

public void onLoaderReset(Loader<Cursor> loader) {
    // data is not available anymore; delete reference
    adapter.swapCursor(null);
}
}

UPDATE

Displaying query information from ContentProvider:

@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
        String sortOrder) {

    // Using SQLiteQueryBuilder instead of query() method
    SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();     

    // Set the DB table to query
    queryBuilder.setTables(AddDBHelper.DB_TABLE);

    int uriType = sURIMatcher.match(uri);
    switch (uriType) {
    case DBAllEntries:
        //no filter or WHERE clause
        break;
    case DBSingleEntry:
        //add ROWID filter when selecting individual item
        queryBuilder.appendWhere(AddDBHelper.KEY_ROWID + "="
                + uri.getLastPathSegment());
        break;      
    default:
        throw new IllegalArgumentException("Unknown URI: " + uri);
    }

    SQLiteDatabase sqlDB = mDBP.getWritableDatabase();
    Cursor cursor = queryBuilder.query(sqlDB, projection, selection,
            selectionArgs, null, null, sortOrder);
    // Ensure potential listeners are getting notified
    cursor.setNotificationUri(getContext().getContentResolver(), uri);

    return cursor;
}

UPDATE

The below call to refillData() worked with the first date change, but did not work again. After restarting the emulator and trying again, I was able to see records for 2 separate date changes but then it would not change data again.

public void showDialogDate() {
    FragmentTransaction ftLogDate = getFragmentManager().beginTransaction();
    fragDate = LogDateDialogFragment.newInstance(this, new LogDateDialogFragmentListener() {
        public void logbookChangedDate(int year, int month, int day) {          
            now.set(Calendar.YEAR, year);
            now.set(Calendar.MONTH, month);
            now.set(Calendar.DAY_OF_MONTH, day);
            newD = now.getTime();
            SimpleDateFormat newDFormatter = new SimpleDateFormat("dd MMM yyyy");
            strDate = newDFormatter.format(newD);
            btn_logbook_date.setText(strDate);

            refillData();

        }}, now);
    fragDate.show(ftLogDate, "DateDialogFragment");     
}

With the below call, data does not change after the date is changed, but when I press the button to open the datepicker again, I can see the listview in the background (behind the dialog fragment) update. For example, I select Date 2, data does not change. I open the datepicker and date 2 data displays behind the dialog. I set the datepicker to date 3 but the listview still shows date 2's data. If open the datepicker again, date 3's data loads in the background behind the dialog fragment.

I get the same result if I call refillData() after the FragDate.show(...) line or before the FragmentTransaction ftLogDate... line.

public void showDialogDate() {
    FragmentTransaction ftLogDate = getFragmentManager().beginTransaction();
    fragDate = LogDateDialogFragment.newInstance(this, new LogDateDialogFragmentListener() {
        public void logbookChangedDate(int year, int month, int day) {          
            now.set(Calendar.YEAR, year);
            now.set(Calendar.MONTH, month);
            now.set(Calendar.DAY_OF_MONTH, day);
            newD = now.getTime();
            SimpleDateFormat newDFormatter = new SimpleDateFormat("dd MMM yyyy");
            strDate = newDFormatter.format(newD);
            btn_logbook_date.setText(strDate);              
        }}, now);
    refillData();
    fragDate.show(ftLogDate, "DateDialogFragment");     
}

Solution

  • Let the FuncLogbook activity implement the listener of the date set event, LogDateDialogFragmentListener :

    public class FuncLogbook extends ListActivity implements
    LoaderManager.LoaderCallbacks<Cursor>, OnItemClickListener, LogDateDialogFragmentListener {
    
    //...
    

    Then you'll need to implement the logbookChangedDate in the activity:

    @Override
    public void logbookChangedDate(int year, int month, int day) {          
                now.set(Calendar.YEAR, year);
                now.set(Calendar.MONTH, month);
                now.set(Calendar.DAY_OF_MONTH, day);
                newD = now.getTime();
                SimpleDateFormat newDFormatter = new SimpleDateFormat("dd MMM yyyy");
                strDate = newDFormatter.format(newD);
                btn_logbook_date.setText(strDate);
                refillData();
    }
    

    and the method to show the dialog will be :

    public void showDialogDate() {
        FragmentTransaction ftLogDate = getFragmentManager().beginTransaction();
        fragDate = LogDateDialogFragment.newInstance(this, now);
        fragDate.show(ftLogDate, "DateDialogFragment");     
    }
    

    Then you could register the activity as a listener in the LogDateDialogFragment in the onAttach callback or with another setter method.