Skip to content

Commit

Permalink
Merge remote-tracking branch 'refs/remotes/origin/develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
alamkanak committed Jan 2, 2016
2 parents 616454f + 56b9d40 commit e1138e0
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 44 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,12 @@ To do
Changelog
---------

**Version 1.2.5**

* Add support for subclasses of `WeekViewEvent`
* Fix scroll animation
* Add support for semi-transparent header colors

**Version 1.2.4**

* **NOTE:** If you are using `WeekView.MonthChangeListener`, make sure to change it into `MonthLoader.MonthChangeListener`
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION_NAME=1.2.4
VERSION_NAME=1.2.5
GROUP=com.github.alamkanak

POM_DESCRIPTION=An android library to show day view, week view, 3 day view etc. in your app.
Expand Down
2 changes: 1 addition & 1 deletion library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ android {
}

dependencies {
compile 'com.android.support:appcompat-v7:23.0.1'
compile 'com.android.support:appcompat-v7:23.1.1'
}

apply from: 'https://raw.github.com/chrisbanes/gradle-mvn-push/master/gradle-mvn-push.gradle'
8 changes: 4 additions & 4 deletions library/src/main/java/com/alamkanak/weekview/MonthLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public double toWeekViewPeriodIndex(Calendar instance){
}

@Override
public List<WeekViewEvent> onLoad(int periodIndex){
public List<? extends WeekViewEvent> onLoad(int periodIndex){
return mOnMonthChangeListener.onMonthChange(periodIndex / 12, periodIndex % 12 + 1);
}

Expand All @@ -34,10 +34,10 @@ public interface MonthChangeListener {
* Very important interface, it's the base to load events in the calendar.
* This method is called three times: once to load the previous month, once to load the next month and once to load the current month.<br/>
* <strong>That's why you can have three times the same event at the same place if you mess up with the configuration</strong>
* @param newYear: year of the events required by the view.
* @param newMonth: month of the events required by the view <br/><strong>1 based (not like JAVA API) --> January = 1 and December = 12</strong>.
* @param newYear : year of the events required by the view.
* @param newMonth : month of the events required by the view <br/><strong>1 based (not like JAVA API) --> January = 1 and December = 12</strong>.
* @return a list of the events happening <strong>during the specified month</strong>.
*/
List<WeekViewEvent> onMonthChange(int newYear, int newMonth);
List<? extends WeekViewEvent> onMonthChange(int newYear, int newMonth);
}
}
146 changes: 109 additions & 37 deletions library/src/main/java/com/alamkanak/weekview/WeekView.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.graphics.Typeface;
import android.os.Build;
import android.support.annotation.Nullable;
import android.support.v4.view.GestureDetectorCompat;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.animation.FastOutLinearInInterpolator;
import android.text.Layout;
import android.text.SpannableStringBuilder;
import android.text.StaticLayout;
Expand All @@ -27,6 +30,7 @@
import android.view.ScaleGestureDetector;
import android.view.SoundEffectConstants;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.OverScroller;

