在 parent 之外对 child 视图进行动画处理
Animating child view outside of parent
我正在尝试为 parent 视图之外的视图设置动画,当我这样做时 child 视图无法在其 parent 之外设置动画。我通过使用 setClipChildren(false)
解决了这个问题并且它起作用了......当视图被动画 up 时。当我为视图 down 设置动画时,图像仍然隐藏。
这是有效的代码。此代码将使平铺按钮动画化到屏幕顶部:
private void setGameBoard(){
brickWall.setClipChildren(false);
brickWall.setClipToPadding(false);
//Build game board
for(int ii = 0; ii < brickRows;ii++){
final int x = ii;
//Build table rows
row = new TableRow(this.getApplicationContext());
row.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, 50));
row.setClipChildren(false);
row.setClipToPadding(false);
// row.setBackgroundColor(ResourcesCompat.getColor(getResources(), R.color.colorAccent, null));
//Build table tiles
for(int jj=0; jj < brickColumns; jj++){
final int y = jj;
final Brick tile = new Brick(this);
tile.setClipBounds(null);
tile.setBackgroundColor(ResourcesCompat.getColor(getResources(), R.color.colorPrimary, null));
//Set margins to create look of tic-tac-toe
TableRow.LayoutParams lp = new TableRow.LayoutParams(
150, 75);
lp.setMargins(0,0,0,0);
//lp.weight = 1;
tile.setLayoutParams(lp);
tile.setWidth(3);
tile.setHeight(10);
tile.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(tile.getHits() == 0){
tile.setBackgroundColor(ResourcesCompat.getColor(getResources(),
R.color.colorGreen, null));
tile.addHit();
} else if (tile.getHits() == 1){
tile.setBackgroundColor(ResourcesCompat.getColor(getResources(),
R.color.colorYellow, null));
tile.addHit();
}else if(tile.getHits() == 2){
brokenBricks++;
float bottomOfScreen = getResources().getDisplayMetrics()
.heightPixels;
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(tile, "translationY",
-2000);
// 2
objectAnimator.setDuration(2000);
tile.setHapticFeedbackEnabled(true);
objectAnimator.start();
//tile.setVisibility(View.INVISIBLE);
if(isRevealComplete()){
brickWall.setVisibility(View.INVISIBLE);
}
}
}
});
row.addView(tile);
}
brickWall.addView(row);
}
}
但是当我调整视图到屏幕底部的位置时,它下面的视图是 "swallowed" 并且当它到达 parent 视图的底部时被隐藏:
private void setGameBoard(){
brickWall.setClipChildren(false);
brickWall.setClipToPadding(false);
//Build game board
for(int ii = 0; ii < brickRows;ii++){
final int x = ii;
//Build table rows
row = new TableRow(this.getApplicationContext());
row.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, 50));
row.setClipChildren(false);
row.setClipToPadding(false);
// row.setBackgroundColor(ResourcesCompat.getColor(getResources(), R.color.colorAccent, null));
//Build table tiles
for(int jj=0; jj < brickColumns; jj++){
final int y = jj;
final Brick tile = new Brick(this);
tile.setClipBounds(null);
tile.setBackgroundColor(ResourcesCompat.getColor(getResources(), R.color.colorPrimary, null));
//Set margins to create look of tic-tac-toe
TableRow.LayoutParams lp = new TableRow.LayoutParams(
150, 75);
lp.setMargins(0,0,0,0);
//lp.weight = 1;
tile.setLayoutParams(lp);
tile.setWidth(3);
tile.setHeight(10);
tile.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(tile.getHits() == 0){
tile.setBackgroundColor(ResourcesCompat.getColor(getResources(),
R.color.colorGreen, null));
tile.addHit();
} else if (tile.getHits() == 1){
tile.setBackgroundColor(ResourcesCompat.getColor(getResources(),
R.color.colorYellow, null));
tile.addHit();
}else if(tile.getHits() == 2){
brokenBricks++;
float bottomOfScreen = getResources().getDisplayMetrics()
.heightPixels;
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(tile, "translationY",
2000);
objectAnimator.setDuration(2000);
tile.setHapticFeedbackEnabled(true);
objectAnimator.start();
//tile.setVisibility(View.INVISIBLE);
if(isRevealComplete()){
brickWall.setVisibility(View.INVISIBLE);
}
}
}
});
row.addView(tile);
}
brickWall.addView(row);
}
}
所以我的问题是:如何在 child 视图下方的 parent 视图之外为 child 视图设置动画?
更新 1
如果我移除我试图放下的瓷砖下方的瓷砖,那么我能够看到所需的效果,直到它下面有另一个瓷砖,此时掉落的瓷砖将 "behind"仍然在那里的瓷砖。那么如何让掉落的瓷砖移动到其下方的 children 之上呢?
更新 2
我注意到的一件新事是,如果我向左或向上移动按钮,它会正常工作;但是,如果将它向下或向右移动,它就会落后于其他视图。这使我相信在当前图块之后创建的按钮具有不同的效果。
好的,所以在我建议的替代方法中,我会使用一个助手 class FlyOverView
获取任何视图的 "photo" 然后将其动画化到任何所需的坐标。一旦动画 运行,您就可以隐藏/删除原始视图,因为在屏幕上移动的只是 canvas 上的图像。您不必担心剪裁其他视图等问题。
您需要在积木系统的外部容器上声明此 FlyOverView
,并且该布局必须覆盖您希望动画可见的整个区域。所以我建议使用以下内容作为你的根容器,它只是一个 FrameLayout
你可以在它的子容器上绘制东西的地方。
package whatever;
public class GameRootLayout extends FrameLayout {
// the currently-animating effect, if any
private FlyOverView mFlyOverView;
public GameRootLayout(Context context, AttributeSet attrs) {
super(context,attrs);
}
@Override
public void dispatchDraw(Canvas canvas) {
// draw the children
super.dispatchDraw(canvas);
// draw our stuff
if (mFlyOverView != null) {
mFlyOverView.delegate_draw(canvas);
}
}
/**
* Starts a flyover animation for the specified view.
* It will "fly" to the desired position with an alpha / translation / rotation effect
*
* @param viewToFly The view to fly
* @param targetX The target X coordinate
* @param targetY The target Y coordinate
*/
public void addFlyOver(View viewToFly, @Px int targetX, @Px int targetY) {
if (mFlyOverView != null) mFlyOverView.cancel();
mFlyOverView = new FlyOverView(this, viewToFly, targetX, targetY, new FlyOverView.OnFlyOverFinishedListener() {
@Override
public void onFlyOverFinishedListener() {
mFlyOverView = null;
}
});
}
}
您可以用作容器
<whatever.GameRootLayout
android:id="@+id/gameRootLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
.
.
</whatever.GameRootLayout>
然后是 FlyOverView 本身:
package com.regaliz.gui.fx;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.util.Log;
import android.view.View;
import android.support.annotation.Px;
import android.view.animation.AccelerateDecelerateInterpolator;
/**
* Neat animation that captures a layer bitmap and "flies" it to the corner of the screen, used to create
* an "added to playlist" effect or something like that. The method delegate_draw() must be called from the
* parent view to update the animation
*
* @author rupps'2014
* license public domain, attribution appreciated
*/
@SuppressWarnings("unused")
public class FlyOverView {
public static final int DEFAULT_DURATION = 1000;
private static final String TAG = "FlyOverView";
private static final boolean LOG_ON = false;
private ObjectAnimator mFlyoverAnimation;
private float mCurrentX, mCurrentY;
private Matrix mMatrix = new Matrix();
private Bitmap mBitmap;
private Paint mPaint = new Paint();
private View mParentView = null;
private OnFlyOverFinishedListener mOnFlyOverFinishedListener;
public interface OnFlyOverFinishedListener {
void onFlyOverFinishedListener();
}
/**
* Creates the FlyOver effect
*
* @param parent Container View to invalidate. That view has to call this class' delegate_draw() in its dispatchDraw().
* @param viewToFly Target View to animate
* @param finalX Final X coordinate
* @param finalY Final Y coordinate
*/
public FlyOverView(View parent, View viewToFly, @Px int finalX, @Px int finalY, OnFlyOverFinishedListener listener) {
setupFlyOver(parent, viewToFly, finalX, finalY, DEFAULT_DURATION, listener);
}
/**
* Creates the FlyOver effect
*
* @param parent Container View to invalidate. That view has to call this class' delegate_draw() in its dispatchDraw().
* @param viewToFly Target View to animate
* @param finalX Final X coordinate
* @param finalY Final Y coordinate
* @param duration Animation duration
*/
public FlyOverView(View parent, View viewToFly, @Px int finalX, @Px int finalY, int duration, OnFlyOverFinishedListener listener) {
setupFlyOver(parent, viewToFly, finalX, finalY, duration, listener);
}
/**
* cancels current animation from the outside
*/
public void cancel() {
if (mFlyoverAnimation != null) mFlyoverAnimation.cancel();
}
private void setupFlyOver(View parentContainer, View viewToFly, @Px int finalX, @Px int finalY, int duration, OnFlyOverFinishedListener listener) {
int[] location = new int[2];
mParentView = parentContainer;
mOnFlyOverFinishedListener = listener;
viewToFly.getLocationInWindow(location);
int
sourceX = location[0],
sourceY = location[1];
if (LOG_ON) Log.v(TAG, "FlyOverView, item " + viewToFly+", finals "+finalX+", "+finalY+", sources "+sourceX+", "+sourceY+ " duration "+duration);
/* Animation definition table */
mFlyoverAnimation = ObjectAnimator.ofPropertyValuesHolder(
this,
PropertyValuesHolder.ofFloat("translationX", sourceX, finalX),
PropertyValuesHolder.ofFloat("translationY", sourceY, finalY),
PropertyValuesHolder.ofFloat("scaleAlpha", 1, 0.2f) // not to 0 so we see the end of the effect in other properties
);
mFlyoverAnimation.setDuration(duration);
mFlyoverAnimation.setRepeatCount(0);
mFlyoverAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
mFlyoverAnimation.addListener(new SimpleAnimationListener() {
@Override
public void onAnimationEnd(Animator animation) {
if (LOG_ON) Log.v(TAG, "FlyOver: End");
mParentView.invalidate();
if (mBitmap != null) mBitmap.recycle(); // just for safety
mBitmap = null;
mOnFlyOverFinishedListener.onFlyOverFinishedListener();
}
});
// take snapshot of viewToFly
viewToFly.setDrawingCacheEnabled(true);
mBitmap = Bitmap.createBitmap(viewToFly.getDrawingCache());
viewToFly.setDrawingCacheEnabled(false);
mFlyoverAnimation.start();
}
// ANIMATOR setter
public void setTranslationX(float position) {
mCurrentX = position;
}
// ANIMATOR setter
public void setTranslationY(float position) {
mCurrentY = position;
}
// ANIMATOR setter
// as this will be called in every iteration, we set here all parameters at once then call invalidate,
// rather than separately
public void setScaleAlpha(float position) {
mPaint.setAlpha((int) (100 * position));
mMatrix.setScale(position, position);
mMatrix.postRotate(360 * position); // asemos de to'
mMatrix.postTranslate(mCurrentX, mCurrentY);
mParentView.invalidate();
}
/**
* This has to be called from the root container's dispatchDraw()
* in order to update the animation.
*/
public void delegate_draw(Canvas c) {
if (LOG_ON) Log.v(TAG, "CX " + mCurrentX + ", CY " + mCurrentY);
c.drawBitmap(mBitmap, mMatrix, mPaint);
}
private abstract class SimpleAnimationListener implements Animator.AnimatorListener {
@Override public void onAnimationStart(Animator animation) {}
@Override public void onAnimationRepeat(Animator animation) {}
@Override public void onAnimationCancel(Animator animation) {}
}
}
然后,当您想要为任何视图设置动画时,只需调用游戏根布局中的函数即可:
GameRootLayout rootLayout = (GameRootLayout)findViewById(...);
.
.
rootLayout.addFlyOver(yourBrick, targetX, targetY);
此示例还将 alpha 和旋转应用于视图,但您可以根据需要轻松调整它。
希望对您有所启发,如有任何问题欢迎随时提问!
虽然 provided by rupps 可能会解决问题,但我个人 不会 使用该方法,因为:
- 它在主线程上分配
Bitmap
对象:除非真正需要,否则你应该尽量不要这样做。
- 它不必要地 * 将样板文件添加到代码库。
* 不需要,因为framework提供了合适的API,下面会提到
因此,您要解决的问题是使 View
动画超出其父项的范围。让我们认识一下 ViewOverlay
API:
An overlay is an extra layer that sits on top of a View (the "host view") which is drawn after all other content in that view (including children, if the view is a ViewGroup). Interaction with the overlay layer is done by adding and removing drawables.
如Israel Ferrer Camacho in his "Smoke & Mirrors" talk所述:
ViewOverlay
is gonna be your best friend forever ... in animations.
作为示例用例,您可以看到 this:
使用 ViewOverlay
API 动画图标。这看起来像 shared element transition?? Well, that's because Transitions API 内部使用 ViewOverlay
.
也a nice example by Dave Smith,展示了使用ViewOverlay
和Animator
的区别:
为了完成答案,我将 post 来自 Dave Smith 示例的一段代码。用法很简单:
container.getOverlay().add(button);
button
将 "overlayed" 位于容器顶部,位于视图层次结构中的坐标中。现在你可以在这个 button
上执行动画,但关键是在不需要时删除覆盖:
@Override
public void onAnimationEnd(Animator arg0) {
container.getOverlay().remove(button);
}
我正在尝试为 parent 视图之外的视图设置动画,当我这样做时 child 视图无法在其 parent 之外设置动画。我通过使用 setClipChildren(false)
解决了这个问题并且它起作用了......当视图被动画 up 时。当我为视图 down 设置动画时,图像仍然隐藏。
这是有效的代码。此代码将使平铺按钮动画化到屏幕顶部:
private void setGameBoard(){
brickWall.setClipChildren(false);
brickWall.setClipToPadding(false);
//Build game board
for(int ii = 0; ii < brickRows;ii++){
final int x = ii;
//Build table rows
row = new TableRow(this.getApplicationContext());
row.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, 50));
row.setClipChildren(false);
row.setClipToPadding(false);
// row.setBackgroundColor(ResourcesCompat.getColor(getResources(), R.color.colorAccent, null));
//Build table tiles
for(int jj=0; jj < brickColumns; jj++){
final int y = jj;
final Brick tile = new Brick(this);
tile.setClipBounds(null);
tile.setBackgroundColor(ResourcesCompat.getColor(getResources(), R.color.colorPrimary, null));
//Set margins to create look of tic-tac-toe
TableRow.LayoutParams lp = new TableRow.LayoutParams(
150, 75);
lp.setMargins(0,0,0,0);
//lp.weight = 1;
tile.setLayoutParams(lp);
tile.setWidth(3);
tile.setHeight(10);
tile.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(tile.getHits() == 0){
tile.setBackgroundColor(ResourcesCompat.getColor(getResources(),
R.color.colorGreen, null));
tile.addHit();
} else if (tile.getHits() == 1){
tile.setBackgroundColor(ResourcesCompat.getColor(getResources(),
R.color.colorYellow, null));
tile.addHit();
}else if(tile.getHits() == 2){
brokenBricks++;
float bottomOfScreen = getResources().getDisplayMetrics()
.heightPixels;
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(tile, "translationY",
-2000);
// 2
objectAnimator.setDuration(2000);
tile.setHapticFeedbackEnabled(true);
objectAnimator.start();
//tile.setVisibility(View.INVISIBLE);
if(isRevealComplete()){
brickWall.setVisibility(View.INVISIBLE);
}
}
}
});
row.addView(tile);
}
brickWall.addView(row);
}
}
但是当我调整视图到屏幕底部的位置时,它下面的视图是 "swallowed" 并且当它到达 parent 视图的底部时被隐藏:
private void setGameBoard(){
brickWall.setClipChildren(false);
brickWall.setClipToPadding(false);
//Build game board
for(int ii = 0; ii < brickRows;ii++){
final int x = ii;
//Build table rows
row = new TableRow(this.getApplicationContext());
row.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, 50));
row.setClipChildren(false);
row.setClipToPadding(false);
// row.setBackgroundColor(ResourcesCompat.getColor(getResources(), R.color.colorAccent, null));
//Build table tiles
for(int jj=0; jj < brickColumns; jj++){
final int y = jj;
final Brick tile = new Brick(this);
tile.setClipBounds(null);
tile.setBackgroundColor(ResourcesCompat.getColor(getResources(), R.color.colorPrimary, null));
//Set margins to create look of tic-tac-toe
TableRow.LayoutParams lp = new TableRow.LayoutParams(
150, 75);
lp.setMargins(0,0,0,0);
//lp.weight = 1;
tile.setLayoutParams(lp);
tile.setWidth(3);
tile.setHeight(10);
tile.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(tile.getHits() == 0){
tile.setBackgroundColor(ResourcesCompat.getColor(getResources(),
R.color.colorGreen, null));
tile.addHit();
} else if (tile.getHits() == 1){
tile.setBackgroundColor(ResourcesCompat.getColor(getResources(),
R.color.colorYellow, null));
tile.addHit();
}else if(tile.getHits() == 2){
brokenBricks++;
float bottomOfScreen = getResources().getDisplayMetrics()
.heightPixels;
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(tile, "translationY",
2000);
objectAnimator.setDuration(2000);
tile.setHapticFeedbackEnabled(true);
objectAnimator.start();
//tile.setVisibility(View.INVISIBLE);
if(isRevealComplete()){
brickWall.setVisibility(View.INVISIBLE);
}
}
}
});
row.addView(tile);
}
brickWall.addView(row);
}
}
所以我的问题是:如何在 child 视图下方的 parent 视图之外为 child 视图设置动画?
更新 1
如果我移除我试图放下的瓷砖下方的瓷砖,那么我能够看到所需的效果,直到它下面有另一个瓷砖,此时掉落的瓷砖将 "behind"仍然在那里的瓷砖。那么如何让掉落的瓷砖移动到其下方的 children 之上呢?
更新 2
我注意到的一件新事是,如果我向左或向上移动按钮,它会正常工作;但是,如果将它向下或向右移动,它就会落后于其他视图。这使我相信在当前图块之后创建的按钮具有不同的效果。
好的,所以在我建议的替代方法中,我会使用一个助手 class FlyOverView
获取任何视图的 "photo" 然后将其动画化到任何所需的坐标。一旦动画 运行,您就可以隐藏/删除原始视图,因为在屏幕上移动的只是 canvas 上的图像。您不必担心剪裁其他视图等问题。
您需要在积木系统的外部容器上声明此 FlyOverView
,并且该布局必须覆盖您希望动画可见的整个区域。所以我建议使用以下内容作为你的根容器,它只是一个 FrameLayout
你可以在它的子容器上绘制东西的地方。
package whatever;
public class GameRootLayout extends FrameLayout {
// the currently-animating effect, if any
private FlyOverView mFlyOverView;
public GameRootLayout(Context context, AttributeSet attrs) {
super(context,attrs);
}
@Override
public void dispatchDraw(Canvas canvas) {
// draw the children
super.dispatchDraw(canvas);
// draw our stuff
if (mFlyOverView != null) {
mFlyOverView.delegate_draw(canvas);
}
}
/**
* Starts a flyover animation for the specified view.
* It will "fly" to the desired position with an alpha / translation / rotation effect
*
* @param viewToFly The view to fly
* @param targetX The target X coordinate
* @param targetY The target Y coordinate
*/
public void addFlyOver(View viewToFly, @Px int targetX, @Px int targetY) {
if (mFlyOverView != null) mFlyOverView.cancel();
mFlyOverView = new FlyOverView(this, viewToFly, targetX, targetY, new FlyOverView.OnFlyOverFinishedListener() {
@Override
public void onFlyOverFinishedListener() {
mFlyOverView = null;
}
});
}
}
您可以用作容器
<whatever.GameRootLayout
android:id="@+id/gameRootLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
.
.
</whatever.GameRootLayout>
然后是 FlyOverView 本身:
package com.regaliz.gui.fx;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.util.Log;
import android.view.View;
import android.support.annotation.Px;
import android.view.animation.AccelerateDecelerateInterpolator;
/**
* Neat animation that captures a layer bitmap and "flies" it to the corner of the screen, used to create
* an "added to playlist" effect or something like that. The method delegate_draw() must be called from the
* parent view to update the animation
*
* @author rupps'2014
* license public domain, attribution appreciated
*/
@SuppressWarnings("unused")
public class FlyOverView {
public static final int DEFAULT_DURATION = 1000;
private static final String TAG = "FlyOverView";
private static final boolean LOG_ON = false;
private ObjectAnimator mFlyoverAnimation;
private float mCurrentX, mCurrentY;
private Matrix mMatrix = new Matrix();
private Bitmap mBitmap;
private Paint mPaint = new Paint();
private View mParentView = null;
private OnFlyOverFinishedListener mOnFlyOverFinishedListener;
public interface OnFlyOverFinishedListener {
void onFlyOverFinishedListener();
}
/**
* Creates the FlyOver effect
*
* @param parent Container View to invalidate. That view has to call this class' delegate_draw() in its dispatchDraw().
* @param viewToFly Target View to animate
* @param finalX Final X coordinate
* @param finalY Final Y coordinate
*/
public FlyOverView(View parent, View viewToFly, @Px int finalX, @Px int finalY, OnFlyOverFinishedListener listener) {
setupFlyOver(parent, viewToFly, finalX, finalY, DEFAULT_DURATION, listener);
}
/**
* Creates the FlyOver effect
*
* @param parent Container View to invalidate. That view has to call this class' delegate_draw() in its dispatchDraw().
* @param viewToFly Target View to animate
* @param finalX Final X coordinate
* @param finalY Final Y coordinate
* @param duration Animation duration
*/
public FlyOverView(View parent, View viewToFly, @Px int finalX, @Px int finalY, int duration, OnFlyOverFinishedListener listener) {
setupFlyOver(parent, viewToFly, finalX, finalY, duration, listener);
}
/**
* cancels current animation from the outside
*/
public void cancel() {
if (mFlyoverAnimation != null) mFlyoverAnimation.cancel();
}
private void setupFlyOver(View parentContainer, View viewToFly, @Px int finalX, @Px int finalY, int duration, OnFlyOverFinishedListener listener) {
int[] location = new int[2];
mParentView = parentContainer;
mOnFlyOverFinishedListener = listener;
viewToFly.getLocationInWindow(location);
int
sourceX = location[0],
sourceY = location[1];
if (LOG_ON) Log.v(TAG, "FlyOverView, item " + viewToFly+", finals "+finalX+", "+finalY+", sources "+sourceX+", "+sourceY+ " duration "+duration);
/* Animation definition table */
mFlyoverAnimation = ObjectAnimator.ofPropertyValuesHolder(
this,
PropertyValuesHolder.ofFloat("translationX", sourceX, finalX),
PropertyValuesHolder.ofFloat("translationY", sourceY, finalY),
PropertyValuesHolder.ofFloat("scaleAlpha", 1, 0.2f) // not to 0 so we see the end of the effect in other properties
);
mFlyoverAnimation.setDuration(duration);
mFlyoverAnimation.setRepeatCount(0);
mFlyoverAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
mFlyoverAnimation.addListener(new SimpleAnimationListener() {
@Override
public void onAnimationEnd(Animator animation) {
if (LOG_ON) Log.v(TAG, "FlyOver: End");
mParentView.invalidate();
if (mBitmap != null) mBitmap.recycle(); // just for safety
mBitmap = null;
mOnFlyOverFinishedListener.onFlyOverFinishedListener();
}
});
// take snapshot of viewToFly
viewToFly.setDrawingCacheEnabled(true);
mBitmap = Bitmap.createBitmap(viewToFly.getDrawingCache());
viewToFly.setDrawingCacheEnabled(false);
mFlyoverAnimation.start();
}
// ANIMATOR setter
public void setTranslationX(float position) {
mCurrentX = position;
}
// ANIMATOR setter
public void setTranslationY(float position) {
mCurrentY = position;
}
// ANIMATOR setter
// as this will be called in every iteration, we set here all parameters at once then call invalidate,
// rather than separately
public void setScaleAlpha(float position) {
mPaint.setAlpha((int) (100 * position));
mMatrix.setScale(position, position);
mMatrix.postRotate(360 * position); // asemos de to'
mMatrix.postTranslate(mCurrentX, mCurrentY);
mParentView.invalidate();
}
/**
* This has to be called from the root container's dispatchDraw()
* in order to update the animation.
*/
public void delegate_draw(Canvas c) {
if (LOG_ON) Log.v(TAG, "CX " + mCurrentX + ", CY " + mCurrentY);
c.drawBitmap(mBitmap, mMatrix, mPaint);
}
private abstract class SimpleAnimationListener implements Animator.AnimatorListener {
@Override public void onAnimationStart(Animator animation) {}
@Override public void onAnimationRepeat(Animator animation) {}
@Override public void onAnimationCancel(Animator animation) {}
}
}
然后,当您想要为任何视图设置动画时,只需调用游戏根布局中的函数即可:
GameRootLayout rootLayout = (GameRootLayout)findViewById(...);
.
.
rootLayout.addFlyOver(yourBrick, targetX, targetY);
此示例还将 alpha 和旋转应用于视图,但您可以根据需要轻松调整它。
希望对您有所启发,如有任何问题欢迎随时提问!
虽然
- 它在主线程上分配
Bitmap
对象:除非真正需要,否则你应该尽量不要这样做。 - 它不必要地 * 将样板文件添加到代码库。
* 不需要,因为framework提供了合适的API,下面会提到
因此,您要解决的问题是使 View
动画超出其父项的范围。让我们认识一下 ViewOverlay
API:
An overlay is an extra layer that sits on top of a View (the "host view") which is drawn after all other content in that view (including children, if the view is a ViewGroup). Interaction with the overlay layer is done by adding and removing drawables.
如Israel Ferrer Camacho in his "Smoke & Mirrors" talk所述:
ViewOverlay
is gonna be your best friend forever ... in animations.
作为示例用例,您可以看到 this:
ViewOverlay
API 动画图标。这看起来像 shared element transition?? Well, that's because Transitions API 内部使用 ViewOverlay
.
也a nice example by Dave Smith,展示了使用ViewOverlay
和Animator
的区别:
为了完成答案,我将 post 来自 Dave Smith 示例的一段代码。用法很简单:
container.getOverlay().add(button);
button
将 "overlayed" 位于容器顶部,位于视图层次结构中的坐标中。现在你可以在这个 button
上执行动画,但关键是在不需要时删除覆盖:
@Override
public void onAnimationEnd(Animator arg0) {
container.getOverlay().remove(button);
}