Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom calendar view in Android

I'm building a custom calendar view for my android app that allows you to swipe between months. I've created custom calendar square views, which I've embedded into a custom calendar month view, and everything is working perfectly on the 1-month scale.

Unfortunately, now I'm stuck. I now want to embed my custom calendar month views into an infinite view pager, so that I can scroll forward and backward through the calendar indefinitely.

I've tried adapting this horizontal pager for infinite scrolling by using a simple trick. I hold an array of 3 of my calendar month views and update the list based on where the user scrolls to. Example...

  1. [July, August, September] (focus is on August, then user swipes to September)
  2. [July, August, September] (focus is now on September)
  3. [August, August, September] (shift August left by one, overwriting July)
  4. [August, September, September] (shift September left by one, overwriting August)
  5. [August, September, September] (set the view to the middle September, so we're centered again)
  6. [August, September, October] (overwrite the 2nd September with the actual next month)

When I do this, though, there's a noticeable flash on the screen when swiping to the next month.

Here's the code for forward scrolling (note: backward scrolling has the same issue)

private void updateViewsForForwardScroll() {
    // Note: INDEX_PREV = 0; INDEX_CURR = 1; INDEX_NEXT = 2
    ((CalendarMonthView) getChildAt(INDEX_PREV)).showMonth(oneMonthPriorTo(currentMonth));
    ((CalendarMonthView) getChildAt(INDEX_CURR)).showMonth(currentMonth);
    setCurrentScreen(INDEX_CURR, false);
    ((CalendarMonthView) getChildAt(INDEX_NEXT)).showMonth(oneMonthAfter(currentMonth));
}

I think the problem is that setCurrentScreen() finishes before showMonth(currentMonth), so the view at INDEX_CURR is still updating when the screen is set. I tried solving that by using the following strategy instead...

  1. [July, August, September] (focus is on August, then user swipes to September)
  2. [July, August, September] (focus is now on September)
  3. [August, September] (remove July, but store it for recycling)
  4. [August, September, October] (recycle July to instead display October, and add the view)

I haven't touched the currently focused view at all, but there's still a flash! This time, the screen flashes from September, to August, then back to September.

So what am I doing wrong? Is there a way to do what I'm trying to do without the user knowing about it? If not, is there an existing class I can leverage?

(As a side question, is there any way I can customize the Android CalendarView visually? That would really solve all my problems...)

Thanks in advance!

like image 319
bmat Avatar asked Nov 13 '22 23:11

bmat


1 Answers

I ended up getting it to work by extending FragmentStatePagerAdapter, and also integrating user113215's suggestion. I didn't go for truly infinite scrolling, but rather virtually infinite (i.e. 10,000 months, or 833 years). Here's the bulk of my code:

public static class CalendarAdapter extends FragmentStatePagerAdapter {
    private static final int NUM_MONTHS = 10000;
    private static final int INDEX_EPOCH = NUM_MONTHS/2;
    private static final int MONTHS_PER_YEAR = 12;
    private static final Calendar calendarMonthEpoch = new GregorianCalendar(2012, Calendar.AUGUST, 1);

    // ...

    @Override
    public int getCount() {
        return NUM_MONTHS;
    }

    @Override
    public Fragment getItem(int position) {
        return CalendarMonthFragment.newInstance(calendarMonthAtPosition(position));
    }

    private static Calendar calendarMonthAtPosition(int position) {
        int offset = position - INDEX_EPOCH;
        Calendar calMonthAtPosition = (Calendar) calendarMonthEpoch.clone(); 
        calMonthAtPosition.add(Calendar.MONTH, offset % MONTHS_PER_YEAR);
        calMonthAtPosition.add(Calendar.YEAR, offset / MONTHS_PER_YEAR);

        return calMonthAtPosition;
    }

    // ...

}
like image 159
bmat Avatar answered Dec 24 '22 20:12

bmat