Commit 04a7c3fc authored by danielpark's avatar danielpark Committed by Commit Bot

-Adds clip and translation animations for context menu.

-Adds logic to correctly resize on orientation changes.
-Adds logic to set clip bounds on resize
  (i.e. image loads or context menu shows/hides all of the link text).
-Sets rounded corners programmatically instead of in the context menu's xml file.

Note: this does not handle the image loading or link text view enlargement animations.

BUG=730329

Review-Url: https://codereview.chromium.org/2960743002
Cr-Commit-Position: refs/heads/master@{#488506}
parent 2f8de5cd
......@@ -10,7 +10,6 @@
<org.chromium.chrome.browser.contextmenu.TabularContextMenuViewPager
android:id="@+id/custom_pager"
android:background="@drawable/white_with_rounded_corners"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
......
......@@ -4,11 +4,19 @@
package org.chromium.chrome.browser.contextmenu;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.support.v4.view.ViewPager;
import android.support.v4.view.animation.LinearOutSlowInInterpolator;
import android.util.AttributeSet;
import android.view.View;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.chrome.R;
/**
......@@ -16,15 +24,24 @@ import org.chromium.chrome.R;
* pager.
*/
public class TabularContextMenuViewPager extends ViewPager {
private static final int ANIMATION_DURATION_MS = 250;
private final int mContextMenuMinimumPaddingPx =
getResources().getDimensionPixelSize(R.dimen.context_menu_min_padding);
private final Drawable mBackgroundDrawable = ApiCompatibilityUtils.getDrawable(
getResources(), R.drawable.white_with_rounded_corners);
public TabularContextMenuViewPager(Context context) {
super(context);
}
private ValueAnimator mAnimator;
private int mOldHeight;
private int mCanvasWidth;
private int mClipHeight;
private int mDifferenceInHeight;
private int mPreviousChildIndex = 1;
public TabularContextMenuViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
mBackgroundDrawable.mutate();
}
/**
......@@ -33,49 +50,120 @@ public class TabularContextMenuViewPager extends ViewPager {
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int menuHeight = 0;
int tabHeight = 0;
// getCurrentItem() does not take into account the tab layout unlike getChildCount().
int currentItemsIndex = getCurrentItem() + 1;
int menuHeight = 0;
// The width of the context menu is defined so that it leaves space between itself and the
// screen's edges. It is also bounded to a max size to prevent the menu from stretching
// across a large display (e.g. a tablet screen).
int deviceWidthPx = getResources().getDisplayMetrics().widthPixels;
int contextMenuWidth = Math.min(deviceWidthPx - 2 * mContextMenuMinimumPaddingPx,
int appWindowWidthPx = getResources().getDisplayMetrics().widthPixels;
int contextMenuWidth = Math.min(appWindowWidthPx - 2 * mContextMenuMinimumPaddingPx,
getResources().getDimensionPixelSize(R.dimen.context_menu_max_width));
widthMeasureSpec = MeasureSpec.makeMeasureSpec(contextMenuWidth, MeasureSpec.EXACTLY);
// The height of the context menu is calculated as the sum of:
// 1. The tab bar's height, which is only visible when the context menu requires it
// (i.e. an ImageLink is clicked)
// 2. The height of the View being displayed for the current tab.
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
child.measure(
widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int measuredHeight = child.getMeasuredHeight();
// The ViewPager also considers the tab layout one of its children, and needs to be
// treated separately from getting the largest height.
if (child.getId() == R.id.tab_layout && child.getVisibility() != GONE) {
tabHeight = measuredHeight;
} else if (i == currentItemsIndex) {
menuHeight = child.getMeasuredHeight();
break;
// getCurrentItem() returns the index of the current page in the pager's pages.
// It does not take into account the tab layout like getChildCount(), so we add 1.
int currentChildIndex = getCurrentItem() + 1;
// Handles the case where this is called while the pager is scrolling between views.
// The height should remain the same as the height of the last view seen before scrolling.
if (getScrollX() != 0 && getScrollX() != mCanvasWidth) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(mOldHeight, MeasureSpec.EXACTLY);
} else {
// The height of the context menu is calculated as the sum of:
// 1. The tab bar's height, which is only visible when the context menu requires it
// (i.e. an ImageLink is clicked)
// 2. The height of the View being displayed for the current tab.
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
child.measure(
widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int measuredHeight = child.getMeasuredHeight();
// The ViewPager also considers the tab layout one of its children, and needs to be
// treated separately from getting the largest height.
if (child.getId() == R.id.tab_layout && child.getVisibility() != GONE) {
tabHeight = measuredHeight;
} else if (i == currentChildIndex) {
menuHeight = child.getMeasuredHeight();
break;
}
}
int fullHeight = menuHeight + tabHeight;
int appWindowHeightPx = getResources().getDisplayMetrics().heightPixels;
fullHeight = Math.min(fullHeight, appWindowHeightPx - 2 * mContextMenuMinimumPaddingPx);
mDifferenceInHeight = fullHeight - mOldHeight;
if (currentChildIndex == mPreviousChildIndex) {
// Handles the snapping of the view when its height changes
// (i.e. an image finished loading or the link became fully visible).
// The pager will immediately snap to the new height.
mClipHeight = fullHeight;
if (menuHeight != 0) mOldHeight = fullHeight;
heightMeasureSpec = MeasureSpec.makeMeasureSpec(fullHeight, MeasureSpec.EXACTLY);
} else {
// Handles the case where the view pager has completely scrolled to a different
// child. It will measure to the larger height so the clipping is visible.
initAnimator();
heightMeasureSpec = MeasureSpec.makeMeasureSpec(
Math.max(mOldHeight, fullHeight), MeasureSpec.EXACTLY);
}
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mPreviousChildIndex = currentChildIndex;
// The animation only runs when switching to a tab with a different height.
if (mAnimator != null) mAnimator.start();
}
// Cap the height of the context menu so that it fits on the screen without touching the
// screen's edges.
int fullHeight = menuHeight + tabHeight;
int deviceHeightPx = getResources().getDisplayMetrics().heightPixels;
fullHeight = Math.min(fullHeight, deviceHeightPx - 2 * mContextMenuMinimumPaddingPx);
private void initAnimator() {
if (mAnimator != null) return;
mAnimator = ValueAnimator.ofFloat(0f, 1f);
mAnimator.setDuration(ANIMATION_DURATION_MS);
mAnimator.setInterpolator(new LinearOutSlowInInterpolator());
mAnimator.addUpdateListener(new AnimatorUpdateListener() {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(fullHeight, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float animatedValue = (float) animation.getAnimatedValue();
if (mDifferenceInHeight < 0) {
setTranslationY(animatedValue * -mDifferenceInHeight / 2);
} else {
setTranslationY((1 - animatedValue) * mDifferenceInHeight / 2);
}
mClipHeight = mOldHeight + (int) (mDifferenceInHeight * animatedValue);
invalidate();
}
});
mAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mOldHeight = mClipHeight;
setTranslationY(0);
if (mDifferenceInHeight < 0) requestLayout();
}
});
}
@Override
public void onDraw(Canvas canvas) {
mCanvasWidth = canvas.getWidth();
int backgroundOffsetX = getScrollX();
mBackgroundDrawable.setBounds(
backgroundOffsetX, 0, canvas.getWidth() + backgroundOffsetX, mClipHeight);
mBackgroundDrawable.draw(canvas);
boolean clipped = false;
if (mClipHeight != 0) {
canvas.save();
canvas.clipRect(0, 0, mCanvasWidth, mClipHeight);
clipped = true;
}
super.onDraw(canvas);
if (clipped) {
canvas.restore();
}
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment