зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1097121 - Animate items being removed from the tabs panel grid (r=lucasr)
This commit is contained in:
Родитель
5d4cc314af
Коммит
63f1621378
|
@ -6,6 +6,7 @@
|
|||
package org.mozilla.gecko.tabs;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.mozilla.gecko.animation.ViewHelper;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
|
@ -18,13 +19,22 @@ import org.mozilla.gecko.Tabs;
|
|||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.PointF;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.widget.GridView;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.widget.Button;
|
||||
import android.widget.GridView;
|
||||
import com.nineoldandroids.animation.Animator;
|
||||
import com.nineoldandroids.animation.AnimatorSet;
|
||||
import com.nineoldandroids.animation.ObjectAnimator;
|
||||
import com.nineoldandroids.animation.PropertyValuesHolder;
|
||||
import com.nineoldandroids.animation.ValueAnimator;
|
||||
|
||||
|
||||
/**
|
||||
* A tabs layout implementation for the tablet redesign (bug 1014156).
|
||||
|
@ -36,12 +46,18 @@ class TabsGridLayout extends GridView
|
|||
Tabs.OnTabsChangedListener {
|
||||
private static final String LOGTAG = "Gecko" + TabsGridLayout.class.getSimpleName();
|
||||
|
||||
private static final int ANIM_TIME_MS = 200;
|
||||
public static final int ANIM_DELAY_MULTIPLE_MS = 20;
|
||||
private static final DecelerateInterpolator ANIM_INTERPOLATOR = new DecelerateInterpolator();
|
||||
|
||||
private final Context mContext;
|
||||
private TabsPanel mTabsPanel;
|
||||
private final SparseArray<PointF> mTabLocations = new SparseArray<PointF>();
|
||||
|
||||
final private boolean mIsPrivate;
|
||||
|
||||
private final TabsLayoutAdapter mTabsAdapter;
|
||||
private final int mColumnWidth;
|
||||
|
||||
public TabsGridLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs, R.attr.tabGridLayoutViewStyle);
|
||||
|
@ -67,9 +83,13 @@ class TabsGridLayout extends GridView
|
|||
setGravity(Gravity.CENTER);
|
||||
setNumColumns(GridView.AUTO_FIT);
|
||||
|
||||
// The clipToPadding setting in the styles.xml doesn't seem to be working (bug 1101784)
|
||||
// so lets set it manually in code for the moment as it's needed for the padding animation
|
||||
setClipToPadding(false);
|
||||
|
||||
final Resources resources = getResources();
|
||||
final int columnWidth = resources.getDimensionPixelSize(R.dimen.new_tablet_tab_panel_column_width);
|
||||
setColumnWidth(columnWidth);
|
||||
mColumnWidth = resources.getDimensionPixelSize(R.dimen.new_tablet_tab_panel_column_width);
|
||||
setColumnWidth(mColumnWidth);
|
||||
|
||||
final int padding = resources.getDimensionPixelSize(R.dimen.new_tablet_tab_panel_grid_padding);
|
||||
final int paddingTop = resources.getDimensionPixelSize(R.dimen.new_tablet_tab_panel_grid_padding_top);
|
||||
|
@ -87,9 +107,7 @@ class TabsGridLayout extends GridView
|
|||
mCloseClickListener = new Button.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
TabsLayoutItemView itemView = (TabsLayoutItemView) v.getTag();
|
||||
Tab tab = Tabs.getInstance().getTab(itemView.getTabId());
|
||||
Tabs.getInstance().closeTab(tab);
|
||||
closeTab(v);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -121,6 +139,47 @@ class TabsGridLayout extends GridView
|
|||
}
|
||||
}
|
||||
|
||||
private void populateTabLocations(final Tab removedTab) {
|
||||
mTabLocations.clear();
|
||||
|
||||
final int firstPosition = getFirstVisiblePosition();
|
||||
final int lastPosition = getLastVisiblePosition();
|
||||
final int numberOfColumns = getNumColumns();
|
||||
final int childCount = getChildCount();
|
||||
final int removedPosition = mTabsAdapter.getPositionForTab(removedTab);
|
||||
|
||||
for (int x = 1, i = (removedPosition - firstPosition) + 1; i < childCount; i++, x++) {
|
||||
final View child = getChildAt(i);
|
||||
if (child != null) {
|
||||
mTabLocations.append(x, new PointF(child.getX(), child.getY()));
|
||||
}
|
||||
}
|
||||
|
||||
final boolean firstChildOffScreen = ((firstPosition > 0) || getChildAt(0).getY() < 0);
|
||||
final boolean lastChildVisible = (lastPosition - childCount == firstPosition - 1);
|
||||
final boolean oneItemOnLastRow = (lastPosition % numberOfColumns == 0);
|
||||
if (firstChildOffScreen && lastChildVisible && oneItemOnLastRow) {
|
||||
// We need to set the view's bottom padding to prevent a sudden jump as the
|
||||
// last item in the row is being removed. We then need to remove the padding
|
||||
// via a sweet animation
|
||||
|
||||
final int removedHeight = getChildAt(0).getMeasuredHeight();
|
||||
final int verticalSpacing = getVerticalSpacing();
|
||||
|
||||
ValueAnimator paddingAnimator = ValueAnimator.ofInt(getPaddingBottom() + removedHeight + verticalSpacing, getPaddingBottom());
|
||||
paddingAnimator.setDuration(ANIM_TIME_MS * 2);
|
||||
|
||||
paddingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), (Integer) animation.getAnimatedValue());
|
||||
}
|
||||
});
|
||||
paddingAnimator.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTabsPanel(TabsPanel panel) {
|
||||
mTabsPanel = panel;
|
||||
|
@ -160,6 +219,9 @@ class TabsGridLayout extends GridView
|
|||
break;
|
||||
|
||||
case CLOSED:
|
||||
if(mTabsAdapter.getCount() > 0) {
|
||||
animateRemoveTab(tab);
|
||||
}
|
||||
if (tab.isPrivate() == mIsPrivate && mTabsAdapter.getCount() > 0) {
|
||||
if (mTabsAdapter.removeTab(tab)) {
|
||||
int selected = mTabsAdapter.getPositionForTab(Tabs.getInstance().getSelectedTab());
|
||||
|
@ -244,4 +306,90 @@ class TabsGridLayout extends GridView
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private View getViewForTab(Tab tab) {
|
||||
final int position = mTabsAdapter.getPositionForTab(tab);
|
||||
return getChildAt(position - getFirstVisiblePosition());
|
||||
}
|
||||
|
||||
void closeTab(View v) {
|
||||
TabsLayoutItemView itemView = (TabsLayoutItemView) v.getTag();
|
||||
Tab tab = Tabs.getInstance().getTab(itemView.getTabId());
|
||||
|
||||
Tabs.getInstance().closeTab(tab);
|
||||
updateSelectedPosition();
|
||||
}
|
||||
|
||||
private void animateRemoveTab(final Tab removedTab) {
|
||||
final int removedPosition = mTabsAdapter.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;
|
||||
}
|
||||
final int removedHeight = removedView.getMeasuredHeight();
|
||||
|
||||
populateTabLocations(removedTab);
|
||||
|
||||
getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
|
||||
@Override
|
||||
public boolean onPreDraw() {
|
||||
getViewTreeObserver().removeOnPreDrawListener(this);
|
||||
// We don't animate the removed child view (it just disappears)
|
||||
// but we still need its size to animate all affected children
|
||||
// within the visible viewport.
|
||||
final int childCount = getChildCount();
|
||||
final int firstPosition = getFirstVisiblePosition();
|
||||
final int numberOfColumns = getNumColumns();
|
||||
|
||||
final List<Animator> childAnimators = new ArrayList<>();
|
||||
|
||||
PropertyValuesHolder translateX, translateY;
|
||||
for (int x = 0, i = removedPosition - firstPosition ; i < childCount; i++, x++) {
|
||||
final View child = getChildAt(i);
|
||||
ObjectAnimator animator;
|
||||
|
||||
if (i % numberOfColumns == numberOfColumns - 1) {
|
||||
// Animate X & Y
|
||||
translateX = PropertyValuesHolder.ofFloat("translationX", -(mColumnWidth * numberOfColumns), 0);
|
||||
translateY = PropertyValuesHolder.ofFloat("translationY", removedHeight, 0);
|
||||
animator = ObjectAnimator.ofPropertyValuesHolder(child, translateX, translateY);
|
||||
} else {
|
||||
// Just animate X
|
||||
translateX = PropertyValuesHolder.ofFloat("translationX", mColumnWidth, 0);
|
||||
animator = ObjectAnimator.ofPropertyValuesHolder(child, translateX);
|
||||
}
|
||||
animator.setStartDelay(x * ANIM_DELAY_MULTIPLE_MS);
|
||||
childAnimators.add(animator);
|
||||
}
|
||||
|
||||
final AnimatorSet animatorSet = new AnimatorSet();
|
||||
animatorSet.playTogether(childAnimators);
|
||||
animatorSet.setDuration(ANIM_TIME_MS);
|
||||
animatorSet.setInterpolator(ANIM_INTERPOLATOR);
|
||||
animatorSet.start();
|
||||
|
||||
// Set the starting position of the child views - because we are delaying the start
|
||||
// of the animation, we need to prevent the items being drawn in their final position
|
||||
// prior to the animation starting
|
||||
for (int x = 1, i = (removedPosition - firstPosition) + 1; i < childCount; i++, x++) {
|
||||
final View child = getChildAt(i);
|
||||
|
||||
final PointF targetLocation = mTabLocations.get(x+1);
|
||||
if (targetLocation == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
child.setX(targetLocation.x);
|
||||
child.setY(targetLocation.y);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче