com.philliphsu.bottomsheetpickers.date.PagingMonthAdapter.java Source code

Java tutorial

Introduction

Here is the source code for com.philliphsu.bottomsheetpickers.date.PagingMonthAdapter.java

Source

/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.philliphsu.bottomsheetpickers.date;

import android.content.Context;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.v4.view.PagerAdapter;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;

import com.philliphsu.bottomsheetpickers.Utils;
import com.philliphsu.bottomsheetpickers.date.MonthView.OnDayClickListener;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;

import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;

/**
 * An adapter for a pager of {@link MonthView} items.
 */
class PagingMonthAdapter extends PagerAdapter implements OnDayClickListener {
    private static final String TAG = "SimpleMonthAdapter";

    private static final String KEY_POSITIONS = "positions";
    private static final String KEY_MONTH_YEAR_TITLES = "month_year_titles";

    private final Context mContext;
    private final boolean mThemeDark;
    private final int mAccentColor;
    private final SparseArray<String> mMonthYearTitles = new SparseArray<>();
    private final ArrayList<MonthView> mMonthViews = new ArrayList<>();
    protected final DatePickerController mController;

    private CalendarDay mSelectedDay;
    private MonthView mCurrentPrimaryItem;

    protected static int WEEK_7_OVERHANG_HEIGHT = 7;
    protected static final int MONTHS_IN_YEAR = 12;

    public PagingMonthAdapter(Context context, DatePickerController controller) {
        this(context, controller, false);
    }

    public PagingMonthAdapter(Context context, DatePickerController controller, boolean themeDark) {
        this(context, controller, themeDark, Utils.getThemeAccentColor(context));
    }

    public PagingMonthAdapter(Context context, DatePickerController controller, boolean themeDark,
            int accentColor) {
        mContext = context;
        mController = controller;
        mThemeDark = themeDark;
        mAccentColor = accentColor;
        init();
        setSelectedDay(mController.getSelectedDay());
    }

    /**
     * Updates the selected day and related parameters.
     *
     * @param day The day to highlight
     */
    public void setSelectedDay(CalendarDay day) {
        mSelectedDay = day;
        notifyDataSetChanged(); // This won't refresh our views!
        // In the ListView-based day picker, notifyDataSetChanged() would have refreshed this
        // adapter's contents, so instantiateItem() is called again on the entire data set.
        // This would have called each MonthView's setMonthParams() with the correct value for the
        // selectedDay (either -1 or the actual selected day if the selection is in that month)
        // and invalidate(). However, PagerAdapter's implementation of notifyDataSetChanged()
        // won't do all this for us, so we have to manually do it.
        for (MonthView mv : mMonthViews) {
            mv.setSelectedDay(mv == mCurrentPrimaryItem ? day.day : -1);
            mv.invalidate();
        }
    }

    public CalendarDay getSelectedDay() {
        return mSelectedDay;
    }

    /**
     * Set up the gesture detector and selected time
     */
    protected void init() {
        mSelectedDay = new CalendarDay(System.currentTimeMillis());
    }

