Merge "Update the BorderAnimator" into udc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
7da60438a3
@@ -25,28 +25,40 @@
|
||||
android:clipToOutline="true"
|
||||
launcher:borderColor="?androidprv:attr/materialColorOutline">
|
||||
|
||||
<include
|
||||
layout="@layout/keyboard_quick_switch_thumbnail"
|
||||
android:id="@+id/thumbnail1"
|
||||
android:layout_width="0dp"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/thumbnail2"/>
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
|
||||
<include
|
||||
layout="@layout/keyboard_quick_switch_thumbnail"
|
||||
android:id="@+id/thumbnail2"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone"
|
||||
android:layout_marginStart="@dimen/keyboard_quick_switch_split_view_spacing"
|
||||
<include
|
||||
layout="@layout/keyboard_quick_switch_thumbnail"
|
||||
android:id="@+id/thumbnail1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/thumbnail1"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/thumbnail2"/>
|
||||
|
||||
<include
|
||||
layout="@layout/keyboard_quick_switch_thumbnail"
|
||||
android:id="@+id/thumbnail2"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone"
|
||||
android:layout_marginStart="@dimen/keyboard_quick_switch_split_view_spacing"
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/thumbnail1"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.android.launcher3.taskbar.KeyboardQuickSwitchTaskView>
|
||||
|
||||
@@ -20,35 +20,47 @@
|
||||
xmlns:launcher="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="@dimen/keyboard_quick_switch_taskview_width"
|
||||
android:layout_height="@dimen/keyboard_quick_switch_taskview_height"
|
||||
android:background="@drawable/keyboard_quick_switch_overview_button_background"
|
||||
android:clipToOutline="true"
|
||||
android:importantForAccessibility="yes"
|
||||
launcher:borderColor="?androidprv:attr/materialColorOutline">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="@dimen/keyboard_quick_switch_recents_icon_size"
|
||||
android:layout_height="@dimen/keyboard_quick_switch_recents_icon_size"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:src="@drawable/ic_empty_recents"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/keyboard_quick_switch_overview_button_background"
|
||||
|
||||
app:tint="?androidprv:attr/materialColorOnSurface"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/text"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
<TextView
|
||||
style="@style/KeyboardQuickSwitchOverview"
|
||||
android:id="@+id/text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="center"
|
||||
|
||||
app:layout_constraintTop_toBottomOf="@id/icon"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="@dimen/keyboard_quick_switch_recents_icon_size"
|
||||
android:layout_height="@dimen/keyboard_quick_switch_recents_icon_size"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:src="@drawable/ic_empty_recents"
|
||||
|
||||
app:tint="?androidprv:attr/materialColorOnSurface"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/text"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
<TextView
|
||||
style="@style/KeyboardQuickSwitchOverview"
|
||||
android:id="@+id/text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="center"
|
||||
|
||||
app:layout_constraintTop_toBottomOf="@id/icon"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.android.launcher3.taskbar.KeyboardQuickSwitchTaskView>
|
||||
|
||||
@@ -25,6 +25,16 @@
|
||||
android:clipToOutline="true"
|
||||
launcher:borderColor="?androidprv:attr/materialColorOutline">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
|
||||
<include
|
||||
layout="@layout/keyboard_quick_switch_thumbnail"
|
||||
android:id="@+id/thumbnail1"
|
||||
@@ -49,4 +59,6 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.android.launcher3.taskbar.KeyboardQuickSwitchTaskView>
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingVertical="@dimen/keyboard_quick_switch_view_spacing"
|
||||
android:layout_marginTop="@dimen/keyboard_quick_switch_margin_top"
|
||||
android:layout_marginHorizontal="@dimen/keyboard_quick_switch_margin_ends"
|
||||
android:background="@drawable/keyboard_quick_switch_view_background"
|
||||
@@ -44,7 +43,9 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/content"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingVertical="@dimen/keyboard_quick_switch_view_spacing"
|
||||
android:clipToPadding="false"/>
|
||||
|
||||
</HorizontalScrollView>
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -45,6 +46,7 @@ public class KeyboardQuickSwitchTaskView extends ConstraintLayout {
|
||||
|
||||
@Nullable private ImageView mThumbnailView1;
|
||||
@Nullable private ImageView mThumbnailView2;
|
||||
@Nullable private View mContent;
|
||||
|
||||
public KeyboardQuickSwitchTaskView(@NonNull Context context) {
|
||||
this(context, null);
|
||||
@@ -84,7 +86,20 @@ public class KeyboardQuickSwitchTaskView extends ConstraintLayout {
|
||||
.getColor(
|
||||
R.styleable.TaskView_borderColor,
|
||||
DEFAULT_BORDER_COLOR),
|
||||
/* invalidateViewCallback= */ KeyboardQuickSwitchTaskView.this::invalidate);
|
||||
/* invalidateViewCallback= */ KeyboardQuickSwitchTaskView.this::invalidate,
|
||||
/* viewScaleTargetProvider= */ new BorderAnimator.ViewScaleTargetProvider() {
|
||||
@NonNull
|
||||
@Override
|
||||
public View getContainerView() {
|
||||
return KeyboardQuickSwitchTaskView.this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public View getContentView() {
|
||||
return mContent;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -93,6 +108,7 @@ public class KeyboardQuickSwitchTaskView extends ConstraintLayout {
|
||||
|
||||
mThumbnailView1 = findViewById(R.id.thumbnail1);
|
||||
mThumbnailView2 = findViewById(R.id.thumbnail2);
|
||||
mContent = findViewById(R.id.content);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
||||
@@ -16,11 +16,13 @@
|
||||
package com.android.quickstep.util;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.annotation.ColorInt;
|
||||
import android.annotation.Nullable;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.view.View;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -34,7 +36,9 @@ import com.android.launcher3.anim.Interpolators;
|
||||
* Utility class for drawing a rounded-rect border around a view.
|
||||
* <p>
|
||||
* To use this class:
|
||||
* 1. Create an instance in the target view.
|
||||
* 1. Create an instance in the target view. NOTE: The border will animate outwards from the
|
||||
* provided border bounds. If the border will not be visible outside of those bounds, then a
|
||||
* {@link ViewScaleTargetProvider} must be provided in the constructor.
|
||||
* 2. Override the target view's {@link android.view.View#draw(Canvas)} method and call
|
||||
* {@link BorderAnimator#drawBorder(Canvas)} after {@code super.draw(canvas)}.
|
||||
* 3. Call {@link BorderAnimator#buildAnimator(boolean)} and start the animation or call
|
||||
@@ -55,6 +59,7 @@ public final class BorderAnimator {
|
||||
@Px private final int mBorderWidthPx;
|
||||
@Px private final int mBorderRadiusPx;
|
||||
@NonNull private final Runnable mInvalidateViewCallback;
|
||||
@Nullable private final ViewScaleTargetProvider mViewScaleTargetProvider;
|
||||
private final long mAppearanceDurationMs;
|
||||
private final long mDisappearanceDurationMs;
|
||||
@NonNull private final Interpolator mInterpolator;
|
||||
@@ -75,6 +80,22 @@ public final class BorderAnimator {
|
||||
borderRadiusPx,
|
||||
borderColor,
|
||||
invalidateViewCallback,
|
||||
/* viewScaleTargetProvider= */ null);
|
||||
}
|
||||
|
||||
public BorderAnimator(
|
||||
@NonNull BorderBoundsBuilder borderBoundsBuilder,
|
||||
int borderWidthPx,
|
||||
int borderRadiusPx,
|
||||
@ColorInt int borderColor,
|
||||
@NonNull Runnable invalidateViewCallback,
|
||||
@Nullable ViewScaleTargetProvider viewScaleTargetProvider) {
|
||||
this(borderBoundsBuilder,
|
||||
borderWidthPx,
|
||||
borderRadiusPx,
|
||||
borderColor,
|
||||
invalidateViewCallback,
|
||||
viewScaleTargetProvider,
|
||||
DEFAULT_APPEARANCE_ANIMATION_DURATION_MS,
|
||||
DEFAULT_DISAPPEARANCE_ANIMATION_DURATION_MS,
|
||||
DEFAULT_INTERPOLATOR);
|
||||
@@ -86,6 +107,7 @@ public final class BorderAnimator {
|
||||
int borderRadiusPx,
|
||||
@ColorInt int borderColor,
|
||||
@NonNull Runnable invalidateViewCallback,
|
||||
@Nullable ViewScaleTargetProvider viewScaleTargetProvider,
|
||||
long appearanceDurationMs,
|
||||
long disappearanceDurationMs,
|
||||
@NonNull Interpolator interpolator) {
|
||||
@@ -93,6 +115,7 @@ public final class BorderAnimator {
|
||||
mBorderWidthPx = borderWidthPx;
|
||||
mBorderRadiusPx = borderRadiusPx;
|
||||
mInvalidateViewCallback = invalidateViewCallback;
|
||||
mViewScaleTargetProvider = viewScaleTargetProvider;
|
||||
mAppearanceDurationMs = appearanceDurationMs;
|
||||
mDisappearanceDurationMs = disappearanceDurationMs;
|
||||
mInterpolator = interpolator;
|
||||
@@ -106,8 +129,10 @@ public final class BorderAnimator {
|
||||
float interpolatedProgress = mInterpolator.getInterpolation(
|
||||
mBorderAnimationProgress.value);
|
||||
float borderWidth = mBorderWidthPx * interpolatedProgress;
|
||||
// Inset the border by half the width to create an inwards-growth animation
|
||||
mAlignmentAdjustment = borderWidth / 2f;
|
||||
// Outset the border by half the width to create an outwards-growth animation
|
||||
mAlignmentAdjustment = (-borderWidth / 2f)
|
||||
// Inset the border if we are scaling the container up
|
||||
+ (mViewScaleTargetProvider == null ? 0 : mBorderWidthPx);
|
||||
|
||||
mBorderPaint.setAlpha(Math.round(255 * interpolatedProgress));
|
||||
mBorderPaint.setStrokeWidth(borderWidth);
|
||||
@@ -121,13 +146,16 @@ public final class BorderAnimator {
|
||||
* calling super.
|
||||
*/
|
||||
public void drawBorder(Canvas canvas) {
|
||||
// Increase the radius if we are scaling the container up
|
||||
float radiusAdjustment = mViewScaleTargetProvider == null
|
||||
? -mAlignmentAdjustment : mAlignmentAdjustment;
|
||||
canvas.drawRoundRect(
|
||||
/* left= */ mBorderBounds.left + mAlignmentAdjustment,
|
||||
/* top= */ mBorderBounds.top + mAlignmentAdjustment,
|
||||
/* right= */ mBorderBounds.right - mAlignmentAdjustment,
|
||||
/* bottom= */ mBorderBounds.bottom - mAlignmentAdjustment,
|
||||
/* rx= */ mBorderRadiusPx - mAlignmentAdjustment,
|
||||
/* ry= */ mBorderRadiusPx - mAlignmentAdjustment,
|
||||
/* rx= */ mBorderRadiusPx + radiusAdjustment,
|
||||
/* ry= */ mBorderRadiusPx + radiusAdjustment,
|
||||
/* paint= */ mBorderPaint);
|
||||
}
|
||||
|
||||
@@ -141,22 +169,81 @@ public final class BorderAnimator {
|
||||
mRunningBorderAnimation.setDuration(
|
||||
isAppearing ? mAppearanceDurationMs : mDisappearanceDurationMs);
|
||||
|
||||
mRunningBorderAnimation.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
setViewScales();
|
||||
}
|
||||
});
|
||||
mRunningBorderAnimation.addListener(
|
||||
AnimatorListeners.forEndCallback(() -> mRunningBorderAnimation = null));
|
||||
AnimatorListeners.forEndCallback(() -> {
|
||||
mRunningBorderAnimation = null;
|
||||
if (isAppearing) {
|
||||
return;
|
||||
}
|
||||
resetViewScales();
|
||||
}));
|
||||
|
||||
return mRunningBorderAnimation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Immediately shows/hides the border without an animation.
|
||||
*
|
||||
* <p>
|
||||
* To animate the appearance/disappearance, see {@link BorderAnimator#buildAnimator(boolean)}
|
||||
*/
|
||||
public void setBorderVisible(boolean visible) {
|
||||
if (mRunningBorderAnimation != null) {
|
||||
mRunningBorderAnimation.end();
|
||||
}
|
||||
mBorderBoundsBuilder.updateBorderBounds(mBorderBounds);
|
||||
if (visible) {
|
||||
setViewScales();
|
||||
}
|
||||
mBorderAnimationProgress.updateValue(visible ? 1f : 0f);
|
||||
if (!visible) {
|
||||
resetViewScales();
|
||||
}
|
||||
}
|
||||
|
||||
private void setViewScales() {
|
||||
if (mViewScaleTargetProvider == null) {
|
||||
return;
|
||||
}
|
||||
View container = mViewScaleTargetProvider.getContainerView();
|
||||
float width = container.getWidth();
|
||||
float height = container.getHeight();
|
||||
// scale up just enough to make room for the border
|
||||
float scaleX = 1f + ((2 * mBorderWidthPx) / width);
|
||||
float scaleY = 1f + ((2 * mBorderWidthPx) / height);
|
||||
|
||||
container.setPivotX(width / 2);
|
||||
container.setPivotY(height / 2);
|
||||
container.setScaleX(scaleX);
|
||||
container.setScaleY(scaleY);
|
||||
|
||||
View contentView = mViewScaleTargetProvider.getContentView();
|
||||
contentView.setPivotX(contentView.getWidth() / 2f);
|
||||
contentView.setPivotY(contentView.getHeight() / 2f);
|
||||
contentView.setScaleX(1f / scaleX);
|
||||
contentView.setScaleY(1f / scaleY);
|
||||
}
|
||||
|
||||
private void resetViewScales() {
|
||||
if (mViewScaleTargetProvider == null) {
|
||||
return;
|
||||
}
|
||||
View container = mViewScaleTargetProvider.getContainerView();
|
||||
container.setPivotX(container.getWidth());
|
||||
container.setPivotY(container.getHeight());
|
||||
container.setScaleX(1f);
|
||||
container.setScaleY(1f);
|
||||
|
||||
View contentView = mViewScaleTargetProvider.getContentView();
|
||||
contentView.setPivotX(contentView.getWidth() / 2f);
|
||||
contentView.setPivotY(contentView.getHeight() / 2f);
|
||||
contentView.setScaleX(1f);
|
||||
contentView.setScaleY(1f);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,4 +256,25 @@ public final class BorderAnimator {
|
||||
*/
|
||||
void updateBorderBounds(Rect rect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider for scaling target views for the beginning and end of this animation.
|
||||
*/
|
||||
public interface ViewScaleTargetProvider {
|
||||
|
||||
/**
|
||||
* Returns the content view's container. This view will be scaled up to make room for the
|
||||
* border.
|
||||
*/
|
||||
@NonNull
|
||||
View getContainerView();
|
||||
|
||||
/**
|
||||
* Returns the content view. This view will be scaled down reciprocally to the container's
|
||||
* up-scaling to maintain its original size. This should be the view containing all of the
|
||||
* content being surrounded by the border.
|
||||
*/
|
||||
@NonNull
|
||||
View getContentView();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user