1010import android .view .View ;
1111import android .view .ViewConfiguration ;
1212import android .view .ViewGroup ;
13+ import android .view .animation .Interpolator ;
1314import android .widget .Scroller ;
1415import androidx .core .view .ViewCompat ;
1516
@@ -53,9 +54,19 @@ public class StackViewLayout extends ViewGroup{
5354
5455 private int dragModel ;
5556 private boolean mIsDragged = false ;
57+ private boolean mIsFling = false ;
58+
5659 private List <Integer > xSpace = new ArrayList <>(stackSize );
5760 private List <Integer > ySpace = new ArrayList <>(stackSize );
5861
62+ private static final Interpolator sInterpolator = new Interpolator () {
63+ @ Override
64+ public float getInterpolation (float t ) {
65+ t -= 1.0f ;
66+ return t * t * t * t * t + 1.0f ;
67+ }
68+ };
69+
5970 public StackViewLayout (Context context ) {
6071 this (context , null );
6172 }
@@ -90,7 +101,7 @@ public StackViewLayout(Context context, AttributeSet attrs, int defStyleAttr) {
90101 ViewConfiguration configuration = ViewConfiguration .get (getContext ());
91102 mMaximumVelocity = configuration .getScaledMaximumFlingVelocity ();
92103 mTouchSlop = configuration .getScaledTouchSlop ();
93- mScroller = new Scroller (context );
104+ mScroller = new Scroller (context , sInterpolator );
94105 if (isInEditMode ()){
95106 adapter = new StackViewAdapter () {
96107 @ Override
@@ -111,6 +122,18 @@ public int getItemCount() {
111122 }
112123 }
113124
125+ @ Override
126+ protected void onAttachedToWindow () {
127+ super .onAttachedToWindow ();
128+ startAutoPlay ();
129+ }
130+
131+ @ Override
132+ protected void onDetachedFromWindow () {
133+ super .onDetachedFromWindow ();
134+ stopAutoPlay (true );
135+ }
136+
114137 @ Override
115138 protected void onMeasure (int widthMeasureSpec , int heightMeasureSpec ) {
116139 int width = getDefaultSize (0 , widthMeasureSpec );
@@ -177,6 +200,8 @@ public void computeScroll() {
177200 int dx = mScroller .getCurrX ();
178201 scrollDx (dx );
179202 ViewCompat .postInvalidateOnAnimation (this );
203+ }else {
204+ nextPage ();
180205 }
181206 }
182207
@@ -200,20 +225,66 @@ private void scrollDx(int dx){
200225 child .layout (left ,top ,right ,bottom );
201226 }
202227
228+ private void resetTouch (){
229+ mScroller .abortAnimation ();
230+ stopAutoPlay (false );
231+ }
232+
233+ private int getSign (){
234+ return MODEL_LEFT == stackModel ?1 :-1 ;
235+ }
236+
203237 private void scrollVelocity (int velocity ){
204238 int count = getChildCount ();
205239 View child ;
206240 int dx ;
241+ int sign ;
207242 if ( 0 == (dragModel &stackModel )) {
208243 int i = count - 2 ;
209244 child = getChildAt (i );
210245 dx = getPaddingLeft ()+edge +xSpace .get (i -1 )-child .getLeft ();
246+ sign = getSign ();
211247 }else {
212248 int i = count -1 ;
213249 child = getChildAt (i );
214250 dx = getPaddingLeft ()+(MODEL_LEFT == stackModel ?getMeasuredWidth ():-childWidthMeasure )-child .getLeft ();
251+ sign = -getSign ();
252+ }
253+ if (velocity *sign >getMeasuredWidth () || -dx *sign >0.45 *childWidthMeasure ){
254+ int dnx = dx +sign *(getMeasuredWidth ()+edge );
255+ startScroll (-dx ,dnx );
256+ mIsFling = true ;
257+ }else {
258+ startScroll (-dx , dx );
259+ }
260+ startAutoPlay ();
261+ }
262+
263+ private void autoScroll (){
264+ if (mScroller .isFinished () && adapter !=null && adapter .getItemCount ()>1 ){
265+ dragModel = MODEL_LEFT ==stackModel ? MODEL_RIGHT :MODEL_LEFT ;
266+ scrollVelocity (2 *getMeasuredWidth ()*getSign ());
267+ }
268+ }
269+
270+ private void nextPage (){
271+ if (mIsFling ) {
272+ mIsFling = false ;
273+ int cnt = adapter .getItemCount ();
274+ if (0 == (dragModel & stackModel )){
275+ current = (current +1 )%cnt ;
276+ int position = (current +stackSize )%cnt ;
277+ int viewType = adapter .getItemViewType (position );
278+ View child = adapter .onCreateView (this , viewType );
279+ addView (child ,0 );
280+ removeViewAt (getChildCount () - 1 );
281+ adapter .onBindView (child ,position );
282+ adapter .onPageSelected (current );
283+ } else {
284+ current = (current -1 +cnt )%cnt ;
285+ removeViewAt (0 );
286+ }
215287 }
216- startScroll (-dx ,dx );
217288 }
218289
219290 private void startScroll (int begin ,int dx ){
@@ -222,28 +293,42 @@ private void startScroll(int begin,int dx){
222293 ViewCompat .postInvalidateOnAnimation (this );
223294 }
224295
225- private void handleAutoPlay ( boolean play ){
226- if (play ) {
227- if (executor == null || executor .isShutdown ()){
296+ private void startAutoPlay ( ){
297+ if (autoPlay ) {
298+ if (executor == null || executor .isShutdown ()) {
228299 executor = new ScheduledThreadPoolExecutor (1 );
300+ }
301+ if (executor .getQueue ().size () <= 0 ) {
229302 executor .scheduleWithFixedDelay (new Runnable () {
230303 @ Override
231304 public void run () {
232-
305+ autoScroll ();
233306 }
234307 }, mDelay , mDelay , TimeUnit .MILLISECONDS );
235308 }
236- }else {
309+ }
310+ }
311+
312+ private void stopAutoPlay (boolean force ){
313+ if (force ) {
237314 if (executor != null && !executor .isShutdown ()) {
238315 executor .shutdownNow ();
239316 executor = null ;
240317 }
318+ }else {
319+ if (executor != null ){
320+ executor .getQueue ().clear ();
321+ }
241322 }
242323 }
243324
244325 public void setAutoPlay (boolean autoPlay ){
245326 this .autoPlay = autoPlay ;
246- handleAutoPlay (autoPlay );
327+ if (autoPlay ){
328+ startAutoPlay ();
329+ }else {
330+ stopAutoPlay (false );
331+ }
247332 }
248333
249334 public void setAdapter (StackViewAdapter adapter ) {
@@ -255,25 +340,16 @@ public StackViewAdapter getAdapter(){
255340 return adapter ;
256341 }
257342
258- @ Override
259- protected void onAttachedToWindow () {
260- super .onAttachedToWindow ();
261- handleAutoPlay (autoPlay );
262- }
263-
264- @ Override
265- protected void onDetachedFromWindow () {
266- super .onDetachedFromWindow ();
267- handleAutoPlay (false );
268- }
269-
270343 @ Override
271344 public boolean onInterceptTouchEvent (MotionEvent ev ) {
272345 log (" action=" +ev .getAction ()+",x=" +ev .getX ()+",y=" +ev .getY ()+",slop=" +mTouchSlop );
346+ if (adapter == null || adapter .getItemCount () <= 1 ){
347+ return false ;
348+ }
273349 final int action = ev .getAction () & MotionEvent .ACTION_MASK ;
274350 switch (action ) {
275351 case MotionEvent .ACTION_DOWN :
276- mScroller . abortAnimation ();
352+ resetTouch ();
277353 mIsDragged = false ;
278354 initVelocityTracker ();
279355 lastX = initX = ev .getX ();
@@ -293,16 +369,13 @@ public boolean onInterceptTouchEvent(MotionEvent ev) {
293369 return true ;
294370 }
295371 break ;
296- case MotionEvent .ACTION_UP :
297- case MotionEvent .ACTION_CANCEL :
298- scrollVelocity (0 );
299- break ;
300372 }
301373 return false ;
302374 }
303375
304376 @ Override
305377 public boolean onTouchEvent (MotionEvent ev ) {
378+ log (" action=" +ev .getAction ()+",x=" +ev .getX ()+",y=" +ev .getY ());
306379 if (adapter == null || adapter .getItemCount () <= 1 ){
307380 return false ;
308381 }
@@ -312,10 +385,6 @@ public boolean onTouchEvent(MotionEvent ev) {
312385 mVelocity .addMovement (ev );
313386 int action = ev .getActionMasked ();
314387 switch (action ) {
315- case MotionEvent .ACTION_DOWN :
316- mScroller .abortAnimation ();
317- mIsDragged = false ;
318- break ;
319388 case MotionEvent .ACTION_MOVE :
320389 if (!mIsDragged ) {
321390 float dx = ev .getX ()-lastX ;
@@ -342,10 +411,6 @@ public boolean onTouchEvent(MotionEvent ev) {
342411 return true ;
343412 }
344413
345- private void resetTouch (){
346-
347- }
348-
349414 private void initVelocityTracker () {
350415 if (mVelocity == null ) {
351416 mVelocity = VelocityTracker .obtain ();
0 commit comments