Bug 1314322 - Create new add animation for tabs list. r?sebastian draft
authorTom Klein <twointofive@gmail.com>
Wed, 26 Oct 2016 15:32:50 -0500
changeset 432768 63ad7745a8d5fdb6721fde8f40e5677261efce39
parent 432767 743dcd9360b7d9214e1fe5d2365c94ef1fd5f300
child 535747 b03943de9d618e01b7e8794e409c69939e090984
push id34417
push userbmo:twointofive@gmail.com
push dateWed, 02 Nov 2016 18:23:50 +0000
reviewerssebastian
bugs1314322
milestone52.0a1
Bug 1314322 - Create new add animation for tabs list. r?sebastian Make the TabsListLayoutAnimator a DefaultItemAnimatorBase. MozReview-Commit-ID: 4vZ93ACPUq
mobile/android/base/java/org/mozilla/gecko/tabs/TabsListLayout.java
mobile/android/base/java/org/mozilla/gecko/tabs/TabsListLayoutAnimator.java
--- a/mobile/android/base/java/org/mozilla/gecko/tabs/TabsListLayout.java
+++ b/mobile/android/base/java/org/mozilla/gecko/tabs/TabsListLayout.java
@@ -31,22 +31,17 @@ public class TabsListLayout extends Tabs
 
         setLayoutManager(new LinearLayoutManager(context));
 
         // A TouchHelper handler for swipe to close.
         final TabsTouchHelperCallback callback = new TabsTouchHelperCallback(this);
         final ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
         touchHelper.attachToRecyclerView(this);
 
