Untitled diff
11 removals
Words removed | 19 |
Total words | 1837 |
Words removed (%) | 1.03 |
519 lines
9 additions
Words added | 12 |
Total words | 1830 |
Words added (%) | 0.66 |
518 lines
import android.content.Context;
import android.content.Context;
import android.content.res.TypedArray;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.RectF;
import android.graphics.Region;
import android.graphics.Region;
import android.graphics.Typeface;
import android.graphics.Typeface;
import android.os.Build;
import android.os.Build;
import android.support.annotation.Nullable;
import android.support.annotation.Nullable;
import android.support.v4.view.GestureDetectorCompat;
import android.support.v4.view.GestureDetectorCompat;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.animation.FastOutLinearInInterpolator;
import android.support.v4.view.animation.FastOutLinearInInterpolator;
import android.text.Layout;
import android.text.Layout;
import android.text.SpannableStringBuilder;
import android.text.SpannableStringBuilder;
import android.text.StaticLayout;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.text.format.DateFormat;
import android.text.style.StyleSpan;
import android.text.style.StyleSpan;
import android.util.AttributeSet;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.util.TypedValue;
import android.view.GestureDetector;
import android.view.GestureDetector;
import android.view.HapticFeedbackConstants;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.ScaleGestureDetector;
import android.view.SoundEffectConstants;
import android.view.SoundEffectConstants;
import android.view.View;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewConfiguration;
import android.widget.OverScroller;
import android.widget.OverScroller;
import com.alamkanak.weekview.DateTimeInterpreter;
import com.alamkanak.weekview.MonthLoader;
import com.alamkanak.weekview.WeekViewEvent;
import com.alamkanak.weekview.WeekViewLoader;
import java.text.SimpleDateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Calendar;
import java.util.Collections;
import java.util.Collections;
import java.util.Comparator;
import java.util.Comparator;
import java.util.List;
import java.util.List;
import java.util.Locale;
import java.util.Locale;
/**
/**
* Created by Raquib-ul-Alam Kanak on 7/21/2014.
* Created by Raquib-ul-Alam Kanak on 7/21/2014.
* Website: http://alamkanak.github.io/
* Website: http://alamkanak.github.io/
*/
*/
public class WeekView extends View {
public class WeekView extends View {
private enum Direction {
private enum Direction {
NONE, LEFT, RIGHT, VERTICAL
NONE, LEFT, RIGHT, VERTICAL
}
}
@Deprecated
@Deprecated
public static final int LENGTH_SHORT = 1;
public static final int LENGTH_SHORT = 1;
@Deprecated
@Deprecated
public static final int LENGTH_LONG = 2;
public static final int LENGTH_LONG = 2;
private final Context mContext;
private final Context mContext;
private Paint mTimeTextPaint;
private Paint mTimeTextPaint;
private float mTimeTextWidth;
private float mTimeTextWidth;
private float mTimeTextHeight;
private float mTimeTextHeight;
private Paint mHeaderTextPaint;
private Paint mHeaderTextPaint;
private float mHeaderTextHeight;
private float mHeaderTextHeight;
private GestureDetectorCompat mGestureDetector;
private GestureDetectorCompat mGestureDetector;
private OverScroller mScroller;
private OverScroller mScroller;
private PointF mCurrentOrigin = new PointF(0f, 0f);
private PointF mCurrentOrigin = new PointF(0f, 0f);
private Direction mCurrentScrollDirection = Direction.NONE;
private Direction mCurrentScrollDirection = Direction.NONE;
private Paint mHeaderBackgroundPaint;
private Paint mHeaderBackgroundPaint;
private float mWidthPerDay;
private float mWidthPerDay;
private Paint mDayBackgroundPaint;
private Paint mDayBackgroundPaint;
private Paint mHourSeparatorPaint;
private Paint mHourSeparatorPaint;
private float mHeaderMarginBottom;
private float mHeaderMarginBottom;
private Paint mTodayBackgroundPaint;
private Paint mTodayBackgroundPaint;
private Paint mFutureBackgroundPaint;
private Paint mFutureBackgroundPaint;
private Paint mPastBackgroundPaint;
private Paint mPastBackgroundPaint;
private Paint mFutureWeekendBackgroundPaint;
private Paint mFutureWeekendBackgroundPaint;
private Paint mPastWeekendBackgroundPaint;
private Paint mPastWeekendBackgroundPaint;
private Paint mNowLinePaint;
private Paint mNowLinePaint;
private Paint mTodayHeaderTextPaint;
private Paint mTodayHeaderTextPaint;
private Paint mEventBackgroundPaint;
private Paint mEventBackgroundPaint;
private float mHeaderColumnWidth;
private float mHeaderColumnWidth;
private List<EventRect> mEventRects;
private List<EventRect> mEventRects;
private List<? extends WeekViewEvent> mPreviousPeriodEvents;
private List<? extends WeekViewEvent> mPreviousPeriodEvents;
private List<? extends WeekViewEvent> mCurrentPeriodEvents;
private List<? extends WeekViewEvent> mCurrentPeriodEvents;
private List<? extends WeekViewEvent> mNextPeriodEvents;
private List<? extends WeekViewEvent> mNextPeriodEvents;
private TextPaint mEventTextPaint;
private TextPaint mEventTextPaint;
private Paint mHeaderColumnBackgroundPaint;
private Paint mHeaderColumnBackgroundPaint;
private int mFetchedPeriod = -1; // the middle period the calendar has fetched.
private int mFetchedPeriod = -1; // the middle period the calendar has fetched.
private boolean mRefreshEvents = false;
private boolean mRefreshEvents = false;
private Direction mCurrentFlingDirection = Direction.NONE;
private Direction mCurrentFlingDirection = Direction.NONE;
private ScaleGestureDetector mScaleDetector;
private ScaleGestureDetector mScaleDetector;
private boolean mIsZooming;
private boolean mIsZooming;
private Calendar mFirstVisibleDay;
private Calendar mFirstVisibleDay;
private Calendar mLastVisibleDay;
private Calendar mLastVisibleDay;
private int mDefaultEventColor;
private int mDefaultEventColor;
private int mMinimumFlingVelocity = 0;
private int mMinimumFlingVelocity = 0;
private int mScaledTouchSlop = 0;
private int mScaledTouchSlop = 0;
// Attributes and their default values.
// Attributes and their default values.
private int mHourHeight = 50;
private int mHourHeight = 50;
private int mNewHourHeight = -1;
private int mNewHourHeight = -1;
private int mMinHourHeight = 0; //no minimum specified (will be dynamic, based on screen)
private int mMinHourHeight = 0; //no minimum specified (will be dynamic, based on screen)
private int mEffectiveMinHourHeight = mMinHourHeight; //compensates for the fact that you can't keep zooming out.
private int mEffectiveMinHourHeight = mMinHourHeight; //compensates for the fact that you can't keep zooming out.
private int mMaxHourHeight = 250;
private int mMaxHourHeight = 250;
private int mColumnGap = 10;
private int mColumnGap = 10;
private int mFirstDayOfWeek = Calendar.MONDAY;
private int mFirstDayOfWeek = Calendar.MONDAY;
private int mTextSize = 12;
private int mTextSize = 12;
private int mHeaderColumnPadding = 10;
private int mHeaderColumnPadding = 10;
private int mHeaderColumnTextColor = Color.BLACK;
private int mHeaderColumnTextColor = Color.BLACK;
private int mNumberOfVisibleDays = 3;
private int mNumberOfVisibleDays = 3;
private int mHeaderRowPadding = 10;
private int mHeaderRowPadding = 10;
private int mHeaderRowBackgroundColor = Color.WHITE;
private int mHeaderRowBackgroundColor = Color.WHITE;
private int mDayBackgroundColor = Color.rgb(245, 245, 245);
private int mDayBackgroundColor = Color.rgb(245, 245, 245);
private int mPastBackgroundColor = Color.rgb(227, 227, 227);
private int mPastBackgroundColor = Color.rgb(227, 227, 227);
private int mFutureBackgroundColor = Color.rgb(245, 245, 245);
private int mFutureBackgroundColor = Color.rgb(245, 245, 245);
private int mPastWeekendBackgroundColor = 0;
private int mPastWeekendBackgroundColor = 0;
private int mFutureWeekendBackgroundColor = 0;
private int mFutureWeekendBackgroundColor = 0;
private int mNowLineColor = Color.rgb(102, 102, 102);
private int mNowLineColor = Color.rgb(102, 102, 102);
private int mNowLineThickness = 5;
private int mNowLineThickness = 5;
private int mHourSeparatorColor = Color.rgb(230, 230, 230);
private int mHourSeparatorColor = Color.rgb(230, 230, 230);
private int mTodayBackgroundColor = Color.rgb(239, 247, 254);
private int mTodayBackgroundColor = Color.rgb(239, 247, 254);
private int mHourSeparatorHeight = 2;
private int mHourSeparatorHeight = 2;
private int mTodayHeaderTextColor = Color.rgb(39, 137, 228);
private int mTodayHeaderTextColor = Color.rgb(39, 137, 228);
private int mEventTextSize = 12;
private int mEventTextSize = 12;
private int mEventTextColor = Color.BLACK;
private int mEventTextColor = Color.BLACK;
private int mEventPadding = 8;
private int mEventPadding = 8;
private int mHeaderColumnBackgroundColor = Color.WHITE;
private int mHeaderColumnBackgroundColor = Color.WHITE;
private boolean mIsFirstDraw = true;
private boolean mIsFirstDraw = true;
private boolean mAreDimensionsInvalid = true;
private boolean mAreDimensionsInvalid = true;
@Deprecated private int mDayNameLength = LENGTH_LONG;
@Deprecated private int mDayNameLength = LENGTH_LONG;
private int mOverlappingEventGap = 0;
private int mOverlappingEventGap = 0;
private int mEventMarginVertical = 0;
private int mEventMarginVertical = 0;
private float mXScrollingSpeed = 1f;
private float mXScrollingSpeed = 1f;
private Calendar mScrollToDay = null;
private Calendar mScrollToDay = null;
private double mScrollToHour = -1;
private double mScrollToHour = -1;
private int mEventCornerRadius = 0;
private int mEventCornerRadius = 0;
private boolean mShowDistinctWeekendColor = false;
private boolean mShowDistinctWeekendColor = false;
private boolean mShowNowLine = false;
private boolean mShowNowLine = false;
private boolean mShowDistinctPastFutureColor = false;
private boolean mShowDistinctPastFutureColor = false;
private boolean mHorizontalFlingEnabled = true;
private boolean mHorizontalFlingEnabled = true;
private boolean mVerticalFlingEnabled = true;
private boolean mVerticalFlingEnabled = true;
// Listeners.
// Listeners.
private EventClickListener mEventClickListener;
private EventClickListener mEventClickListener;
private EventLongPressListener mEventLongPressListener;
private EventLongPressListener mEventLongPressListener;
private WeekViewLoader mWeekViewLoader;
private WeekViewLoader mWeekViewLoader;
private EmptyViewClickListener mEmptyViewClickListener;
private EmptyViewClickListener mEmptyViewClickListener;
private EmptyViewLongPressListener mEmptyViewLongPressListener;
private EmptyViewLongPressListener mEmptyViewLongPressListener;
private DateTimeInterpreter mDateTimeInterpreter;
private DateTimeInterpreter mDateTimeInterpreter;
private ScrollListener mScrollListener;
private ScrollListener mScrollListener;
private final GestureDetector.SimpleOnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() {
private final GestureDetector.SimpleOnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() {
@Override
@Override
public boolean onDown(MotionEvent e) {
public boolean onDown(MotionEvent e) {
goToNearestOrigin();
goToNearestOrigin();
return true;
return true;
}
}
@Override
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
// Check if view is zoomed.
// Check if view is zoomed.
if (mIsZooming)
if (mIsZooming)
return true;
return true;
switch (mCurrentScrollDirection) {
switch (mCurrentScrollDirection) {
case NONE: {
case NONE: {
// Allow scrolling only in one direction.
// Allow scrolling only in one direction.
if (Math.abs(distanceX) > Math.abs(distanceY)) {
if (Math.abs(distanceX) > Math.abs(distanceY)) {
if (distanceX > 0) {
if (distanceX > 0) {
mCurrentScrollDirection = Direction.LEFT;
mCurrentScrollDirection = Direction.LEFT;
} else {
} else {
mCurrentScrollDirection = Direction.RIGHT;
mCurrentScrollDirection = Direction.RIGHT;
}
}
} else {
} else {
mCurrentScrollDirection = Direction.VERTICAL;
mCurrentScrollDirection = Direction.VERTICAL;
}
}
break;
break;
}
}
case LEFT: {
case LEFT: {
// Change direction if there was enough change.
// Change direction if there was enough change.
if (Math.abs(distanceX) > Math.abs(distanceY) && (distanceX < -mScaledTouchSlop)) {
if (Math.abs(distanceX) > Math.abs(distanceY) && (distanceX < -mScaledTouchSlop)) {
mCurrentScrollDirection = Direction.RIGHT;
mCurrentScrollDirection = Direction.RIGHT;
}
}
break;
break;
}
}
case RIGHT: {
case RIGHT: {
// Change direction if there was enough change.
// Change direction if there was enough change.
if (Math.abs(distanceX) > Math.abs(distanceY) && (distanceX > mScaledTouchSlop)) {
if (Math.abs(distanceX) > Math.abs(distanceY) && (distanceX > mScaledTouchSlop)) {
mCurrentScrollDirection = Direction.LEFT;
mCurrentScrollDirection = Direction.LEFT;
}
}
break;
break;
}
}
}
}
// Calculate the new origin after scroll.
// Calculate the new origin after scroll.
switch (mCurrentScrollDirection) {
switch (mCurrentScrollDirection) {
case LEFT:
case LEFT:
case RIGHT:
case RIGHT:
mCurrentOrigin.x -= distanceX * mXScrollingSpeed;
mCurrentOrigin.x -= distanceX * mXScrollingSpeed;
ViewCompat.postInvalidateOnAnimation(WeekView.this);
ViewCompat.postInvalidateOnAnimation(WeekView.this);
break;
break;
case VERTICAL:
case VERTICAL:
mCurrentOrigin.y -= distanceY;
mCurrentOrigin.y -= distanceY;
ViewCompat.postInvalidateOnAnimation(WeekView.this);
ViewCompat.postInvalidateOnAnimation(WeekView.this);
break;
break;
}
}
return true;
return true;
}
}
@Override
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (mIsZooming)
if (mIsZooming)
return true;
return true;
if ((mCurrentFlingDirection == Direction.LEFT && !mHorizontalFlingEnabled) ||
if ((mCurrentFlingDirection == Direction.LEFT && !mHorizontalFlingEnabled) ||
(mCurrentFlingDirection == Direction.RIGHT && !mHorizontalFlingEnabled) ||
(mCurrentFlingDirection == Direction.RIGHT && !mHorizontalFlingEnabled) ||
(mCurrentFlingDirection == Direction.VERTICAL && !mVerticalFlingEnabled)) {
(mCurrentFlingDirection == Direction.VERTICAL && !mVerticalFlingEnabled)) {
return true;
return true;
}
}
mScroller.forceFinished(true);
mScroller.forceFinished(true);
mCurrentFlingDirection = mCurrentScrollDirection;
mCurrentFlingDirection = mCurrentScrollDirection;
switch (mCurrentFlingDirection) {
switch (mCurrentFlingDirection) {
case LEFT:
case LEFT:
case RIGHT:
case RIGHT:
mScroller.fling((int) mCurrentOrigin.x, (int) mCurrentOrigin.y, (int) (velocityX * mXScrollingSpeed), 0, Integer.MIN_VALUE, Integer.MAX_VALUE, (int) -(mHourHeight * 24 + mHeaderTextHeight + mHeaderRowPadding * 2 + mHeaderMarginBottom + mTimeTextHeight / 2 - getHeight()), 0);
mScroller.fling((int) mCurrentOrigin.x, (int) mCurrentOrigin.y, (int) (velocityX * mXScrollingSpeed), 0, Integer.MIN_VALUE, Integer.MAX_VALUE, (int) -(mHourHeight * 24 + mHeaderTextHeight + mHeaderRowPadding * 2 + mHeaderMarginBottom + mTimeTextHeight / 2 - getHeight()), 0);
break;
break;
case VERTICAL:
case VERTICAL:
mScroller.fling((int) mCurrentOrigin.x, (int) mCurrentOrigin.y, 0, (int) velocityY, Integer.MIN_VALUE, Integer.MAX_VALUE, (int) -(mHourHeight * 24 + mHeaderTextHeight + mHeaderRowPadding * 2 + mHeaderMarginBottom + mTimeTextHeight/2 - getHeight()), 0);
mScroller.fling((int) mCurrentOrigin.x, (int) mCurrentOrigin.y, 0, (int) velocityY, Integer.MIN_VALUE, Integer.MAX_VALUE, (int) -(mHourHeight * 24 + mHeaderTextHeight + mHeaderRowPadding * 2 + mHeaderMarginBottom + mTimeTextHeight/2 - getHeight()), 0);
break;
break;
}
}
ViewCompat.postInvalidateOnAnimation(WeekView.this);
ViewCompat.postInvalidateOnAnimation(WeekView.this);
return true;
return true;
}
}
@Override
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
public boolean onSingleTapConfirmed(MotionEvent e) {
// If the tap was on an event then trigger the callback.
// If the tap was on an event then trigger the callback.
if (mEventRects != null && mEventClickListener != null) {
if (mEventRects != null && mEventClickListener != null) {
List<EventRect> reversedEventRects = mEventRects;
List<EventRect> reversedEventRects = mEventRects;
Collections.reverse(reversedEventRects);
Collections.reverse(reversedEventRects);
for (EventRect event : reversedEventRects) {
for (EventRect event : reversedEventRects) {
if (event.rectF != null && e.getX() > event.rectF.left && e.getX() < event.rectF.right && e.getY() > event.rectF.top && e.getY() < event.rectF.bottom) {
if (event.rectF != null && e.getX() > event.rectF.left && e.getX() < event.rectF.right && e.getY() > event.rectF.top && e.getY() < event.rectF.bottom) {
mEventClickListener.onEventClick(event.originalEvent, event.rectF);
mEventClickListener.onEventClick(event.originalEvent, event.rectF);
playSoundEffect(SoundEffectConstants.CLICK);
playSoundEffect(SoundEffectConstants.CLICK);
return super.onSingleTapConfirmed(e);
return super.onSingleTapConfirmed(e);
}
}
}
}
}
}
// If the tap was on in an empty space, then trigger the callback.
// If the tap was on in an empty space, then trigger the callback.
if (mEmptyViewClickListener != null && e.getX() > mHeaderColumnWidth && e.getY() > (mHeaderTextHeight + mHeaderRowPadding * 2 + mHeaderMarginBottom)) {
if (mEmptyViewClickListener != null && e.getX() > mHeaderColumnWidth && e.getY() > (mHeaderTextHeight + mHeaderRowPadding * 2 + mHeaderMarginBottom)) {
Calendar selectedTime = getTimeFromPoint(e.getX(), e.getY());
Calendar selectedTime = getTimeFromPoint(e.getX(), e.getY());
if (selectedTime != null) {
if (selectedTime != null) {
playSoundEffect(SoundEffectConstants.CLICK);
playSoundEffect(SoundEffectConstants.CLICK);
mEmptyViewClickListener.onEmptyViewClicked(selectedTime);
mEmptyViewClickListener.onEmptyViewClicked(selectedTime);
}
}
}
}
return super.onSingleTapConfirmed(e);
return super.onSingleTapConfirmed(e);
}
}
@Override
@Override
public void onLongPress(MotionEvent e) {
public void onLongPress(MotionEvent e) {
super.onLongPress(e);
super.onLongPress(e);
if (mEventLongPressListener != null && mEventRects != null) {
if (mEventLongPressListener != null && mEventRects != null) {
List<EventRect> reversedEventRects = mEventRects;
List<EventRect> reversedEventRects = mEventRects;
Collections.reverse(reversedEventRects);
Collections.reverse(reversedEventRects);
for (EventRect event : reversedEventRects) {
for (EventRect event : reversedEventRects) {
if (event.rectF != null && e.getX() > event.rectF.left && e.getX() < event.rectF.right && e.getY() > event.rectF.top && e.getY() < event.rectF.bottom) {
if (event.rectF != null && e.getX() > event.rectF.left && e.getX() < event.rectF.right && e.getY() > event.rectF.top && e.getY() < event.rectF.bottom) {
mEventLongPressListener.onEventLongPress(event.originalEvent, event.rectF);
mEventLongPressListener.onEventLongPress(event.originalEvent, event.rectF);
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
return;
return;
}
}
}
}
}
}
// If the tap was on in an empty space, then trigger the callback.
// If the tap was on in an empty space, then trigger the callback.
if (mEmptyViewLongPressListener != null && e.getX() > mHeaderColumnWidth && e.getY() > (mHeaderTextHeight + mHeaderRowPadding * 2 + mHeaderMarginBottom)) {
if (mEmptyViewLongPressListener != null && e.getX() > mHeaderColumnWidth && e.getY() > (mHeaderTextHeight + mHeaderRowPadding * 2 + mHeaderMarginBottom)) {
Calendar selectedTime = getTimeFromPoint(e.getX(), e.getY());
Calendar selectedTime = getTimeFromPoint(e.getX(), e.getY());
if (selectedTime != null) {
if (selectedTime != null) {
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
mEmptyViewLongPressListener.onEmptyViewLongPress(selectedTime);
mEmptyViewLongPressListener.onEmptyViewLongPress(selectedTime);
}
}
}
}
}
}
};
};
public WeekView(Context context) {
public WeekView(Context context) {
this(context, null);
this(context, null);
}
}
public WeekView(Context context, AttributeSet attrs) {
public WeekView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
this(context, attrs, 0);
}
}
public WeekView(Context context, AttributeSet attrs, int defStyleAttr) {
public WeekView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
super(context, attrs, defStyleAttr);
// Hold references.
// Hold references.
mContext = context;
mContext = context;
// Get the attribute values (if any).
// Get the attribute values (if any).
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.WeekView, 0, 0);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.WeekView, 0, 0);
try {
try {
mFirstDayOfWeek = a.getInteger(R.styleable.WeekView_firstDayOfWeek, mFirstDayOfWeek);
mFirstDayOfWeek = a.getInteger(R.styleable.WeekView_firstDayOfWeek, mFirstDayOfWeek);
mHourHeight = a.getDimensionPixelSize(R.styleable.WeekView_hourHeight, mHourHeight);
mHourHeight = a.getDimensionPixelSize(R.styleable.WeekView_hourHeight, mHourHeight);
mMinHourHeight = a.getDimensionPixelSize(R.styleable.WeekView_minHourHeight, mMinHourHeight);
mMinHourHeight = a.getDimensionPixelSize(R.styleable.WeekView_minHourHeight, mMinHourHeight);
mEffectiveMinHourHeight = mMinHourHeight;
mEffectiveMinHourHeight = mMinHourHeight;
mMaxHourHeight = a.getDimensionPixelSize(R.styleable.WeekView_maxHourHeight, mMaxHourHeight);
mMaxHourHeight = a.getDimensionPixelSize(R.styleable.WeekView_maxHourHeight, mMaxHourHeight);
mTextSize = a.getDimensionPixelSize(R.styleable.WeekView_textSize, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, mTextSize, context.getResources().getDisplayMetrics()));
mTextSize = a.getDimensionPixelSize(R.styleable.WeekView_textSize, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, mTextSize, context.getResources().getDisplayMetrics()));
mHeaderColumnPadding = a.getDimensionPixelSize(R.styleable.WeekView_headerColumnPadding, mHeaderColumnPadding);
mHeaderColumnPadding = a.getDimensionPixelSize(R.styleable.WeekView_headerColumnPadding, mHeaderColumnPadding);
mColumnGap = a.getDimensionPixelSize(R.styleable.WeekView_columnGap, mColumnGap);
mColumnGap = a.getDimensionPixelSize(R.styleable.WeekView_columnGap, mColumnGap);
mHeaderColumnTextColor = a.getColor(R.styleable.WeekView_headerColumnTextColor, mHeaderColumnTextColor);
mHeaderColumnTextColor = a.getColor(R.styleable.WeekView_headerColumnTextColor, mHeaderColumnTextColor);
mNumberOfVisibleDays = a.getInteger(R.styleable.WeekView_noOfVisibleDays, mNumberOfVisibleDays);
mNumberOfVisibleDays = a.getInteger(R.styleable.WeekView_noOfVisibleDays, mNumberOfVisibleDays);
mHeaderRowPadding = a.getDimensionPixelSize(R.styleable.WeekView_headerRowPadding, mHeaderRowPadding);
mHeaderRowPadding = a.getDimensionPixelSize(R.styleable.WeekView_headerRowPadding, mHeaderRowPadding);
mHeaderRowBackgroundColor = a.getColor(R.styleable.WeekView_headerRowBackgroundColor, mHeaderRowBackgroundColor);
mHeaderRowBackgroundColor = a.getColor(R.styleable.WeekView_headerRowBackgroundColor, mHeaderRowBackgroundColor);
mDayBackgroundColor = a.getColor(R.styleable.WeekView_dayBackgroundColor, mDayBackgroundColor);
mDayBackgroundColor = a.getColor(R.styleable.WeekView_dayBackgroundColor, mDayBackgroundColor);
mFutureBackgroundColor = a.getColor(R.styleable.WeekView_futureBackgroundColor, mFutureBackgroundColor);
mFutureBackgroundColor = a.getColor(R.styleable.WeekView_futureBackgroundColor, mFutureBackgroundColor);
mPastBackgroundColor = a.getColor(R.styleable.WeekView_pastBackgroundColor, mPastBackgroundColor);
mPastBackgroundColor = a.getColor(R.styleable.WeekView_pastBackgroundColor, mPastBackgroundColor);
mFutureWeekendBackgroundColor = a.getColor(R.styleable.WeekView_futureWeekendBackgroundColor, mFutureBackgroundColor); // If not set, use the same color as in the week
mFutureWeekendBackgroundColor = a.getColor(R.styleable.WeekView_futureWeekendBackgroundColor, mFutureBackgroundColor); // If not set, use the same color as in the week
mPastWeekendBackgroundColor = a.getColor(R.styleable.WeekView_pastWeekendBackgroundColor, mPastBackgroundColor);
mPastWeekendBackgroundColor = a.getColor(R.styleable.WeekView_pastWeekendBackgroundColor, mPastBackgroundColor);
mNowLineColor = a.getColor(R.styleable.WeekView_nowLineColor, mNowLineColor);
mNowLineColor = a.getColor(R.styleable.WeekView_nowLineColor, mNowLineColor);
mNowLineThickness = a.getDimensionPixelSize(R.styleable.WeekView_nowLineThickness, mNowLineThickness);
mNowLineThickness = a.getDimensionPixelSize(R.styleable.WeekView_nowLineThickness, mNowLineThickness);
mHourSeparatorColor = a.getColor(R.styleable.WeekView_hourSeparatorColor, mHourSeparatorColor);
mHourSeparatorColor = a.getColor(R.styleable.WeekView_hourSeparatorColor, mHourSeparatorColor);
mTodayBackgroundColor = a.getColor(R.styleable.WeekView_todayBackgroundColor, mTodayBackgroundColor);
mTodayBackgroundColor = a.getColor(R.styleable.WeekView_todayBackgroundColor, mTodayBackgroundColor);
mHourSeparatorHeight = a.getDimensionPixelSize(R.styleable.WeekView_hourSeparatorHeight, mHourSeparatorHeight);
mHourSeparatorHeight = a.getDimensionPixelSize(R.styleable.WeekView_hourSeparatorHeight, mHourSeparatorHeight);
mTodayHeaderTextColor = a.getColor(R.styleable.WeekView_todayHeaderTextColor, mTodayHeaderTextColor);
mTodayHeaderTextColor = a.getColor(R.styleable.WeekView_todayHeaderTextColor, mTodayHeaderTextColor);
mEventTextSize = a.getDimensionPixelSize(R.styleable.WeekView_eventTextSize, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, mEventTextSize, context.getResources().getDisplayMetrics()));
mEventTextSize = a.getDimensionPixelSize(R.styleable.WeekView_eventTextSize, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, mEventTextSize, context.getResources().getDisplayMetrics()));
mEventTextColor = a.getColor(R.styleable.WeekView_eventTextColor, mEventTextColor);
mEventTextColor = a.getColor(R.styleable.WeekView_eventTextColor, mEventTextColor);
mEventPadding = a.getDimensionPixelSize(R.styleable.WeekView_eventPadding, mEventPadding);
mEventPadding = a.getDimensionPixelSize(R.styleable.WeekView_eventPadding, mEventPadding);
mHeaderColumnBackgroundColor = a.getColor(R.styleable.WeekView_headerColumnBackground, mHeaderColumnBackgroundColor);
mHeaderColumnBackgroundColor = a.getColor(R.styleable.WeekView_headerColumnBackground, mHeaderColumnBackgroundColor);
mDayNameLength = a.getInteger(R.styleable.WeekView_dayNameLength, mDayNameLength);
mDayNameLength = a.getInteger(R.styleable.WeekView_dayNameLength, mDayNameLength);
mOverlappingEventGap = a.getDimensionPixelSize(R.styleable.WeekView_overlappingEventGap, mOverlappingEventGap);
mOverlappingEventGap = a.getDimensionPixelSize(R.styleable.WeekView_overlappingEventGap, mOverlappingEventGap);
mEventMarginVertical = a.getDimensionPixelSize(R.styleable.WeekView_eventMarginVertical, mEventMarginVertical);
mEventMarginVertical = a.getDimensionPixelSize(R.styleable.WeekView_eventMarginVertical, mEventMarginVertical);
mXScrollingSpeed = a.getFloat(R.styleable.WeekView_xScrollingSpeed, mXScrollingSpeed);
mXScrollingSpeed = a.getFloat(R.styleable.WeekView_xScrollingSpeed, mXScrollingSpeed);
mEventCornerRadius = a.getDimensionPixelSize(R.styleable.WeekView_eventCornerRadius, mEventCornerRadius);
mEventCornerRadius = a.getDimensionPixelSize(R.styleable.WeekView_eventCornerRadius, mEventCornerRadius);
mShowDistinctPastFutureColor = a.getBoolean(R.styleable.WeekView_showDistinctPastFutureColor, mShowDistinctPastFutureColor);
mShowDistinctPastFutureColor = a.getBoolean(R.styleable.WeekView_showDistinctPastFutureColor, mShowDistinctPastFutureColor);
mShowDistinctWeekendColor = a.getBoolean(R.styleable.WeekView_showDistinctWeekendColor, mShowDistinctWeekendColor);
mShowDistinctWeekendColor = a.getBoolean(R.styleable.WeekView_showDistinctWeekendColor, mShowDistinctWeekendColor);
mShowNowLine = a.getBoolean(R.styleable.WeekView_showNowLine, mShowNowLine);
mShowNowLine = a.getBoolean(R.styleable.WeekView_showNowLine, mShowNowLine);
mHorizontalFlingEnabled = a.getBoolean(R.styleable.WeekView_horizontalFlingEnabled, mHorizontalFlingEnabled);
mHorizontalFlingEnabled = a.getBoolean(R.styleable.WeekView_horizontalFlingEnabled, mHorizontalFlingEnabled);
mVerticalFlingEnabled = a.getBoolean(R.styleable.WeekView_verticalFlingEnabled, mVerticalFlingEnabled);
mVerticalFlingEnabled = a.getBoolean(R.styleable.WeekView_verticalFlingEnabled, mVerticalFlingEnabled);
} finally {
} finally {
a.recycle();
a.recycle();
}
}
init();
init();
}
}
private void init() {
private void init() {
// Scrolling initialization.
// Scrolling initialization.
mGestureDetector = new GestureDetectorCompat(mContext, mGestureListener);
mGestureDetector = new GestureDetectorCompat(mContext, mGestureListener);
mScroller = new OverScroller(mContext, new FastOutLinearInInterpolator());
mScroller = new OverScroller(mContext, new FastOutLinearInInterpolator());
mMinimumFlingVelocity = ViewConfiguration.get(mContext).getScaledMinimumFlingVelocity();
mMinimumFlingVelocity = ViewConfiguration.get(mContext).getScaledMinimumFlingVelocity();
mScaledTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
mScaledTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
// Measure settings for time column.
// Measure settings for time column.
mTimeTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTimeTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTimeTextPaint.setTextAlign(Paint.Align.RIGHT);
mTimeTextPaint.setTextAlign(Paint.Align.RIGHT);
mTimeTextPaint.setTextSize(mTextSize);
mTimeTextPaint.setTextSize(mTextSize);
mTimeTextPaint.setColor(mHeaderColumnTextColor);
mTimeTextPaint.setColor(mHeaderColumnTextColor);
Rect rect = new Rect();
Rect rect = new Rect();
mTimeTextPaint.getTextBounds("00 PM", 0, "00 PM".length(), rect);
mTimeTextPaint.getTextBounds("00 PM", 0, "00 PM".length(), rect);
mTimeTextHeight = rect.height();
mTimeTextHeight = rect.height();
mHeaderMarginBottom = mTimeTextHeight / 2;
mHeaderMarginBottom = mTimeTextHeight / 2;
initTextTimeWidth();
initTextTimeWidth();
// Measure settings for header row.
// Measure settings for header row.
mHeaderTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mHeaderTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mHeaderTextPaint.setColor(mHeaderColumnTextColor);
mHeaderTextPaint.setColor(mHeaderColumnTextColor);
mHeaderTextPaint.setTextAlign(Paint.Align.CENTER);
mHeaderTextPaint.setTextAlign(Paint.Align.CENTER);
mHeaderTextPaint.setTextSize(mTextSize);
mHeaderTextPaint.setTextSize(mTextSize);
mHeaderTextPaint.getTextBounds("00 PM", 0, "00 PM".length(), rect);
mHeaderTextPaint.getTextBounds("00 PM", 0, "00 PM".length(), rect);
mHeaderTextHeight = rect.height();
mHeaderTextHeight = rect.height();
mHeaderTextPaint.setTypeface(Typeface.DEFAULT_BOLD);
mHeaderTextPaint.setTypeface(Typeface.DEFAULT_BOLD);
// Prepare header background paint.
// Prepare header background paint.
mHeaderBackgroundPaint = new Paint();
mHeaderBackgroundPaint = new Paint();
mHeaderBackgroundPaint.setColor(mHeaderRowBackgroundColor);
mHeaderBackgroundPaint.setColor(mHeaderRowBackgroundColor);
// Prepare day background color paint.
// Prepare day background color paint.
mDayBackgroundPaint = new Paint();
mDayBackgroundPaint = new Paint();
mDayBackgroundPaint.setColor(mDayBackgroundColor);
mDayBackgroundPaint.setColor(mDayBackgroundColor);
mFutureBackgroundPaint = new Paint();
mFutureBackgroundPaint = new Paint();
mFutureBackgroundPaint.setColor(mFutureBackgroundColor);
mFutureBackgroundPaint.setColor(mFutureBackgroundColor);
mPastBackgroundPaint = new Paint();
mPastBackgroundPaint = new Paint();
mPastBackgroundPaint.setColor(mPastBackgroundColor);
mPastBackgroundPaint.setColor(mPastBackgroundColor);
mFutureWeekendBackgroundPaint = new Paint();
mFutureWeekendBackgroundPaint = new Paint();
mFutureWeekendBackgroundPaint.setColor(mFutureWeekendBackgroundColor);
mFutureWeekendBackgroundPaint.setColor(mFutureWeekendBackgroundColor);
mPastWeekendBackgroundPaint = new Paint();
mPastWeekendBackgroundPaint = new Paint();
mPastWeekendBackgroundPaint.setColor(mPastWeekendBackgroundColor);
mPastWeekendBackgroundPaint.setColor(mPastWeekendBackgroundColor);
// Prepare hour separator color paint.
// Prepare hour separator color paint.
mHourSeparatorPaint = new Paint();
mHourSeparatorPaint = new Paint();
mHourSeparatorPaint.setStyle(Paint.Style.STROKE);
mHourSeparatorPaint.setStyle(Paint.Style.STROKE);
mHourSeparatorPaint.setStrokeWidth(mHourSeparatorHeight);
mHourSeparatorPaint.setStrokeWidth(mHourSeparatorHeight);
mHourSeparatorPaint.setColor(mHourSeparatorColor);
mHourSeparatorPaint.setColor(mHourSeparatorColor);
// Prepare the "now" line color paint
// Prepare the "now" line color paint
mNowLinePaint = new Paint();
mNowLinePaint = new Paint();
mNowLinePaint.setStrokeWidth(mNowLineThickness);
mNowLinePaint.setStrokeWidth(mNowLineThickness);
mNowLinePaint.setColor(mNowLineColor);
mNowLinePaint.setColor(mNowLineColor);
// Prepare today background color paint.
// Prepare today background color paint.
mTodayBackgroundPaint = new Paint();
mTodayBackgroundPaint = new Paint();
mTodayBackgroundPaint.setColor(mTodayBackgroundColor);
mTodayBackgroundPaint.setColor(mTodayBackgroundColor);
// Prepare today header text color paint.
// Prepare today header text color paint.
mTodayHeaderTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTodayHeaderTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTodayHeaderTextPaint.setTextAlign(Paint.Align.CENTER);
mTodayHeaderTextPaint.setTextAlign(Paint.Align.CENTER);
mTodayHeaderTextPaint.setTextSize(mTextSize);
mTodayHeaderTextPaint.setTextSize(mTextSize);
mTodayHeaderTextPaint.setTypeface(Typeface.DEFAULT_BOLD);
mTodayHeaderTextPaint.setTypeface(Typeface.DEFAULT_BOLD);
mTodayHeaderTextPaint.setColor(mTodayHeaderTextColor);
mTodayHeaderTextPaint.setColor(mTodayHeaderTextColor);
// Prepare event background color.
// Prepare event background color.
mEventBackgroundPaint = new Paint();
mEventBackgroundPaint = new Paint();
mEventBackgroundPaint.setColor(Color.rgb(174, 208, 238));
mEventBackgroundPaint.setColor(Color.rgb(174, 208, 238));
// Prepare header column background color.
// Prepare header column background color.
mHeaderColumnBackgroundPaint = new Paint();
mHeaderColumnBackgroundPaint = new Paint();
mHeaderColumnBackgroundPaint.setColor(mHeaderColumnBackgroundColor);
mHeaderColumnBackgroundPaint.setColor(mHeaderColumnBackgroundColor);
// Prepare event text size and color.
// Prepare event text size and color.
mEventTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.LINEAR_TEXT_FLAG);
mEventTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.LINEAR_TEXT_FLAG);
mEventTextPaint.setStyle(Paint.Style.FILL);
mEventTextPaint.setStyle(Paint.Style.FILL);
mEventTextPaint.setColor(mEventTextColor);
mEventTextPaint.setColor(mEventTextColor);
mEventTextPaint.setTextSize(mEventTextSize);
mEventTextPaint.setTextSize(mEventTextSize);
// Set default event color.
// Set default event color.
mDefaultEventColor = Color.parseColor("#9fc6e7");
mDefaultEventColor = Color.parseColor("#9fc6e7");
mScaleDetector = new ScaleGestureDetector(mContext, new ScaleGestureDetector.OnScaleGestureListener() {
mScaleDetector = new ScaleGestureDetector(mContext, new ScaleGestureDetector.OnScaleGestureListener() {
@Override
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
public void onScaleEnd(ScaleGestureDetector detector) {
mIsZooming = false;
mIsZooming = false;
}
}
@Override
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
public boolean onScaleBegin(ScaleGestureDetector detector) {
mIsZooming = true;
mIsZooming = true;
goToNearestOrigin();
goToNearestOrigin();
return true;
return true;
}
}
@Override
@Override
public boolean onScale(ScaleGestureDetector detector) {
public boolean onScale(ScaleGestureDetector detector) {
mNewHourHeight = Math.round(mHourHeight * detector.getScaleFactor());
mNewHourHeight = Math.round(mHourHeight * detector.getScaleFactor());
invalidate();
invalidate();
return true;
return true;
}
}
});
});
}
}
// fix rotation changes
// fix rotation changes
@Override
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
super.onSizeChanged(w, h, oldw, oldh);
mAreDimensionsInvalid = true;
mAreDimensionsInvalid = true;
}
}
/**
/**
* Initialize time column width. Calculate value with all possible hours (supposed widest text).
* Initialize time column width. Calculate value with all possible hours (supposed widest text).
*/
*/
private void initTextTimeWidth() {
private void initTextTimeWidth() {
mTimeTextWidth = 0;
mTimeTextWidth = 0;
for (int i = 0; i < 24; i++) {
for (int i = 0; i < 12; i++) {
// Measure time string and get max width.
// Measure time string and get max width.
String time = getDateTimeInterpreter().interpretTime(i);
String time = getDateTimeInterpreter().interpretTime(i);
if (time == null)
if (time == null)
throw new IllegalStateException("A DateTimeInterpreter must not return null time");
throw new IllegalStateException("A DateTimeInterpreter must not return null time");
mTimeTextWidth = Math.max(mTimeTextWidth, mTimeTextPaint.measureText(time));
mTimeTextWidth = Math.max(mTimeTextWidth, mTimeTextPaint.measureText(time));
}
}
}
}
@Override
@Override
protected void onDraw(Canvas canvas) {
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
super.onDraw(canvas);
// Hide everything in the first cell (top left corner).
// Hide everything in the first cell (top left corner).
canvas.drawRect(0, 0, mTimeTextWidth + mHeaderColumnPadding * 2, mHeaderTextHeight + mHeaderRowPadding * 2, mHeaderBackgroundPaint);
canvas.drawRect(0, 0, mTimeTextWidth + mHeaderColumnPadding * 2, mHeaderTextHeight + mHeaderRowPadding * 2, mHeaderBackgroundPaint);
// Draw the header row.
// Draw the header row.
drawHeaderRowAndEvents(canvas);
drawHeaderRowAndEvents(canvas);
// Draw the time column and all the axes/separators.
// Draw the time column and all the axes/separators.
drawTimeColumnAndAxes(canvas);
drawTimeColumnAndAxes(canvas);
}
}
private void drawTimeColumnAndAxes(Canvas canvas) {
private void drawTimeColumnAndAxes(Canvas canvas) {
// Draw the background color for the header column.
// Draw the background color for the header column.
canvas.drawRect(0, mHeaderTextHeight + mHeaderRowPadding * 2, mHeaderColumnWidth, getHeight(), mHeaderColumnBackgroundPaint);
canvas.drawRect(0, mHeaderTextHeight + mHeaderRowPadding * 2, mHeaderColumnWidth, getHeight(), mHeaderColumnBackgroundPaint);
// Clip to paint in left column only.
// Clip to paint in left column only.
canvas.clipRect(0, mHeaderTextHeight + mHeaderRowPadding * 2, mHeaderColumnWidth, getHeight(), Region.Op.REPLACE);
canvas.clipRect(0, mHeaderTextHeight + mHeaderRowPadding * 2, mHeaderColumnWidth, getHeight(), Region.Op.REPLACE);
for (int i = 0; i < 24; i++) {
for (int i = 0; i < 12; i++) {
float top = mHeaderTextHeight + mHeaderRowPadding * 2 + mCurrentOrigin.y + mHourHeight * i + mHeaderMarginBottom;
float top = mHeaderTextHeight + mHeaderRowPadding * 2 + mCurrentOrigin.y + mHourHeight * i + mHeaderMarginBottom;
// Draw the text if its y position is not outside of the visible area. The pivot point of the text is the point at the bottom-right corner.
// Draw the text if its y position is not outside of the visible area. The pivot point of the text is the point at the bottom-right corner.
String time = getDateTimeInterpreter().interpretTime(i);
String time = getDateTimeInterpreter().interpretTime(i+7);
if (time == null)
if (time == null)
throw new IllegalStateException("A DateTimeInterpreter must not return null time");
throw new IllegalStateException("A DateTimeInterpreter must not return null time");
if (top < getHeight()) canvas.drawText(time, mTimeTextWidth + mHeaderColumnPadding, top + mTimeTextHeight, mTimeTextPaint);
if (top < getHeight()) canvas.drawText((time), mTimeTextWidth + mHeaderColumnPadding, top + mTimeTextHeight, mTimeTextPaint);
}
}
}
}
private void drawHeaderRowAndEvents(Canvas canvas) {
private void drawHeaderRowAndEvents(Canvas canvas) {
// Calculate the available width for each day.
// Calculate the available width for each day.
mHeaderColumnWidth = mTimeTextWidth + mHeaderColumnPadding *2;
mHeaderColumnWidth = mTimeTextWidth + mHeaderColumnPadding *2;
mWidthPerDay = getWidth() - mHeaderColumnWidth - mColumnGap * (mNumberOfVisibleDays - 1);
mWidthPerDay = getWidth() - mHeaderColumnWidth - mColumnGap
mWidthPerDay = mWidthPerDay/mNumberOfVisibleDays;
Calendar today = today();
if (mAreDimensionsInvalid) {
mEffectiveMinHour