10
10
import android .graphics .RectF ;
11
11
import android .graphics .Region ;
12
12
import android .graphics .Typeface ;
13
+ import android .os .Build ;
13
14
import android .support .annotation .Nullable ;
14
15
import android .support .v4 .view .GestureDetectorCompat ;
15
16
import android .support .v4 .view .ViewCompat ;
17
+ import android .support .v4 .view .animation .FastOutLinearInInterpolator ;
16
18
import android .text .Layout ;
17
19
import android .text .SpannableStringBuilder ;
18
20
import android .text .StaticLayout ;
28
30
import android .view .ScaleGestureDetector ;
29
31
import android .view .SoundEffectConstants ;
30
32
import android .view .View ;
33
+ import android .view .ViewConfiguration ;
31
34
import android .widget .OverScroller ;
32
35
33
36
import java .text .SimpleDateFormat ;
44
47
*/
45
48
public class WeekView extends View {
46
49
50
+ private enum Direction {
51
+ NONE , LEFT , RIGHT , VERTICAL
52
+ }
53
+
47
54
@ Deprecated
48
55
public static final int LENGTH_SHORT = 1 ;
49
56
@ Deprecated
@@ -86,7 +93,8 @@ public class WeekView extends View {
86
93
private Calendar mFirstVisibleDay ;
87
94
private Calendar mLastVisibleDay ;
88
95
private int mDefaultEventColor ;
89
-
96
+ private int mMinimumFlingVelocity = 0 ;
97
+ private int mScaledTouchSlop = 0 ;
90
98
// Attributes and their default values.
91
99
private int mHourHeight = 50 ;
92
100
private int mNewHourHeight = -1 ;
@@ -152,20 +160,40 @@ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float d
152
160
if (mIsZooming )
153
161
return true ;
154
162
155
- // Calculate the direction to scroll.
156
- if (mCurrentScrollDirection == Direction .NONE ) {
157
- // allow scrolling only in one direction
158
- if (Math .abs (distanceX ) > Math .abs (distanceY )){
159
- mCurrentScrollDirection = Direction .HORIZONTAL ;
163
+ switch (mCurrentScrollDirection ) {
164
+ case NONE : {
165
+ // allow scrolling only in one direction
166
+ if (Math .abs (distanceX ) > Math .abs (distanceY )) {
167
+ if (distanceX > 0 ) {
168
+ mCurrentScrollDirection = Direction .LEFT ;
169
+ } else {
170
+ mCurrentScrollDirection = Direction .RIGHT ;
171
+ }
172
+ } else {
173
+ mCurrentScrollDirection = Direction .VERTICAL ;
174
+ }
175
+ break ;
160
176
}
161
- else {
162
- mCurrentScrollDirection = Direction .VERTICAL ;
177
+ case LEFT : {
178
+ // change direction if there was enough change
179
+ if (Math .abs (distanceX ) > Math .abs (distanceY ) && (distanceX < -mScaledTouchSlop )) {
180
+ mCurrentScrollDirection = Direction .RIGHT ;
181
+ }
182
+ break ;
183
+ }
184
+ case RIGHT : {
185
+ // change direction if there was enough change
186
+ if (Math .abs (distanceX ) > Math .abs (distanceY ) && (distanceX > mScaledTouchSlop )) {
187
+ mCurrentScrollDirection = Direction .LEFT ;
188
+ }
189
+ break ;
163
190
}
164
191
}
165
192
166
193
// Calculate the new origin after scroll.
167
194
switch (mCurrentScrollDirection ) {
168
- case HORIZONTAL :
195
+ case LEFT :
196
+ case RIGHT :
169
197
mCurrentOrigin .x -= distanceX * mXScrollingSpeed ;
170
198
ViewCompat .postInvalidateOnAnimation (WeekView .this );
171
199
break ;
@@ -179,14 +207,20 @@ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float d
179
207
180
208
@ Override
181
209
public boolean onFling (MotionEvent e1 , MotionEvent e2 , float velocityX , float velocityY ) {
210
+ if (mIsZooming )
211
+ return true ;
212
+
182
213
mScroller .forceFinished (true );
183
214
184
215
mCurrentFlingDirection = mCurrentScrollDirection ;
185
- if (mCurrentFlingDirection == Direction .HORIZONTAL ){
186
- 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 );
187
- }
188
- else if (mCurrentFlingDirection == Direction .VERTICAL ){
189
- 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 );
216
+ switch (mCurrentFlingDirection ) {
217
+ case LEFT :
218
+ case RIGHT :
219
+ 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 );
220
+ break ;
221
+ case VERTICAL :
222
+ 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 );
223
+ break ;
190
224
}
191
225
192
226
ViewCompat .postInvalidateOnAnimation (WeekView .this );
@@ -248,10 +282,6 @@ public void onLongPress(MotionEvent e) {
248
282
}
249
283
};
250
284
251
- private enum Direction {
252
- NONE , HORIZONTAL , VERTICAL
253
- }
254
-
255
285
public WeekView (Context context ) {
256
286
this (context , null );
257
287
}
@@ -315,7 +345,10 @@ public WeekView(Context context, AttributeSet attrs, int defStyleAttr) {
315
345
private void init () {
316
346
// Scrolling initialization.
317
347
mGestureDetector = new GestureDetectorCompat (mContext , mGestureListener );
318
- mScroller = new OverScroller (mContext );
348
+ mScroller = new OverScroller (mContext , new FastOutLinearInInterpolator ());
349
+
350
+ mMinimumFlingVelocity = ViewConfiguration .get (mContext ).getScaledMinimumFlingVelocity ();
351
+ mScaledTouchSlop = ViewConfiguration .get (mContext ).getScaledTouchSlop ();
319
352
320
353
// Measure settings for time column.
321
354
mTimeTextPaint = new Paint (Paint .ANTI_ALIAS_FLAG );
@@ -1646,7 +1679,7 @@ public boolean onTouchEvent(MotionEvent event) {
1646
1679
1647
1680
// Check after call of mGestureDetector, so mCurrentFlingDirection and mCurrentScrollDirection are set.
1648
1681
if (event .getAction () == MotionEvent .ACTION_UP && !mIsZooming && mCurrentFlingDirection == Direction .NONE ) {
1649
- if (mCurrentScrollDirection == Direction .HORIZONTAL ) {
1682
+ if (mCurrentScrollDirection == Direction .RIGHT || mCurrentScrollDirection == Direction . LEFT ) {
1650
1683
goToNearestOrigin ();
1651
1684
}
1652
1685
mCurrentScrollDirection = Direction .NONE ;
@@ -1656,15 +1689,29 @@ public boolean onTouchEvent(MotionEvent event) {
1656
1689
}
1657
1690
1658
1691
private void goToNearestOrigin (){
1659
- float leftDays = Math .round (mCurrentOrigin .x / (mWidthPerDay + mColumnGap ));
1692
+ double leftDays = mCurrentOrigin .x / (mWidthPerDay + mColumnGap );
1693
+
1694
+ if (mCurrentFlingDirection != Direction .NONE ) {
1695
+ // snap to nearest day
1696
+ leftDays = Math .round (leftDays );
1697
+ } else if (mCurrentScrollDirection == Direction .LEFT ) {
1698
+ // snap to last day
1699
+ leftDays = Math .floor (leftDays );
1700
+ } else if (mCurrentScrollDirection == Direction .RIGHT ) {
1701
+ // snap to next day
1702
+ leftDays = Math .ceil (leftDays );
1703
+ } else {
1704
+ // snap to nearest day
1705
+ leftDays = Math .round (leftDays );
1706
+ }
1660
1707
1661
1708
int nearestOrigin = (int ) (mCurrentOrigin .x - leftDays * (mWidthPerDay + mColumnGap ));
1662
1709
1663
1710
if (nearestOrigin != 0 ) {
1664
1711
// Stop current animation.
1665
1712
mScroller .forceFinished (true );
1666
1713
// Snap to date.
1667
- mScroller .startScroll ((int ) mCurrentOrigin .x , (int ) mCurrentOrigin .y , -nearestOrigin , 0 , 50 );
1714
+ mScroller .startScroll ((int ) mCurrentOrigin .x , (int ) mCurrentOrigin .y , -nearestOrigin , 0 , ( int ) ( Math . abs ( nearestOrigin ) / mWidthPerDay * 500 ) );
1668
1715
ViewCompat .postInvalidateOnAnimation (WeekView .this );
1669
1716
}
1670
1717
// Reset scrolling and fling direction.
@@ -1682,14 +1729,29 @@ public void computeScroll() {
1682
1729
goToNearestOrigin ();
1683
1730
}
1684
1731
} else {
1685
- if (mScroller .computeScrollOffset ()) {
1732
+ if (mCurrentFlingDirection != Direction .NONE && forceFinishScroll ()) {
1733
+ goToNearestOrigin ();
1734
+ } else if (mScroller .computeScrollOffset ()) {
1686
1735
mCurrentOrigin .y = mScroller .getCurrY ();
1687
1736
mCurrentOrigin .x = mScroller .getCurrX ();
1688
1737
ViewCompat .postInvalidateOnAnimation (this );
1689
1738
}
1690
1739
}
1691
1740
}
1692
1741
1742
+ /**
1743
+ * check if scrolling should be stopped
1744
+ * @return true if scrolling should be stopped before reaching the end of animation
1745
+ */
1746
+ private boolean forceFinishScroll () {
1747
+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .ICE_CREAM_SANDWICH ) {
1748
+ // current velocity only available since api 14
1749
+ return mScroller .getCurrVelocity () <= mMinimumFlingVelocity ;
1750
+ } else {
1751
+ return false ;
1752
+ }
1753
+ }
1754
+
1693
1755
1694
1756
/////////////////////////////////////////////////////////////////
1695
1757
//
0 commit comments