update calendar
This commit is contained in:
parent
d1d9055ba7
commit
adcf29eeea
|
|
@ -28,6 +28,7 @@ dependencies {
|
|||
compile 'io.reactivex:rxandroid:1.2.1'
|
||||
|
||||
provided 'com.android.support:appcompat-v7:24.2.1'
|
||||
provided 'com.android.support:recyclerview-v7:24.2.1'
|
||||
|
||||
provided 'com.squareup.retrofit2:retrofit:2.1.0'
|
||||
provided 'com.squareup.okhttp3:okhttp:3.4.1'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,309 @@
|
|||
/*
|
||||
* Copyright (c) 2016 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
|
||||
*
|
||||
* This file is part of RoboSwag library.
|
||||
*
|
||||
* 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 ru.touchin.templates.calendar;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.StaggeredGridLayoutManager;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
||||
|
||||
/**
|
||||
* Created by Ilia Kurtov on 17/03/2016.
|
||||
* Adapter for Calendar view. Use with {@link CalendarRecyclerView}.
|
||||
*
|
||||
* @param <TDayViewHolder> Type of ViewHolders of a day with a date;
|
||||
* @param <THeaderViewHolder> Type of ViewHolders of a months header;
|
||||
* @param <TEmptyViewHolder> Type of ViewHolders of an empty cell.
|
||||
*/
|
||||
public abstract class CalendarAdapter<TDayViewHolder extends RecyclerView.ViewHolder, THeaderViewHolder extends RecyclerView.ViewHolder,
|
||||
TEmptyViewHolder extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||
|
||||
public static final int HEADER_ITEM_TYPE = 0;
|
||||
public static final int EMPTY_ITEM_TYPE = 1;
|
||||
public static final int DAY_ITEM_TYPE = 2;
|
||||
|
||||
public static final int MONTHS_IN_YEAR = 12;
|
||||
public static final long ONE_DAY_LENGTH = TimeUnit.DAYS.toMillis(1);
|
||||
|
||||
@NonNull
|
||||
private final List<CalendarItem> calendarItems;
|
||||
@Nullable
|
||||
private Integer startSelectionPosition;
|
||||
@Nullable
|
||||
private Integer endSelectionPosition;
|
||||
@Nullable
|
||||
private String[] monthsNames;
|
||||
|
||||
/**
|
||||
* Constructor that takes all necessary data to initialize.
|
||||
*
|
||||
* @param startDate First date in the calendar range;
|
||||
* @param endDate Last date (not inclusive) in the calendar range;
|
||||
* @param monthsNames String array of months names where #0 is January and #11 is December.
|
||||
*/
|
||||
public CalendarAdapter(@NonNull final DateTime startDate, @NonNull final DateTime endDate, @Nullable final String... monthsNames) {
|
||||
super();
|
||||
if (monthsNames != null && monthsNames.length == MONTHS_IN_YEAR) {
|
||||
this.monthsNames = monthsNames;
|
||||
}
|
||||
calendarItems = CalendarUtils.fillRanges(startDate, endDate);
|
||||
if (calendarItems.isEmpty()) {
|
||||
throw new ShouldNotHappenException("There is no items in calendar with startDate: " + DateTimeFormat.fullDate().print(startDate)
|
||||
+ ", and endDate: " + DateTimeFormat.fullDate().print(endDate));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set selected dates range in calendar. Call this method before attaching this adapter to {@link CalendarRecyclerView}.
|
||||
*
|
||||
* @param startSelectionDate First date that should be selected;
|
||||
* @param endSelectionDate Last date that should be selected (inclusive).
|
||||
*/
|
||||
public void setSelectedRange(@Nullable final DateTime startSelectionDate, @Nullable final DateTime endSelectionDate) {
|
||||
if (startSelectionDate != null) {
|
||||
startSelectionPosition = CalendarUtils.findPositionByDate(calendarItems, startSelectionDate.withTimeAtStartOfDay().getMillis());
|
||||
}
|
||||
if (endSelectionDate != null) {
|
||||
endSelectionPosition = CalendarUtils.findPositionByDate(calendarItems, endSelectionDate.withTimeAtStartOfDay().getMillis());
|
||||
}
|
||||
|
||||
notifySelectedDaysChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method finds the number of the first cell of selected range.
|
||||
*
|
||||
* @param departure Pass true if {@link CalendarRecyclerView} connected with this adapter should select departure (pass true) date
|
||||
* or arrival (pass false).
|
||||
* @return position of the cell to scroll to at the calendar view opening.
|
||||
*/
|
||||
@Nullable
|
||||
public Integer getPositionToScroll(final boolean departure) {
|
||||
if (departure && startSelectionPosition != null) {
|
||||
return CalendarUtils.findPositionOfSelectedMonth(calendarItems, startSelectionPosition);
|
||||
}
|
||||
if (!departure && endSelectionPosition != null) {
|
||||
return CalendarUtils.findPositionOfSelectedMonth(calendarItems, endSelectionPosition);
|
||||
}
|
||||
if (!departure && startSelectionPosition != null) {
|
||||
return CalendarUtils.findPositionOfSelectedMonth(calendarItems, startSelectionPosition);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void notifySelectedDaysChanged() {
|
||||
if (startSelectionPosition == null && endSelectionPosition == null) {
|
||||
return;
|
||||
}
|
||||
if (startSelectionPosition == null) {
|
||||
notifyItemRangeChanged(endSelectionPosition, 1);
|
||||
return;
|
||||
}
|
||||
if (endSelectionPosition == null) {
|
||||
notifyItemRangeChanged(startSelectionPosition, 1);
|
||||
return;
|
||||
}
|
||||
notifyItemRangeChanged(startSelectionPosition, endSelectionPosition - startSelectionPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
|
||||
switch (viewType) {
|
||||
case HEADER_ITEM_TYPE:
|
||||
return createHeaderViewHolder(parent);
|
||||
case EMPTY_ITEM_TYPE:
|
||||
return createEmptyViewHolder(parent);
|
||||
case DAY_ITEM_TYPE:
|
||||
return createDayViewHolder(parent);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that creates Header ViewHolder with type of THeaderViewHolder.
|
||||
*
|
||||
* @param parent {@link ViewGroup} for inflating ViewHolder;
|
||||
* @return New THeaderViewHolder;
|
||||
*/
|
||||
protected abstract THeaderViewHolder createHeaderViewHolder(final ViewGroup parent);
|
||||
|
||||
/**
|
||||
* Method that creates Empty ViewHolder with type of TEmptyViewHolder.
|
||||
*
|
||||
* @param parent {@link ViewGroup} for inflating ViewHolder;
|
||||
* @return New TEmptyViewHolder;
|
||||
*/
|
||||
protected abstract TEmptyViewHolder createEmptyViewHolder(final ViewGroup parent);
|
||||
|
||||
/**
|
||||
* Method that creates Day ViewHolder with type of TDayViewHolder.
|
||||
*
|
||||
* @param parent {@link ViewGroup} for inflating ViewHolder;
|
||||
* @return New TDayViewHolder;
|
||||
*/
|
||||
protected abstract TDayViewHolder createDayViewHolder(final ViewGroup parent);
|
||||
|
||||
/**
|
||||
* Bind data to a Header ViewHolder.
|
||||
*
|
||||
* @param viewHolder ViewHolder for binding;
|
||||
* @param monthName Name of month;
|
||||
* @param firstMonth True if bind called for the first month in calendar.
|
||||
*/
|
||||
protected abstract void bindHeaderItem(@NonNull final THeaderViewHolder viewHolder, @NonNull final String monthName, final boolean firstMonth);
|
||||
|
||||
/**
|
||||
* Bind data to an Empty ViewHolder.
|
||||
*
|
||||
* @param viewHolder ViewHolder for binding;
|
||||
* @param selectionMode Either {@link SelectionMode#SELECTED_MIDDLE} or {@link SelectionMode#NOT_SELECTED} can be here.
|
||||
*/
|
||||
protected abstract void bindEmptyItem(@NonNull final TEmptyViewHolder viewHolder, @NonNull final SelectionMode selectionMode);
|
||||
|
||||
/**
|
||||
* Bind data to a Day ViewHolder.
|
||||
*
|
||||
* @param viewHolder ViewHolder for binding;
|
||||
* @param day Text with number of a day. Eg "1" or "29";
|
||||
* @param date Date of current day;
|
||||
* @param selectionMode Selection mode for this item;
|
||||
* @param dateState Shows calendar date state for this item.
|
||||
*/
|
||||
protected abstract void bindDayItem(@NonNull final TDayViewHolder viewHolder,
|
||||
@NonNull final String day,
|
||||
@NonNull final DateTime date,
|
||||
@NonNull final SelectionMode selectionMode,
|
||||
@NonNull final ComparingToToday dateState);
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
|
||||
final CalendarItem calendarItem = CalendarUtils.findItemByPosition(calendarItems, position);
|
||||
|
||||
if (calendarItem instanceof CalendarHeaderItem) {
|
||||
final StaggeredGridLayoutManager.LayoutParams layoutParams =
|
||||
new StaggeredGridLayoutManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
layoutParams.setFullSpan(true);
|
||||
holder.itemView.setLayoutParams(layoutParams);
|
||||
final String monthName = monthsNames != null ? monthsNames[((CalendarHeaderItem) calendarItem).getMonth()]
|
||||
: String.valueOf(((CalendarHeaderItem) calendarItem).getMonth());
|
||||
bindHeaderItem((THeaderViewHolder) holder, monthName, position == 0);
|
||||
} else if (calendarItem instanceof CalendarEmptyItem) {
|
||||
if (startSelectionPosition != null && endSelectionPosition != null
|
||||
&& position >= startSelectionPosition && position <= endSelectionPosition) {
|
||||
bindEmptyItem((TEmptyViewHolder) holder, SelectionMode.SELECTED_MIDDLE);
|
||||
} else {
|
||||
bindEmptyItem((TEmptyViewHolder) holder, SelectionMode.NOT_SELECTED);
|
||||
}
|
||||
} else if (calendarItem instanceof CalendarDayItem) {
|
||||
bindDay((TDayViewHolder) holder, position, calendarItem);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO fix suppress
|
||||
@SuppressWarnings("PMD.CyclomaticComplexity")
|
||||
private void bindDay(final TDayViewHolder holder, final int position, final CalendarItem calendarItem) {
|
||||
final String currentDay = String.valueOf(((CalendarDayItem) calendarItem).getPositionOfFirstDay()
|
||||
+ position - calendarItem.getStartRange());
|
||||
final DateTime currentDate = new DateTime(((CalendarDayItem) calendarItem).getDateOfFirstDay()
|
||||
+ (position - calendarItem.getStartRange()) * ONE_DAY_LENGTH);
|
||||
final ComparingToToday dateState = ((CalendarDayItem) calendarItem).getComparingToToday();
|
||||
if (startSelectionPosition != null && position == startSelectionPosition) {
|
||||
if (endSelectionPosition == null || endSelectionPosition.equals(startSelectionPosition)) {
|
||||
bindDayItem(holder, currentDay, currentDate, SelectionMode.SELECTED_ONE_ONLY, dateState);
|
||||
return;
|
||||
}
|
||||
bindDayItem(holder, currentDay, currentDate, SelectionMode.SELECTED_FIRST, dateState);
|
||||
return;
|
||||
}
|
||||
if (endSelectionPosition != null && position == endSelectionPosition) {
|
||||
bindDayItem(holder, currentDay, currentDate, SelectionMode.SELECTED_LAST, dateState);
|
||||
return;
|
||||
}
|
||||
if (startSelectionPosition != null && endSelectionPosition != null
|
||||
&& position >= startSelectionPosition && position <= endSelectionPosition) {
|
||||
bindDayItem(holder, currentDay, currentDate, SelectionMode.SELECTED_MIDDLE, dateState);
|
||||
return;
|
||||
}
|
||||
|
||||
bindDayItem(holder, currentDay, currentDate, SelectionMode.NOT_SELECTED, dateState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(final int position) {
|
||||
final CalendarItem calendarItem = CalendarUtils.findItemByPosition(calendarItems, position);
|
||||
|
||||
if (calendarItem instanceof CalendarHeaderItem) {
|
||||
return HEADER_ITEM_TYPE;
|
||||
} else if (calendarItem instanceof CalendarEmptyItem) {
|
||||
return EMPTY_ITEM_TYPE;
|
||||
} else if (calendarItem instanceof CalendarDayItem) {
|
||||
return DAY_ITEM_TYPE;
|
||||
}
|
||||
|
||||
return super.getItemViewType(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return calendarItems.isEmpty() ? 0 : calendarItems.get(calendarItems.size() - 1).getEndRange();
|
||||
}
|
||||
|
||||
/**
|
||||
* Selection mode that shows the type of selection of a calendar cell.
|
||||
*/
|
||||
public enum SelectionMode {
|
||||
|
||||
/**
|
||||
* Selection mode for the case when first date in the calendar range selected
|
||||
* (not first and last simultaneously; for this purpose see {@link #SELECTED_ONE_ONLY}).
|
||||
*/
|
||||
SELECTED_FIRST,
|
||||
/**
|
||||
* Selection mode for the case when date in a middle of the calendar range selected.
|
||||
*/
|
||||
SELECTED_MIDDLE,
|
||||
/**
|
||||
* Selection mode for the case when last date in the calendar range selected
|
||||
* (not last and first simultaneously; for this purpose see {@link #SELECTED_ONE_ONLY}).
|
||||
*/
|
||||
SELECTED_LAST,
|
||||
/**
|
||||
* Selection mode for the case when only one date selected.
|
||||
*/
|
||||
SELECTED_ONE_ONLY,
|
||||
/**
|
||||
* Selection mode for the case when nothing selected.
|
||||
*/
|
||||
NOT_SELECTED
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright (c) 2016 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
|
||||
*
|
||||
* This file is part of RoboSwag library.
|
||||
*
|
||||
* 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 ru.touchin.templates.calendar;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* Created by Ilia Kurtov on 17/03/2016.
|
||||
* Calendar header item for showing headers for months in calendar.
|
||||
*/
|
||||
public class CalendarDayItem implements CalendarItem {
|
||||
|
||||
private final long dateOfFirstDay;
|
||||
private final int positionOfFirstDate;
|
||||
private final int startRange;
|
||||
private final int endRange;
|
||||
@NonNull
|
||||
private final ComparingToToday comparingToToday;
|
||||
|
||||
public CalendarDayItem(final long dateOfFirstDay,
|
||||
final int positionOfFirstDate,
|
||||
final int startRange,
|
||||
final int endRange,
|
||||
@NonNull final ComparingToToday comparingToToday) {
|
||||
this.dateOfFirstDay = dateOfFirstDay;
|
||||
this.positionOfFirstDate = positionOfFirstDate;
|
||||
this.startRange = startRange;
|
||||
this.endRange = endRange;
|
||||
this.comparingToToday = comparingToToday;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns date of the first date in millis in this calendar range.
|
||||
*
|
||||
* @return Date of first date in this item in millis.
|
||||
*/
|
||||
public long getDateOfFirstDay() {
|
||||
return dateOfFirstDay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns position of calendar cell for the first date.
|
||||
*
|
||||
* @return Position of calendar cell for the first date.
|
||||
*/
|
||||
public int getPositionOfFirstDay() {
|
||||
return positionOfFirstDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStartRange() {
|
||||
return startRange;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEndRange() {
|
||||
return endRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns comparison of current item to today.
|
||||
*
|
||||
* @return comparison of current item to today.
|
||||
*/
|
||||
@NonNull
|
||||
public ComparingToToday getComparingToToday() {
|
||||
return comparingToToday;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 2016 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
|
||||
*
|
||||
* This file is part of RoboSwag library.
|
||||
*
|
||||
* 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 ru.touchin.templates.calendar;
|
||||
|
||||
/**
|
||||
* Created by Ilia Kurtov on 17/03/2016.
|
||||
* Calendar item for showing empty cells in calendar.
|
||||
*/
|
||||
public class CalendarEmptyItem implements CalendarItem {
|
||||
|
||||
private final int startRange;
|
||||
private final int endRange;
|
||||
|
||||
public CalendarEmptyItem(final int startRange, final int endRange) {
|
||||
this.startRange = startRange;
|
||||
this.endRange = endRange;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStartRange() {
|
||||
return startRange;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEndRange() {
|
||||
return endRange;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (c) 2016 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
|
||||
*
|
||||
* This file is part of RoboSwag library.
|
||||
*
|
||||
* 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 ru.touchin.templates.calendar;
|
||||
|
||||
/**
|
||||
* Created by Ilia Kurtov on 17/03/2016.
|
||||
* Calendar header item for showing headers for months in calendar.
|
||||
*/
|
||||
public class CalendarHeaderItem implements CalendarItem {
|
||||
|
||||
private final int month;
|
||||
private final int startRange;
|
||||
private final int endRange;
|
||||
|
||||
public CalendarHeaderItem(final int month, final int startRange, final int endRange) {
|
||||
this.month = month;
|
||||
this.startRange = startRange;
|
||||
this.endRange = endRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns number of month (where 0 is January and 11 is December).
|
||||
*
|
||||
* @return Number of month (where 0 is January and 11 is December).
|
||||
*/
|
||||
public int getMonth() {
|
||||
return month;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStartRange() {
|
||||
return startRange;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEndRange() {
|
||||
return endRange;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (c) 2016 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
|
||||
*
|
||||
* This file is part of RoboSwag library.
|
||||
*
|
||||
* 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 ru.touchin.templates.calendar;
|
||||
|
||||
/**
|
||||
* Created by Ilia Kurtov on 17/03/2016.
|
||||
* Interface for items for {@link CalendarAdapter}. Instead of storing data about all calendar cells separately,
|
||||
* it sores list of this items. CalendarItem represents range with the same calendar items.
|
||||
*/
|
||||
public interface CalendarItem {
|
||||
|
||||
/**
|
||||
* Returns number of starting cell of this range.
|
||||
*
|
||||
* @return number of starting cell of this range.
|
||||
*/
|
||||
int getStartRange();
|
||||
|
||||
/**
|
||||
* Returns number of ending cell of this range.
|
||||
*
|
||||
* @return number of ending cell of this range.
|
||||
*/
|
||||
int getEndRange();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (c) 2016 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
|
||||
*
|
||||
* This file is part of RoboSwag library.
|
||||
*
|
||||
* 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 ru.touchin.templates.calendar;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import ru.touchin.roboswag.core.log.Lc;
|
||||
|
||||
/**
|
||||
* Created by Ilia Kurtov on 17/03/2016.
|
||||
* Specific {@link RecyclerView} that works with {@link CalendarAdapter}. It optimizes speed of the calendar.
|
||||
*/
|
||||
public class CalendarRecyclerView extends RecyclerView {
|
||||
|
||||
private static final int HEADER_MAX_ELEMENTS_IN_A_ROW = 1;
|
||||
private static final int EMPTY_MAX_ELEMENTS_IN_A_ROW = 6;
|
||||
private static final int DAY_MAX_ELEMENTS_IN_A_ROW = 7;
|
||||
|
||||
public CalendarRecyclerView(@NonNull final Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public CalendarRecyclerView(@NonNull final Context context, @Nullable final AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public CalendarRecyclerView(@NonNull final Context context, @Nullable final AttributeSet attrs, final int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
getRecycledViewPool().setMaxRecycledViews(CalendarAdapter.HEADER_ITEM_TYPE, HEADER_MAX_ELEMENTS_IN_A_ROW * 3);
|
||||
getRecycledViewPool().setMaxRecycledViews(CalendarAdapter.EMPTY_ITEM_TYPE, EMPTY_MAX_ELEMENTS_IN_A_ROW * 3);
|
||||
getRecycledViewPool().setMaxRecycledViews(CalendarAdapter.DAY_ITEM_TYPE, DAY_MAX_ELEMENTS_IN_A_ROW * 3);
|
||||
setItemViewCacheSize(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to set adapter that extends from {@link CalendarAdapter}.
|
||||
*
|
||||
* @param calendarAdapter Adapter that extends from {@link CalendarAdapter}.
|
||||
*/
|
||||
// This suppress needed for using only specific CalendarAdapter}
|
||||
@SuppressWarnings("PMD.UselessOverridingMethod")
|
||||
public void setAdapter(@NonNull final CalendarAdapter calendarAdapter) {
|
||||
super.setAdapter(calendarAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void setAdapter(final Adapter adapter) {
|
||||
Lc.assertion("Unsupported adapter class. Use CalendarAdapter instead.");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,272 @@
|
|||
/*
|
||||
* Copyright (c) 2016 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
|
||||
*
|
||||
* This file is part of RoboSwag library.
|
||||
*
|
||||
* 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 ru.touchin.templates.calendar;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeFieldType;
|
||||
import org.joda.time.Days;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import ru.touchin.roboswag.core.log.Lc;
|
||||
|
||||
/**
|
||||
* Created by Ilia Kurtov on 17/03/2016.
|
||||
* Utility class to simplify working with {@link CalendarAdapter}.
|
||||
*/
|
||||
public final class CalendarUtils {
|
||||
|
||||
private static final int DAYS_IN_WEEK = 7;
|
||||
private static final long ONE_DAY = TimeUnit.DAYS.toMillis(1);
|
||||
|
||||
/**
|
||||
* Method finds CalendarItem for specified position. Find process is optimized and use binary search algorithm.
|
||||
*
|
||||
* @param calendarItems List of {@link CalendarItem} where need to find specific element;
|
||||
* @param position Position of adapter;
|
||||
* @return CalendarItem for specified position.
|
||||
*/
|
||||
@Nullable
|
||||
public static CalendarItem findItemByPosition(@NonNull final List<CalendarItem> calendarItems, final long position) {
|
||||
return find(calendarItems, position, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method finds position of Header that respond to requested position.
|
||||
*
|
||||
* @param calendarItems List of {@link CalendarItem} where need to find specific element;
|
||||
* @param position Position of adapter;
|
||||
* @return Position of Header that respond to requested position.
|
||||
* Returns null if Header or related CalendarItem was not found for specified position.
|
||||
*/
|
||||
@Nullable
|
||||
public static Integer findPositionOfSelectedMonth(@NonNull final List<CalendarItem> calendarItems, final long position) {
|
||||
final CalendarItem calendarItem = find(calendarItems, position, true);
|
||||
if (calendarItem != null) {
|
||||
return calendarItem.getStartRange();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method finds position of calendar cell that respond to specified date.
|
||||
*
|
||||
* @param calendarItems List of {@link CalendarItem} where need to find specific element;
|
||||
* @param date Requested date in milliseconds.
|
||||
* @return Position of Calendar cell that that has specific date.
|
||||
* Returns null if CalendarItem was not found for specified position.
|
||||
*/
|
||||
@Nullable
|
||||
public static Integer findPositionByDate(@NonNull final List<CalendarItem> calendarItems, final long date) {
|
||||
int low = 0;
|
||||
int high = calendarItems.size() - 1;
|
||||
int addition = 0;
|
||||
float count = 0;
|
||||
while (true) {
|
||||
final int mid = (low + high) / 2 + addition;
|
||||
if (calendarItems.get(mid) instanceof CalendarDayItem) {
|
||||
if (date < ((CalendarDayItem) calendarItems.get(mid)).getDateOfFirstDay()) {
|
||||
if (mid == 0) {
|
||||
Lc.assertion("Selected date smaller then min date in calendar");
|
||||
return null;
|
||||
}
|
||||
high = mid - 1;
|
||||
} else {
|
||||
final long endDate = ((CalendarDayItem) calendarItems.get(mid)).getDateOfFirstDay()
|
||||
+ (calendarItems.get(mid).getEndRange() - calendarItems.get(mid).getStartRange()) * ONE_DAY;
|
||||
if (date > endDate) {
|
||||
if (mid == calendarItems.size()) {
|
||||
Lc.assertion("Selected date bigger then max date in calendar");
|
||||
return null;
|
||||
}
|
||||
low = mid + 1;
|
||||
} else {
|
||||
return (int) (calendarItems.get(mid).getStartRange()
|
||||
+ (date - (((CalendarDayItem) calendarItems.get(mid)).getDateOfFirstDay())) / ONE_DAY);
|
||||
}
|
||||
}
|
||||
count = 0;
|
||||
addition = 0;
|
||||
} else {
|
||||
count++;
|
||||
addition = ((int) Math.ceil(count / 2)) * ((int) (StrictMath.pow(-1, (count - 1))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create list of {@link CalendarItem} according to start and end Dates.
|
||||
*
|
||||
* @param startDate Start date of the range;
|
||||
* @param endDate End date of the range;
|
||||
* @return List of CalendarItems that could be one of these: {@link CalendarHeaderItem}, {@link CalendarDayItem} or {@link CalendarEmptyItem}.
|
||||
*/
|
||||
@NonNull
|
||||
@SuppressWarnings("checkstyle:MethodLength")
|
||||
public static List<CalendarItem> fillRanges(@NonNull final DateTime startDate, @NonNull final DateTime endDate) {
|
||||
final DateTime cleanStartDate = startDate.withTimeAtStartOfDay();
|
||||
final DateTime cleanEndDate = endDate.plusDays(1).withTimeAtStartOfDay();
|
||||
|
||||
DateTime tempTime = cleanStartDate;
|
||||
|
||||
final List<CalendarItem> calendarItems = fillCalendarTillCurrentDate(cleanStartDate, tempTime);
|
||||
|
||||
tempTime = tempTime.plusDays(Days.ONE.getDays());
|
||||
|
||||
final int totalDaysCount = Days.daysBetween(tempTime, cleanEndDate).getDays();
|
||||
int shift = calendarItems.get(calendarItems.size() - 1).getEndRange();
|
||||
int firstDate = tempTime.getDayOfMonth() - 1;
|
||||
int daysEnded = 1;
|
||||
|
||||
while (true) {
|
||||
final int daysInCurrentMonth = tempTime.dayOfMonth().getMaximumValue();
|
||||
final long firstRangeDate = tempTime.getMillis();
|
||||
|
||||
if ((daysEnded + (daysInCurrentMonth - firstDate)) <= totalDaysCount) {
|
||||
tempTime = tempTime.plusMonths(1).withDayOfMonth(1);
|
||||
|
||||
calendarItems.add(new CalendarDayItem(firstRangeDate, firstDate + 1, shift + daysEnded,
|
||||
shift + daysEnded + (daysInCurrentMonth - firstDate) - 1, ComparingToToday.AFTER_TODAY));
|
||||
daysEnded += daysInCurrentMonth - firstDate;
|
||||
if (daysEnded == totalDaysCount) {
|
||||
break;
|
||||
}
|
||||
firstDate = 0;
|
||||
|
||||
final int firstDayInWeek = tempTime.getDayOfWeek() - 1;
|
||||
|
||||
if (firstDayInWeek != 0) {
|
||||
calendarItems.add(new CalendarEmptyItem(shift + daysEnded, shift + daysEnded + (DAYS_IN_WEEK - firstDayInWeek - 1)));
|
||||
shift += (DAYS_IN_WEEK - firstDayInWeek);
|
||||
}
|
||||
|
||||
calendarItems.add(new CalendarHeaderItem(tempTime.getMonthOfYear() - 1, shift + daysEnded, shift + daysEnded));
|
||||
shift += 1;
|
||||
|
||||
if (firstDayInWeek != 0) {
|
||||
calendarItems.add(new CalendarEmptyItem(shift + daysEnded, shift + daysEnded + firstDayInWeek - 1));
|
||||
shift += firstDayInWeek;
|
||||
}
|
||||
|
||||
} else {
|
||||
calendarItems.add(new CalendarDayItem(firstRangeDate, firstDate + 1, shift + daysEnded, shift + totalDaysCount,
|
||||
ComparingToToday.AFTER_TODAY));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return calendarItems;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.ModifiedCyclomaticComplexity", "PMD.StdCyclomaticComplexity"})
|
||||
private static CalendarItem find(@NonNull final List<CalendarItem> calendarItems, final long position, final boolean getHeaderPosition) {
|
||||
int low = 0;
|
||||
int high = calendarItems.size() - 1;
|
||||
while (true) {
|
||||
final int mid = (low + high) / 2;
|
||||
if (position < calendarItems.get(mid).getStartRange()) {
|
||||
if (mid == 0 || position > calendarItems.get(mid - 1).getEndRange()) {
|
||||
Lc.assertion("CalendarAdapter cannot find item with that position");
|
||||
return null;
|
||||
}
|
||||
high = mid - 1;
|
||||
} else if (position > calendarItems.get(mid).getEndRange()) {
|
||||
if (mid == calendarItems.size() || position < calendarItems.get(mid + 1).getStartRange()) {
|
||||
Lc.assertion("CalendarAdapter cannot find item with that position");
|
||||
return null;
|
||||
}
|
||||
low = mid + 1;
|
||||
} else {
|
||||
if (getHeaderPosition) {
|
||||
int calendarShift = mid;
|
||||
while (true) {
|
||||
calendarShift--;
|
||||
if (calendarShift == -1) {
|
||||
return null;
|
||||
}
|
||||
if (calendarItems.get(calendarShift) instanceof CalendarHeaderItem) {
|
||||
return calendarItems.get(calendarShift);
|
||||
}
|
||||
}
|
||||
}
|
||||
return calendarItems.get(mid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static List<CalendarItem> fillCalendarTillCurrentDate(@NonNull final DateTime cleanStartDate, @NonNull final DateTime startDate) {
|
||||
DateTime temp = startDate;
|
||||
final List<CalendarItem> calendarItems = new ArrayList<>();
|
||||
int shift = 0;
|
||||
final int firstDate = temp.getDayOfMonth() - 1; //?? - 1 ?
|
||||
|
||||
// add first month header
|
||||
calendarItems.add(new CalendarHeaderItem(temp.get(DateTimeFieldType.monthOfYear()) - 1, shift, shift)); // is Month starts from 1 or 0 ?
|
||||
temp = temp.withDayOfMonth(1);
|
||||
shift += 1;
|
||||
|
||||
final int firstDayInTheWeek = temp.getDayOfWeek() - 1;
|
||||
|
||||
// check if first day is Monday. If not - add empty items. Otherwise do nothing
|
||||
if (firstDayInTheWeek != 0) {
|
||||
calendarItems.add(new CalendarEmptyItem(shift, shift + firstDayInTheWeek - 1));
|
||||
}
|
||||
shift += firstDayInTheWeek;
|
||||
|
||||
// add range with days before today
|
||||
calendarItems.add(new CalendarDayItem(temp.getMillis(), 1, shift, shift + firstDate - 1, ComparingToToday.BEFORE_TODAY));
|
||||
shift += firstDate;
|
||||
|
||||
// add today item
|
||||
temp = cleanStartDate;
|
||||
calendarItems.add(new CalendarDayItem(temp.getMillis(), firstDate + 1, shift, shift, ComparingToToday.TODAY));
|
||||
|
||||
//add empty items and header if current day the last day in the month
|
||||
if (temp.getDayOfMonth() == temp.dayOfMonth().getMaximumValue()) {
|
||||
addItemsIfCurrentDayTheLastDayInTheMonth(startDate, calendarItems);
|
||||
}
|
||||
|
||||
return calendarItems;
|
||||
}
|
||||
|
||||
private static void addItemsIfCurrentDayTheLastDayInTheMonth(@NonNull final DateTime dateTime,
|
||||
@NonNull final List<CalendarItem> calendarItems) {
|
||||
|
||||
int shift = calendarItems.get(calendarItems.size() - 1).getEndRange();
|
||||
final DateTime nextMonthFirstDay = dateTime.plusDays(1);
|
||||
final int firstFayInNextMonth = nextMonthFirstDay.getDayOfWeek() - 1;
|
||||
calendarItems.add(new CalendarEmptyItem(shift, shift + (7 - firstFayInNextMonth) - 1));
|
||||
shift += 7 - firstFayInNextMonth;
|
||||
calendarItems.add(new CalendarHeaderItem(nextMonthFirstDay.getMonthOfYear() + 1, shift, shift));
|
||||
shift += 1;
|
||||
calendarItems.add(new CalendarEmptyItem(shift, shift + firstFayInNextMonth - 1));
|
||||
}
|
||||
|
||||
private CalendarUtils() {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (c) 2016 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
|
||||
*
|
||||
* This file is part of RoboSwag library.
|
||||
*
|
||||
* 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 ru.touchin.templates.calendar;
|
||||
|
||||
/**
|
||||
* Created by Ilia Kurtov on 18/03/2016.
|
||||
* Show the comparison between a date and today.
|
||||
*/
|
||||
public enum ComparingToToday {
|
||||
|
||||
BEFORE_TODAY,
|
||||
TODAY,
|
||||
AFTER_TODAY
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue