我正在尝试为其父视图之外的视图设置动画,当我执行时,子视图无法在其父视图之外进行动画处理。我使用setClipChildren(false)
解决了这个问题,并且它有效...当视图动画 up 时。当我为视图向下设置时,图像仍然是隐藏的。
这是有效的代码。此代码会将平铺按钮设置为屏幕顶部的动画:
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);
}
}
但是当我调整视图到屏幕底部的位置时,它下面的视图是&#34;吞下&#34;当它到达父视图的底部时隐藏:
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);
}
}
所以我的问题是: 如何在子视图下方的父视图之外设置子视图的动画?
更新1
如果我移除瓷砖下方的瓷砖,我试图放下,然后我能够看到所需的效果,直到它下面有另一个瓷砖,此时下降的瓷砖将在&#34;后面&#34;还在那里的瓷砖。那么如何让掉落的瓷砖移动到它下面的孩子身上呢?
更新2
我注意到的一件新事情是,如果我向左或向上移动按钮,它可以正常工作;但是,如果将其向下或向右移动,它会落后于其他视图。这让我相信在当前图块之后创建的按钮具有不同的效果。
答案 0 :(得分:6)
虽然answer提供的rupps可以解决问题,但我个人不使用这种方法,因为:
Bitmap
对象:除非真正需要,否则你应该努力不这样做。*不必要,因为框架提供了适当的API,如下所述。
因此,您要解决的问题是使View
的动画超出其父级的界限。让我们熟悉ViewOverlay
API:
叠加是位于视图(&#34;主机视图&#34;)之上的额外图层,它是在该视图中的所有其他内容之后绘制的(包括子视图,如果视图是ViewGroup)。通过添加和删除drawable来完成与覆盖层的交互。
正如Israel Ferrer Camacho中"Smoke & Mirrors" talk提到的那样:
ViewOverlay
永远是你最好的朋友......在动画中。
作为示例用例,您可以看到this:
使用ViewOverlay
API制作动画图标。这看起来像shared element transition ??好吧,那是因为Transitions API内部使用ViewOverlay
。
a nice example Dave Smith,显示使用ViewOverlay
和Animator
之间的区别:
为了完成答案,我将发布Dave Smith的一些代码示例。用法很简单:
container.getOverlay().add(button);
button
将&#34;覆盖&#34; 位于容器右侧,位于视图层次结构中的坐标中。现在,您可以对此button
执行动画,但关键是在不需要时删除叠加层:
@Override
public void onAnimationEnd(Animator arg0) {
container.getOverlay().remove(button);
}
答案 1 :(得分:4)
好的,所以在我建议的替代方法中,我会使用一个帮助类FlyOverView
,它可以获取任何视图的“照片”,然后将其设置为任意所需的坐标。一旦动画运行,您就可以隐藏/删除原始视图,因为在屏幕上移动的内容只是在画布上显示的图像。您不必担心剪切其他视图等。
您需要在砖块系统的外部容器上声明此FlyOverView
,并且该布局必须覆盖您希望动画可见的整个区域。所以我建议你在root容器中使用以下内容,它只是一个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和旋转应用于视图,但您可以根据需要轻松调整它。
如果您有任何问题可以随意提出,我希望这可以激励您!