mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1055606 - Animate tab strip when tabs added added or removed (r=mcomella)
This commit is contained in:
parent
714140e316
commit
bb787d958f
@ -82,9 +82,7 @@ public class TabStrip extends ThemedLinearLayout {
|
|||||||
switch (msg) {
|
switch (msg) {
|
||||||
case RESTORED:
|
case RESTORED:
|
||||||
case ADDED:
|
case ADDED:
|
||||||
// Refresh the list to make sure the new tab is
|
tabStripView.addTab(tab);
|
||||||
// added in the right position.
|
|
||||||
tabStripView.refreshTabs();
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CLOSED:
|
case CLOSED:
|
||||||
|
@ -11,11 +11,16 @@ import android.graphics.Canvas;
|
|||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.ViewTreeObserver;
|
import android.view.ViewTreeObserver;
|
||||||
import android.view.ViewTreeObserver.OnPreDrawListener;
|
import android.view.ViewTreeObserver.OnPreDrawListener;
|
||||||
|
|
||||||
|
import com.nineoldandroids.animation.Animator;
|
||||||
|
import com.nineoldandroids.animation.AnimatorSet;
|
||||||
|
import com.nineoldandroids.animation.ObjectAnimator;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -27,6 +32,10 @@ import org.mozilla.gecko.widget.TwoWayView;
|
|||||||
public class TabStripView extends TwoWayView {
|
public class TabStripView extends TwoWayView {
|
||||||
private static final String LOGTAG = "GeckoTabStrip";
|
private static final String LOGTAG = "GeckoTabStrip";
|
||||||
|
|
||||||
|
private static final int ANIM_TIME_MS = 200;
|
||||||
|
private static final AccelerateDecelerateInterpolator ANIM_INTERPOLATOR =
|
||||||
|
new AccelerateDecelerateInterpolator();
|
||||||
|
|
||||||
private final TabStripAdapter adapter;
|
private final TabStripAdapter adapter;
|
||||||
private final Drawable divider;
|
private final Drawable divider;
|
||||||
|
|
||||||
@ -71,14 +80,110 @@ public class TabStripView extends TwoWayView {
|
|||||||
setItemChecked(selected, true);
|
setItemChecked(selected, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateSelectedPosition() {
|
private void updateSelectedPosition(boolean ensureVisible) {
|
||||||
final int selected = getPositionForSelectedTab();
|
final int selected = getPositionForSelectedTab();
|
||||||
if (selected != -1) {
|
if (selected != -1) {
|
||||||
updateSelectedStyle(selected);
|
updateSelectedStyle(selected);
|
||||||
ensurePositionIsVisible(selected);
|
|
||||||
|
if (ensureVisible) {
|
||||||
|
ensurePositionIsVisible(selected);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void animateRemoveTab(Tab removedTab) {
|
||||||
|
final int removedPosition = adapter.getPositionForTab(removedTab);
|
||||||
|
|
||||||
|
final View removedView = getViewForTab(removedTab);
|
||||||
|
|
||||||
|
// The removed position might not have a matching child view
|
||||||
|
// when it's not within the visible range of positions in the strip.
|
||||||
|
if (removedView == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't animate the removed child view (it just disappears)
|
||||||
|
// but we still need its size of animate all affected children
|
||||||
|
// within the visible viewport.
|
||||||
|
final int removedSize = removedView.getWidth() + getItemMargin();
|
||||||
|
|
||||||
|
getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreDraw() {
|
||||||
|
getViewTreeObserver().removeOnPreDrawListener(this);
|
||||||
|
|
||||||
|
final int firstPosition = getFirstVisiblePosition();
|
||||||
|
final List<Animator> childAnimators = new ArrayList<Animator>();
|
||||||
|
|
||||||
|
final int childCount = getChildCount();
|
||||||
|
for (int i = removedPosition - firstPosition; i < childCount; i++) {
|
||||||
|
final View child = getChildAt(i);
|
||||||
|
|
||||||
|
// TODO: optimize with Valueresolver
|
||||||
|
final ObjectAnimator animator =
|
||||||
|
ObjectAnimator.ofFloat(child, "translationX", removedSize, 0);
|
||||||
|
childAnimators.add(animator);
|
||||||
|
}
|
||||||
|
|
||||||
|
final AnimatorSet animatorSet = new AnimatorSet();
|
||||||
|
animatorSet.playTogether(childAnimators);
|
||||||
|
animatorSet.setDuration(ANIM_TIME_MS);
|
||||||
|
animatorSet.setInterpolator(ANIM_INTERPOLATOR);
|
||||||
|
animatorSet.start();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void animateNewTab(Tab newTab) {
|
||||||
|
final int newPosition = adapter.getPositionForTab(newTab);
|
||||||
|
if (newPosition < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreDraw() {
|
||||||
|
getViewTreeObserver().removeOnPreDrawListener(this);
|
||||||
|
|
||||||
|
final int firstPosition = getFirstVisiblePosition();
|
||||||
|
|
||||||
|
final View newChild = getChildAt(newPosition - firstPosition);
|
||||||
|
if (newChild == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<Animator> childAnimators = new ArrayList<Animator>();
|
||||||
|
childAnimators.add(
|
||||||
|
ObjectAnimator.ofFloat(newChild, "translationY", newChild.getHeight(), 0));
|
||||||
|
|
||||||
|
// This will momentaneously add a gap on the right side
|
||||||
|
// because TwoWayView doesn't provide APIs to control
|
||||||
|
// view recycling programatically to handle these transitory
|
||||||
|
// states in the container during animations.
|
||||||
|
|
||||||
|
final int tabSize = newChild.getWidth();
|
||||||
|
final int newIndex = newPosition - firstPosition;
|
||||||
|
final int childCount = getChildCount();
|
||||||
|
for (int i = newIndex + 1; i < childCount; i++) {
|
||||||
|
final View child = getChildAt(i);
|
||||||
|
|
||||||
|
childAnimators.add(
|
||||||
|
ObjectAnimator.ofFloat(child, "translationX", -tabSize, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
final AnimatorSet animatorSet = new AnimatorSet();
|
||||||
|
animatorSet.playTogether(childAnimators);
|
||||||
|
animatorSet.setDuration(ANIM_TIME_MS);
|
||||||
|
animatorSet.setInterpolator(ANIM_INTERPOLATOR);
|
||||||
|
animatorSet.start();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void ensurePositionIsVisible(final int position) {
|
private void ensurePositionIsVisible(final int position) {
|
||||||
getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
|
getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
|
||||||
@Override
|
@Override
|
||||||
@ -111,16 +216,24 @@ public class TabStripView extends TwoWayView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
adapter.refresh(tabs);
|
adapter.refresh(tabs);
|
||||||
updateSelectedPosition();
|
updateSelectedPosition(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void clearTabs() {
|
void clearTabs() {
|
||||||
adapter.clear();
|
adapter.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void addTab(Tab tab) {
|
||||||
|
// Refresh the list to make sure the new tab is
|
||||||
|
// added in the right position.
|
||||||
|
refreshTabs();
|
||||||
|
animateNewTab(tab);
|
||||||
|
}
|
||||||
|
|
||||||
void removeTab(Tab tab) {
|
void removeTab(Tab tab) {
|
||||||
|
animateRemoveTab(tab);
|
||||||
adapter.removeTab(tab);
|
adapter.removeTab(tab);
|
||||||
updateSelectedPosition();
|
updateSelectedPosition(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void selectTab(Tab tab) {
|
void selectTab(Tab tab) {
|
||||||
@ -128,7 +241,7 @@ public class TabStripView extends TwoWayView {
|
|||||||
isPrivate = tab.isPrivate();
|
isPrivate = tab.isPrivate();
|
||||||
refreshTabs();
|
refreshTabs();
|
||||||
} else {
|
} else {
|
||||||
updateSelectedPosition();
|
updateSelectedPosition(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user