-        final TabsListLayoutAnimator animator = new TabsListLayoutAnimator();
-        animator.setRemoveDuration(ANIMATION_DURATION);
-        // A fade in/out each time the title/thumbnail/etc. gets updated isn't helpful, so disable
-        // the change animation.
-        animator.setSupportsChangeAnimations(false);
-        setItemAnimator(animator);
+        setItemAnimator(new TabsListLayoutAnimator(ANIMATION_DURATION));
     }
 
     @Override
     public void closeAll() {
         final int childCount = getChildCount();
 
         // Just close the panel if there are no tabs to close.
         if (childCount == 0) {
--- a/mobile/android/base/java/org/mozilla/gecko/tabs/TabsListLayoutAnimator.java
+++ b/mobile/android/base/java/org/mozilla/gecko/tabs/TabsListLayoutAnimator.java
@@ -1,153 +1,65 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.tabs;
 
-import org.mozilla.gecko.util.ThreadUtils;
+import org.mozilla.gecko.widget.DefaultItemAnimatorBase;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.support.v4.animation.AnimatorCompatHelper;
-import android.support.v7.widget.DefaultItemAnimator;
+import android.support.v4.view.ViewCompat;
 import android.support.v7.widget.RecyclerView;
 import android.view.View;
-import android.view.ViewPropertyAnimator;
 
-import java.util.ArrayList;
-import java.util.List;
-
-// This is a light rewrite of DefaultItemAnimator to support a non-default remove animation.
-class TabsListLayoutAnimator extends DefaultItemAnimator {
-    private List<RecyclerView.ViewHolder> pendingRemovals = new ArrayList<>();
-    // ViewHolders on which remove animations are currently being run.
-    private List<RecyclerView.ViewHolder> removeAnimations = new ArrayList<>();
-
-    @Override
-    public void runPendingAnimations() {
-        if (pendingRemovals.isEmpty()) {
-            super.runPendingAnimations();
-            return;
-        }
-
-        for (RecyclerView.ViewHolder holder : pendingRemovals) {
-            animateRemoveImpl(holder);
-        }
-        pendingRemovals.clear();
-
-        // Run remaining default animations, but only after the remove animations finish.
-        ThreadUtils.postDelayedToUiThread(new Runnable() {
-            @Override
-            public void run() {
-                TabsListLayoutAnimator.super.runPendingAnimations();
-            }
-        }, getRemoveDuration());
+class TabsListLayoutAnimator extends DefaultItemAnimatorBase {
+    public TabsListLayoutAnimator(int animationDuration) {
+        setRemoveDuration(animationDuration);
+        setAddDuration(animationDuration);
+        // A fade in/out each time the title/thumbnail/etc. gets updated isn't helpful, so disable
+        // the change animation.
+        setSupportsChangeAnimations(false);
     }
 
     @Override
-    public boolean animateRemove(RecyclerView.ViewHolder holder) {
+    protected boolean preAnimateRemoveImpl(final RecyclerView.ViewHolder holder) {
         // If the view isn't at full alpha then we were closed by a swipe which an
         // ItemTouchHelper is animating for us, so just return without animating the remove and
         // let runPendingAnimations pick up the rest.
         if (holder.itemView.getAlpha() < 1) {
-            dispatchRemoveFinished(holder);
             return false;
         }
         resetAnimation(holder);
-        pendingRemovals.add(holder);
         return true;
     }
 
-    private void animateRemoveImpl(final RecyclerView.ViewHolder holder) {
-        final TabsLayoutItemView itemView = (TabsLayoutItemView) holder.itemView;
-        removeAnimations.add(holder);
-        final ViewPropertyAnimator animator = itemView.animate();
-        animator.translationX(itemView.getWidth())
-                .alpha(0)
+    @Override
+    protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) {
+        final View itemView = holder.itemView;
+        ViewCompat.animate(itemView)
                 .setDuration(getRemoveDuration())
-                .setListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationStart(Animator animation) {
-                        dispatchRemoveStarting(holder);
-                    }
-
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        animator.setListener(null);
-                        itemView.setAlpha(1);
-                        itemView.setTranslationX(0);
-                        dispatchRemoveFinished(holder);
-                        removeAnimations.remove(holder);
-                        dispatchFinishedWhenDone();
-                    }
-                }).start();
+                .translationX(itemView.getWidth())
+                .alpha(0)
+                .setListener(new DefaultRemoveVpaListener(holder))
+                .start();
     }
 
     @Override
-    public void endAnimation(RecyclerView.ViewHolder item) {
-        final View view = item.itemView;
-        // This will trigger end callback which should set properties to their target values.
-        view.animate().cancel();
-        if (pendingRemovals.remove(item)) {
-            view.setAlpha(1);
-            view.setTranslationX(0);
-            dispatchRemoveFinished(item);
-        }
-
-        if (isRunning()) {
-            super.endAnimation(item);
-        } else {
-            dispatchAnimationsFinished();
-        }
-    }
-
-    private void resetAnimation(RecyclerView.ViewHolder holder) {
-        AnimatorCompatHelper.clearInterpolator(holder.itemView);
-        endAnimation(holder);
+    protected boolean preAnimateAddImpl(RecyclerView.ViewHolder holder) {
+        resetAnimation(holder);
+        final View itemView = holder.itemView;
+        itemView.setTranslationX(itemView.getWidth());
+        itemView.setAlpha(0);
+        return true;
     }
 
     @Override
-    public boolean isRunning() {
-        return (!pendingRemovals.isEmpty() ||
-                !removeAnimations.isEmpty() ||
-                super.isRunning());
-    }
-
-    /**
-     * Check the state of currently pending and running animations. If there are none
-     * pending/running, call {@link #dispatchAnimationsFinished()} to notify any
-     * listeners.
-     */
-    private void dispatchFinishedWhenDone() {
-        if (!isRunning()) {
-            dispatchAnimationsFinished();
-        }
-    }
-
-    @Override
-    public void endAnimations() {
-        final int count = pendingRemovals.size();
-        for (int i = count - 1; i >= 0; i--) {
-            final RecyclerView.ViewHolder item = pendingRemovals.get(i);
-            dispatchRemoveFinished(item);
-            pendingRemovals.remove(i);
-        }
-
-        if (!isRunning()) {
-            dispatchAnimationsFinished();
-            return;
-        }
-
-        cancelAll(removeAnimations);
-
-        // We're relying on super.endAnimations() to call dispatchAnimationsFinished() here.
-        super.endAnimations();
-    }
-
-    void cancelAll(List<RecyclerView.ViewHolder> viewHolders) {
-        for (int i = viewHolders.size() - 1; i >= 0; i--) {
-            viewHolders.get(i).itemView.animate().cancel();
-        }
+    protected void animateAddImpl(final RecyclerView.ViewHolder holder) {
+        final View itemView = holder.itemView;
+        ViewCompat.animate(itemView)
+                .setDuration(getAddDuration())
+                .translationX(0)
+                .alpha(1)
+                .setListener(new DefaultAddVpaListener(holder))
+                .start();
     }
 }