import java.text.SimpleDateFormat;
Expand All @@ -43,6 +47,10 @@
*/
public class WeekView extends View {

private enum Direction {
NONE, LEFT, RIGHT, VERTICAL
}

@Deprecated
public static final int LENGTH_SHORT = 1;
@Deprecated
Expand Down Expand Up @@ -72,9 +80,9 @@ public class WeekView extends View {
private Paint mEventBackgroundPaint;
private float mHeaderColumnWidth;
private List<EventRect> mEventRects;
private List<WeekViewEvent> mPreviousPeriodEvents;
private List<WeekViewEvent> mCurrentPeriodEvents;
private List<WeekViewEvent> mNextPeriodEvents;
private List<? extends WeekViewEvent> mPreviousPeriodEvents;
private List<? extends WeekViewEvent> mCurrentPeriodEvents;
private List<? extends WeekViewEvent> mNextPeriodEvents;
private TextPaint mEventTextPaint;
private Paint mHeaderColumnBackgroundPaint;
private int mFetchedPeriod = -1; // the middle period the calendar has fetched.
Expand All @@ -85,7 +93,8 @@ public class WeekView extends View {
private Calendar mFirstVisibleDay;
private Calendar mLastVisibleDay;
private int mDefaultEventColor;

private int mMinimumFlingVelocity = 0;
private int mScaledTouchSlop = 0;
// Attributes and their default values.
private int mHourHeight = 50;
private int mNewHourHeight = -1;
Expand Down Expand Up @@ -151,20 +160,40 @@ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float d
if (mIsZooming)
return true;

// Calculate the direction to scroll.
if (mCurrentScrollDirection == Direction.NONE) {
// allow scrolling only in one direction
if (Math.abs(distanceX) > Math.abs(distanceY)){
mCurrentScrollDirection = Direction.HORIZONTAL;
switch (mCurrentScrollDirection) {
case NONE: {
// Allow scrolling only in one direction.
if (Math.abs(distanceX) > Math.abs(distanceY)) {
if (distanceX > 0) {
mCurrentScrollDirection = Direction.LEFT;
} else {
mCurrentScrollDirection = Direction.RIGHT;
}
} else {
mCurrentScrollDirection = Direction.VERTICAL;
}
break;
}
else {
mCurrentScrollDirection = Direction.VERTICAL;
case LEFT: {
// Change direction if there was enough change.
if (Math.abs(distanceX) > Math.abs(distanceY) && (distanceX < -mScaledTouchSlop)) {
mCurrentScrollDirection = Direction.RIGHT;
}
break;
}
case RIGHT: {
// Change direction if there was enough change.
if (Math.abs(distanceX) > Math.abs(distanceY) && (distanceX > mScaledTouchSlop)) {
mCurrentScrollDirection = Direction.LEFT;
}
break;
}
}

// Calculate the new origin after scroll.
switch (mCurrentScrollDirection) {
case HORIZONTAL:
case LEFT:
case RIGHT:
mCurrentOrigin.x -= distanceX * mXScrollingSpeed;
ViewCompat.postInvalidateOnAnimation(WeekView.this);
break;
Expand All @@ -178,14 +207,20 @@ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float d

@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (mIsZooming)
return true;

mScroller.forceFinished(true);

mCurrentFlingDirection = mCurrentScrollDirection;
if (mCurrentFlingDirection == Direction.HORIZONTAL){
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);
}
else if (mCurrentFlingDirection == Direction.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);
switch (mCurrentFlingDirection) {
case LEFT:
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);
break;
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);
break;
}

ViewCompat.postInvalidateOnAnimation(WeekView.this);
Expand Down Expand Up @@ -247,10 +282,6 @@ public void onLongPress(MotionEvent e) {
}
};

private enum Direction {
NONE, HORIZONTAL, VERTICAL
}

public WeekView(Context context) {
this(context, null);
}
Expand Down Expand Up @@ -314,7 +345,10 @@ public WeekView(Context context, AttributeSet attrs, int defStyleAttr) {
private void init() {
// Scrolling initialization.
mGestureDetector = new GestureDetectorCompat(mContext, mGestureListener);
mScroller = new OverScroller(mContext);
mScroller = new OverScroller(mContext, new FastOutLinearInInterpolator());

mMinimumFlingVelocity = ViewConfiguration.get(mContext).getScaledMinimumFlingVelocity();
mScaledTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();

// Measure settings for time column.
mTimeTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
Expand Down Expand Up @@ -438,23 +472,23 @@ private void initTextTimeWidth() {
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

// Hide everything in the first cell (top left corner).
canvas.drawRect(0, 0, mTimeTextWidth + mHeaderColumnPadding * 2, mHeaderTextHeight + mHeaderRowPadding * 2, mHeaderBackgroundPaint);

// Draw the header row.
drawHeaderRowAndEvents(canvas);

// Draw the time column and all the axes/separators.
drawTimeColumnAndAxes(canvas);

// Hide everything in the first cell (top left corner).
canvas.drawRect(0, 0, mTimeTextWidth + mHeaderColumnPadding * 2, mHeaderTextHeight + mHeaderRowPadding * 2, mHeaderBackgroundPaint);

// Hide anything that is in the bottom margin of the header row.
canvas.drawRect(mHeaderColumnWidth, mHeaderTextHeight + mHeaderRowPadding * 2, getWidth(), mHeaderRowPadding * 2 + mHeaderTextHeight + mHeaderMarginBottom + mTimeTextHeight/2, mHeaderColumnBackgroundPaint);
}

private void drawTimeColumnAndAxes(Canvas canvas) {
// Draw the background color for the header column.
canvas.drawRect(0, mHeaderTextHeight + mHeaderRowPadding * 2, mHeaderColumnWidth, getHeight(), mHeaderColumnBackgroundPaint);

// Clip to paint in left column only.
canvas.clipRect(0, mHeaderTextHeight + mHeaderRowPadding * 2, mHeaderColumnWidth, getHeight(), Region.Op.REPLACE);

for (int i = 0; i < 24; i++) {
float top = mHeaderTextHeight + mHeaderRowPadding * 2 + mCurrentOrigin.y + mHourHeight * i + mHeaderMarginBottom;

Expand Down Expand Up @@ -544,6 +578,9 @@ else if (mNewHourHeight > mMaxHourHeight)
}
}

// Clip to paint events only.
canvas.clipRect(mHeaderColumnWidth, mHeaderTextHeight + mHeaderRowPadding * 2 + mHeaderMarginBottom + mTimeTextHeight/2, getWidth(), getHeight(), Region.Op.REPLACE);

// Iterate through each day.
Calendar oldFirstVisibleDay = mFirstVisibleDay;
mFirstVisibleDay = (Calendar) today.clone();
Expand Down Expand Up @@ -629,6 +666,10 @@ else if (day.before(today)) {
startPixel += mWidthPerDay + mColumnGap;
}


// Clip to paint header row only.
canvas.clipRect(mHeaderColumnWidth, 0, getWidth(), mHeaderTextHeight + mHeaderRowPadding * 2, Region.Op.REPLACE);

// Draw the header background.
canvas.drawRect(0, 0, getWidth(), mHeaderTextHeight + mHeaderRowPadding * 2, mHeaderBackgroundPaint);

Expand Down Expand Up @@ -845,9 +886,9 @@ private void getMoreEvents(Calendar day) {
if (mWeekViewLoader != null){
int periodToFetch = (int) mWeekViewLoader.toWeekViewPeriodIndex(day);
if (!isInEditMode() && (mFetchedPeriod < 0 || mFetchedPeriod != periodToFetch || mRefreshEvents)) {
List<WeekViewEvent> previousPeriodEvents = null;
List<WeekViewEvent> currentPeriodEvents = null;
List<WeekViewEvent> nextPeriodEvents = null;
List<? extends WeekViewEvent> previousPeriodEvents = null;
List<? extends WeekViewEvent> currentPeriodEvents = null;
List<? extends WeekViewEvent> nextPeriodEvents = null;

if (mPreviousPeriodEvents != null && mCurrentPeriodEvents != null && mNextPeriodEvents != null){
if (periodToFetch == mFetchedPeriod-1){
Expand Down Expand Up @@ -917,6 +958,8 @@ else if (periodToFetch == mFetchedPeriod+1){
* @param event The event to cache.
*/
private void cacheEvent(WeekViewEvent event) {
if(event.getStartTime().compareTo(event.getEndTime()) >= 0)
return;
if (!isSameDay(event.getStartTime(), event.getEndTime())) {
// Add first day.
Calendar endTime = (Calendar) event.getStartTime().clone();
Expand Down Expand Up @@ -961,7 +1004,7 @@ private void cacheEvent(WeekViewEvent event) {
* Sort and cache events.
* @param events The events to be sorted and cached.
*/
private void sortAndCacheEvents(List<WeekViewEvent> events) {
private void sortAndCacheEvents(List<? extends WeekViewEvent> events) {
sortEvents(events);
for (WeekViewEvent event : events) {
cacheEvent(event);
Expand All @@ -972,7 +1015,7 @@ private void sortAndCacheEvents(List<WeekViewEvent> events) {
* Sorts the events in ascending order.
* @param events The events to be sorted.
*/
private void sortEvents(List<WeekViewEvent> events) {
private void sortEvents(List<? extends WeekViewEvent> events) {
Collections.sort(events, new Comparator<WeekViewEvent>() {
@Override
public int compare(WeekViewEvent event1, WeekViewEvent event2) {
Expand Down Expand Up @@ -1636,7 +1679,7 @@ public boolean onTouchEvent(MotionEvent event) {

// Check after call of mGestureDetector, so mCurrentFlingDirection and mCurrentScrollDirection are set.
if (event.getAction() == MotionEvent.ACTION_UP && !mIsZooming && mCurrentFlingDirection == Direction.NONE) {
if (mCurrentScrollDirection == Direction.HORIZONTAL) {
if (mCurrentScrollDirection == Direction.RIGHT || mCurrentScrollDirection == Direction.LEFT) {
goToNearestOrigin();
}
mCurrentScrollDirection = Direction.NONE;
Expand All @@ -1646,15 +1689,29 @@ public boolean onTouchEvent(MotionEvent event) {
}

private void goToNearestOrigin(){
float leftDays = Math.round(mCurrentOrigin.x / (mWidthPerDay + mColumnGap));
double leftDays = mCurrentOrigin.x / (mWidthPerDay + mColumnGap);

if (mCurrentFlingDirection != Direction.NONE) {
// snap to nearest day
leftDays = Math.round(leftDays);
} else if (mCurrentScrollDirection == Direction.LEFT) {
// snap to last day
leftDays = Math.floor(leftDays);
} else if (mCurrentScrollDirection == Direction.RIGHT) {
// snap to next day
leftDays = Math.ceil(leftDays);
} else {
// snap to nearest day
leftDays = Math.round(leftDays);
}

int nearestOrigin = (int) (mCurrentOrigin.x - leftDays * (mWidthPerDay + mColumnGap));

if (nearestOrigin != 0) {
// Stop current animation.
mScroller.forceFinished(true);
// Snap to date.
mScroller.startScroll((int) mCurrentOrigin.x, (int) mCurrentOrigin.y, -nearestOrigin, 0, 50);
mScroller.startScroll((int) mCurrentOrigin.x, (int) mCurrentOrigin.y, -nearestOrigin, 0, (int) (Math.abs(nearestOrigin) / mWidthPerDay * 500));
ViewCompat.postInvalidateOnAnimation(WeekView.this);
}
// Reset scrolling and fling direction.
Expand All @@ -1672,14 +1729,29 @@ public void computeScroll() {
goToNearestOrigin();
}
} else {
if (mScroller.computeScrollOffset()) {
if (mCurrentFlingDirection != Direction.NONE && forceFinishScroll()) {
goToNearestOrigin();
} else if (mScroller.computeScrollOffset()) {
mCurrentOrigin.y = mScroller.getCurrY();
mCurrentOrigin.x = mScroller.getCurrX();
ViewCompat.postInvalidateOnAnimation(this);
}
}
}

/**
* Check if scrolling should be stopped.
* @return true if scrolling should be stopped before reaching the end of animation.
*/
private boolean forceFinishScroll() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
// current velocity only available since api 14
return mScroller.getCurrVelocity() <= mMinimumFlingVelocity;
} else {
return false;
}
}


/////////////////////////////////////////////////////////////////
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ public interface WeekViewLoader {
* @param periodIndex the period to load
* @return A list with the events of this period
*/
List<WeekViewEvent> onLoad(int periodIndex);
List<? extends WeekViewEvent> onLoad(int periodIndex);
}

0 comments on commit e1138e0

Please sign in to comment.