Gmail三片段动画场景的完整工作示例?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (36)

TL;Dr:我在找一个完整工作样本我将称之为“Gmail三片段动画”场景。具体来说,我们想从两个片段开始,如下所示:

对于某些UI事件(例如,点击片段B中的某个内容),我们希望:

  • 片段A从屏幕上滑到左边
  • B段滑到屏幕的左边边缘,缩小到A段空出的位置
  • 碎片C从屏幕的右侧滑入,并占据碎片B空出的位置

@Reto Meier捐款对于相同的基本问题,表示将使用setCustomAnimations()带着FragmentTransaction。对于两个片段场景(例如,最初只看到片段A,并希望使用动画效果替换它)。

  • 因为只能指定一个“in”和一个“out”动画,所以我看出您将如何处理三个片段场景所需的所有不同动画。
  • <objectAnimator>在他的示例代码中,使用了以像素为单位的硬连线位置,考虑到屏幕大小的变化,这似乎是不切实际的。setCustomAnimations()需要动画资源,从而排除了用Java定义这些东西的可能性
  • 我不知道比例动画的对象是如何与如下的东西相关联的android:layout_weightLinearLayout按百分比分配空间
  • 我不知道如何在一开始就处理C片段(GONEandroid:layout_weight0?动画片前的刻度为0?还有别的吗?)

@Roman Nurik指出。包括你自己定义的那些。这可以帮助解决硬连线位置的问题,而代价是创建自己的自定义布局管理器子类。这对一些人有帮助,但我仍然对Reto的其他解决方案感到困惑。

显示了一些诱人的伪代码,基本上说所有三个片段最初都驻留在容器中,碎片C在开始时通过hide()交易操作。然后我们show()C和hide()a当UI事件发生时。但是,我不明白这是如何处理B改变大小的事实。它还依赖于这样一个事实,即显然可以向同一个容器中添加多个片段,而且我不确定从长期来看,这是否是可靠的行为(更不用说它应该会中断)。

提问于
用户回答回答于

简而言之,ThreePaneLayout在那个项目中LinearLayout子类,设计用于在景观中与三个孩子一起工作。这些子元素的宽度可以在布局XML中通过任何需要的方式来设置--我显示使用权重,但是可以通过维度资源或其他什么设置特定的宽度。第三个孩子--问题中的C片段--应该有一个零的宽度。

package com.commonsware.android.anim.threepane;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;

public class ThreePaneLayout extends LinearLayout {
  private static final int ANIM_DURATION=500;
  private View left=null;
  private View middle=null;
  private View right=null;
  private int leftWidth=-1;
  private int middleWidthNormal=-1;

  public ThreePaneLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    initSelf();
  }

  void initSelf() {
    setOrientation(HORIZONTAL);
  }

  @Override
  public void onFinishInflate() {
    super.onFinishInflate();

    left=getChildAt(0);
    middle=getChildAt(1);
    right=getChildAt(2);
  }

  public View getLeftView() {
    return(left);
  }

  public View getMiddleView() {
    return(middle);
  }

  public View getRightView() {
    return(right);
  }

  public void hideLeft() {
    if (leftWidth == -1) {
      leftWidth=left.getWidth();
      middleWidthNormal=middle.getWidth();
      resetWidget(left, leftWidth);
      resetWidget(middle, middleWidthNormal);
      resetWidget(right, middleWidthNormal);
      requestLayout();
    }

    translateWidgets(-1 * leftWidth, left, middle, right);

    ObjectAnimator.ofInt(this, "middleWidth", middleWidthNormal,
                         leftWidth).setDuration(ANIM_DURATION).start();
  }

  public void showLeft() {
    translateWidgets(leftWidth, left, middle, right);

    ObjectAnimator.ofInt(this, "middleWidth", leftWidth,
                         middleWidthNormal).setDuration(ANIM_DURATION)
                  .start();
  }

  public void setMiddleWidth(int value) {
    middle.getLayoutParams().width=value;
    requestLayout();
  }

  private void translateWidgets(int deltaX, View... views) {
    for (final View v : views) {
      v.setLayerType(View.LAYER_TYPE_HARDWARE, null);

      v.animate().translationXBy(deltaX).setDuration(ANIM_DURATION)
       .setListener(new AnimatorListenerAdapter() {
         @Override
         public void onAnimationEnd(Animator animation) {
           v.setLayerType(View.LAYER_TYPE_NONE, null);
         }
       });
    }
  }

  private void resetWidget(View v, int width) {
    LinearLayout.LayoutParams p=
        (LinearLayout.LayoutParams)v.getLayoutParams();

    p.width=width;
    p.weight=0;
  }
}

但是,在运行时,无论最初如何设置宽度,宽度管理都由ThreePaneLayout你第一次使用hideLeft()从显示所指的问题A和B改为B和C的片段。ThreePaneLayout-与碎片没有具体联系--这三个部分是leftmiddle,和right。在你打电话的时候hideLeft(),我们记录下leftmiddle把三个中任何一个的重量都取零,这样我们就可以完全控制尺寸了。在...的时刻hideLeft(),我们设置了right的原始大小middle

动画有两种:

  • ViewPropertyAnimator向左转换三个小部件的宽度left,使用硬件层
  • ObjectAnimator的自定义伪属性middleWidth更改middle的宽度,从它开始的任何宽度到最初的宽度left

(使用AnimatorSetObjectAnimators所有这些,虽然这是暂时可行的)

(也有可能middleWidthObjectAnimator否定硬件层的值,因为这需要相当连续的失效)

(是的)一定可能我在动画理解上还有空白,我喜欢用括号表达)

净效果是left从屏幕上滑落,middle幻灯片的原始位置和大小left,和right在后面翻译middle

showLeft()简单地逆转这个过程,用同样的动画师组合,只是方向颠倒。

活动使用ThreePaneLayout抱着一双ListFragment小部件和Button。选择左侧片段中的某个内容会添加(或更新)中间片段的内容。在中间片段中选择某项设置Button,+执行hideLeft()ThreePaneLayout。如果我们把左边藏起来,按回去,就会执行showLeft()否则,返回退出活动。因为这不需要FragmentTransactions为了影响动画,我们只能自己管理“后台堆栈”。

用户回答回答于

用法:

layout = new ThreeLayout(this, 3);
layout.setAnimationDuration(1000);
setContentView(layout);
layout.getLeftView();   //<---inflate FragmentA here
layout.getMiddleView(); //<---inflate FragmentB here
layout.getRightView();  //<---inflate FragmentC here

//Left Animation set
layout.startLeftAnimation();

//Right Animation set
layout.startRightAnimation();

//You can even set interpolators

解释:

创建了一个新的自定义关系Layout(三层)和2个自定义动画(MyScale动画,MyTranslate动画)

ThreeLayout将左窗格的权重设为param,假设另一个可见视图具有weight=1

所以new ThreeLayout(context,3)创建一个具有3个子视图的新视图,左窗格具有总屏幕的1/3。另一个视图占用所有可用空间。

它在运行时计算宽度,一个更安全的实现是第一次计算维数,在drag()中,而不是在post()中。

缩放和翻译动画实际上调整了视图的大小和移动,而不是伪-比例。注意fillAfter(true)不在任何地方使用。

设置了这些规则之后,RelativeLayout负责其他所有事务。动画改变了margins(行动中)及[width,height]按比例

访问每个子程序

public FrameLayout getLeftLayout() {}

public FrameLayout getMiddleLayout() {}

public FrameLayout getRightLayout() {}

下面是2幅动画

阶段1

-在屏幕上---视图1_____视图3_____

阶段2

视图1_____视图3_____

扫码关注云+社区