3

我有一个 ViewPager,我需要在按下按钮时整体移动它。我为此使用了动画。

当我按下它时,我会为它翻译“x”。我使用 setFillAfter(true) 来保持新位置。但是当我更改 ViewPager 的页面时,它会跳回到原来的 x 位置!

我只在Android 4.1上看到过这个问题,用Android 4.0没有问题!所以它看起来像是Android中的某种回归。

我附上了一个测试项目,我可以在没有我所有其他东西的情况下重现这个问题。我认为最好是您想帮助我解决这个问题,以便在您的 Eclipse 中导入项目并亲自查看。

我还添加了视频,一个在我看到问题的 HTC One X 上,另一个在装有 Android 4.0 的平板电脑上,问题不存在。

我一直在拼命寻找解决这个丑陋的副作用,但直到现在还没有运气......

(对不起大电影文件......)

Android 4.0 无副作用视频

带有副作用的视频 Android 4.1

您可以在其中重现问题的项目

编辑:

我添加了以下内容:

@Override
public void onAnimationEnd(Animation animation) {
    RelativeLayout.LayoutParams lp = (android.widget.RelativeLayout.LayoutParams) myViewPager.getLayoutParams();
    if (!i)
        lp.setMargins(300,0,0,0);
    else
        lp.setMargins(0,0,0,0);

    myViewPager.setLayoutParams(lp);
}

之后它保持在正确的位置,但它快速“闪烁”,就像动画仍然在最后显示,当我改变边距时,它仍然显示动画后的偏移量。然后它跳到正确的位置。

4

4 回答 4

3

主要问题似乎是动画类型选择不正确。您会看到,作为工具的 View Animation适合与 ViewPager 等复杂的交互式对象一起使用。它仅提供视图绘制位置的低成本动画。动画 ViewPager 响应用户操作的视觉行为未定义,不应依赖。丑陋的电影,当你用真实的物体代替“gost”时,这是很自然的。

该机制旨在用于您的情况,因为 API 11 是内置在视图中以优化性能的专用属性动画器:ViewPropertyAnimator,或者不是专用的,但更通用的 ObjectAnimator 和 AnimatorSet。

属性动画使 View 真正改变它的位置和正常工作。

要制作项目,例如使用 ViewPropertyAnimator,请将您的侦听器设置更改为:

btn.setOnClickListener(new OnClickListener() {
    boolean b = false;
    @Override
    public void onClick(View v) {
        if(b) {
            myViewPager.animate().translationX(0f).setDuration(700);
        }
        else {
            myViewPager.animate().translationX(300f).setDuration(700);
        }
        b=!b;
    }
});

如果您只想使用 xml 配置,请坚持使用 |ObjectAnimator 和 AnimatorSet。阅读上面的链接以获取更多信息。

如果您急于支持蜂窝之前的设备,您可以使用 Jake Warton 的NineOldAndroids 项目。希望有帮助。

于 2013-05-23T09:48:15.440 回答
1

这是因为动画的 setFillAfter(true) 实际上并没有改变 View 的位置或任何属性;它所做的只是创建视图的绘图缓存的位图并将其留在动画结束的地方。一旦屏幕再次失效(即更改 ViewPager 中的页面),位图将被删除,并且看起来好像 View 正在返回其原始位置,而实际上它已经在那里。

如果您希望 View 在动画完成后保留其位置,您需要实际调整 View 的 LayoutParams 以匹配您想要的效果。为此,您可以重写 Animation 的 onAnimationEnd 方法,并在那里调整 View 的 LayoutParams。

调整 LayoutParams 后,您可以删除对 setFillAfter(true) 的调用,您的视图实际上将保持在您期望的位置。

于 2013-05-13T13:39:50.743 回答
1

关于闪退问题:

我之前遇到过这个问题,它源于onAnimationEnd()调用与下一个布局传递不同步的可能性。Animation通过对视图应用变换,相对于当前位置绘制它来工作。

但是,可以在您在onAnimationEnd()方法中移动视图之后呈现视图。在这种情况下,Animation 的转换仍然被正确应用,但 Animation 认为 View 没有改变它的原始位置,这意味着它将相对于它的 ENDING 位置而不是它的 STARTING 位置进行绘制。

我的解决方案是创建 Animation 的自定义子类并添加一个方法 ,changeYOffset(int change)该方法修改在 Animation 方法期间应用的 y 平移applyTransformation。我在 View 的onLayout()方法中调用了这个新方法,并传递了新的 y 偏移量。

这是我的动画中的一些代码,MenuAnimation

/**
* Signal to this animation that a layout pass has caused the View on which this animation is
* running to have its "top" coordinate changed.
*
* @param change
* the difference in pixels
*/
public void changeYOffset(int change) {
    fromY -= change;
    toY -= change;
}

@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
    float reverseTime = 1f - interpolatedTime;

    float dy = (interpolatedTime * toY) + (reverseTime * fromY);
    float alpha = (interpolatedTime * toAlpha) + (reverseTime * fromAlpha);

    if (alpha > 1f) {
        alpha = 1f;
    }
    else if (alpha < 0f) {
        alpha = 0f;
    }

    t.setAlpha(alpha);
    t.getMatrix().setTranslate(0f, dy);
}

从 View 类:

private int lastTop;

// ...

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    // the animation is expecting that its View will not be moved by the container
    // during its time period. if this does happen, we need to inform it of the change.
    Animation anim = getAnimation();
    if (anim != null && anim instanceof MenuAnimation) {
        MenuAnimation animation = (MenuAnimation) anim;
        animation.changeYOffset(top - lastTop);
    }

    // ...

    lastTop = top;

    super.onLayout(changed, left, top, right, bottom);
}
于 2013-05-21T22:09:10.157 回答
1

Crucero 关于 setFillAfter 不调整参数失效后的正确做法是正确的。当视图重新布局时(在它失效后会发生传递),它的布局参数将是始终应用​​的那些,所以它应该回到原来的位置。

Jschools 对 onAnimationEnd 的看法是正确的。强烈建议您使用调试器单步调试源代码,您会发现在触发 onAnimationEnd 后进行的更新会影响视图的绘制位置,此时您实际上已经应用了布局参数,因此偏移量加倍引起的闪烁。

但这可以通过确保在正确的时间重新布局来解决。您希望在动画结束时将重新定位逻辑放在 ui 消息队列的末尾,以便在动画之后但布局之前对其进行轮询。令人讨厌的是,没有任何地方建议这样做,但我还没有在任何版本的 SDK 中找到一个原因(当只执行一次并且没有错误地使用 ui 线程时)这不应该起作用。

由于我们在一些旧设备上发现的另一个问题,还清除了动画。

所以,试试:

@Override
public void onAnimationEnd(final Animation animation) {
   myViewPager.post(new Runnable() {
       @Override
       public public void run() {
           final RelativeLayout.LayoutParams lp = (android.widget.RelativeLayout.LayoutParams) myViewPager.getLayoutParams();
           if (!someBooleanIPresume)
               lp.setMargins(300,0,0,0);
           else
               lp.setMargins(0,0,0,0);

           myViewPager.setLayoutParams(lp);
           myViewPager.clearAnimation();
       }
}
于 2013-05-22T09:18:05.823 回答