    @Override
    public int getCount() {
        final Calendar minDate = mController.getMinDate();
        final Calendar maxDate = mController.getMaxDate();
        final int minMonth = minDate == null ? 0 : minDate.get(Calendar.MONTH);

        // The number of months remaining in minDate's year, from minMonth forward.
        int monthsInMinYear = MONTHS_IN_YEAR - minMonth;
        // The number of months in maxDate's year, up to maxDate's month.
        // From Calendar.JANUARY (0) forward.
        int monthsInMaxYear = maxDate == null ? MONTHS_IN_YEAR : maxDate.get(Calendar.MONTH) + 1;

        int count = monthsInMinYear + monthsInMaxYear;
        // Add the number of months in between the min and max dates.
        // Subtract 2 from the year range because we already counted the min and max years.
        // Add 1 to get the total number of years in the year range.
        // e.g. There are 11 years in the range [2016, 2026].
        // => net difference = -1
        count += (mController.getMaxYear() - mController.getMinYear() - 1) * MONTHS_IN_YEAR;
        return count;
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == object;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        MonthView v;
        HashMap<String, Integer> drawingParams = null;
        v = createMonthView(mContext, mThemeDark, mAccentColor);
        // Set up the new view
        v.setClickable(true);
        v.setOnDayClickListener(this);
        if (drawingParams == null) {
            drawingParams = new HashMap<>();
        }
        drawingParams.clear();

        final int month = getMonth(position);
        final int year = getYear(position);

        int selectedDay = -1;
        if (isSelectedDayInMonth(year, month)) {
            selectedDay = mSelectedDay.day;
        }

        // Invokes requestLayout() to ensure that the recycled view is set with the appropriate
        // height/number of weeks before being displayed.
        v.reuse();

        drawingParams.put(MonthView.VIEW_PARAMS_SELECTED_DAY, selectedDay);
        drawingParams.put(MonthView.VIEW_PARAMS_YEAR, year);
        drawingParams.put(MonthView.VIEW_PARAMS_MONTH, month);
        drawingParams.put(MonthView.VIEW_PARAMS_WEEK_START, mController.getFirstDayOfWeek());
        v.setMonthParams(drawingParams);
        v.invalidate();
        container.addView(v, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
        mMonthYearTitles.append(position, v.getMonthAndYearString());
        mMonthViews.add(v);
        return v;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView((View) object);
        mMonthYearTitles.delete(position);
        mMonthViews.remove(object);
    }

    @Override
    public void setPrimaryItem(ViewGroup container, int position, Object object) {
        mCurrentPrimaryItem = (MonthView) object;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return mMonthYearTitles.get(position);
    }

    @Override
    public Parcelable saveState() {
        Bundle state = null;
        final int size = mMonthYearTitles.size();
        if (size > 0) {
            state = new Bundle();
            String[] titles = new String[size];
            int[] positions = new int[size];
            for (int i = 0; i < size; i++) {
                titles[i] = mMonthYearTitles.valueAt(i);
                positions[i] = mMonthYearTitles.keyAt(i);
            }
            state.putStringArray(KEY_MONTH_YEAR_TITLES, titles);
            state.putIntArray(KEY_POSITIONS, positions);
        }
        return state;
    }

    @Override
    public void restoreState(Parcelable state, ClassLoader loader) {
        if (state != null) {
            Bundle bundle = (Bundle) state;
            bundle.setClassLoader(loader);
            String[] titles = bundle.getStringArray(KEY_MONTH_YEAR_TITLES);
            int[] positions = bundle.getIntArray(KEY_POSITIONS);
            mMonthYearTitles.clear();
            if (titles != null && positions != null) {
                // Both arrays should be the same size, so use either length.
                for (int i = 0; i < titles.length; i++) {
                    mMonthYearTitles.append(positions[i], titles[i]);
                }
            }
        }
    }

    public MonthView createMonthView(Context context) {
        return createMonthView(context, false);
    }

    public MonthView createMonthView(Context context, boolean themeDark) {
        return createMonthView(context, themeDark, mAccentColor);
    }

    public MonthView createMonthView(Context context, boolean themeDark, int accentColor) {
        final MonthView monthView = new SimpleMonthView(context);
        monthView.setDatePickerController(mController);
        monthView.setTheme(context, themeDark);
        monthView.setTodayNumberColor(accentColor);
        monthView.setSelectedCirclePaintColor(accentColor);
        return monthView;
    }

    private boolean isSelectedDayInMonth(int year, int month) {
        return mSelectedDay.year == year && mSelectedDay.month == month;
    }

    @Override
    public void onDayClick(MonthView view, CalendarDay day) {
        if (day != null) {
            onDayTapped(day);
        }
    }

    /**
     * Maintains the same hour/min/sec but moves the day to the tapped day.
     *
     * @param day The day that was tapped
     */
    protected void onDayTapped(CalendarDay day) {
        mController.tryVibrate();
        mController.onDayOfMonthSelected(day.year, day.month, day.day);
        setSelectedDay(day);
    }

    /**
     * Returns the month (0 - 11) displayed at this position.
     */
    final int getMonth(int position) {
        return (position + getMonthOffset()) % MONTHS_IN_YEAR;
    }

    /**
     * Returns the year displayed at this position.
     */
    final int getYear(int position) {
        return (position + getMonthOffset()) / MONTHS_IN_YEAR + mController.getMinYear();
    }

    /**
     * @return The page position at which the given day is located.
     */
    final int getPosition(CalendarDay day) {
        int positionOfYear = MONTHS_IN_YEAR * (day.year - mController.getMinYear()) - getMonthOffset();
        return positionOfYear + day.month;
    }

    private int getMonthOffset() {
        Calendar minDate = mController.getMinDate();
        return minDate == null ? 0 : minDate.get(Calendar.MONTH);
    }
}