Gmail三段动画方案的完整工作示例?


128

TL; DR:我正在寻找一个完整的工作样本,该样本将被称为“ Gmail三片段动画”方案。具体来说,我们想从两个片段开始,如下所示:

两个碎片

在发生某些UI事件(例如,点击片段B中的某些内容)时,我们想要:

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

并且,在按下“返回”按钮时,我们希望该组操作被逆转。

现在,我已经看到了很多部分实现。我将在下面回顾其中的四个。除了不完整之外,它们都有问题。


@Reto迈尔促成这种普遍的回答相同的基本问题,表明你会使用setCustomAnimations()一个FragmentTransaction。对于两个片段的场景(例如,您最初只看到片段A,并希望使用动画效果将其替换为新的片段B),我完全同意。然而:

  • 由于您只能指定一个“入”和一个“出”动画,因此我看不到如何处理三片段场景所需的所有不同动画
  • <objectAnimator>他的代码示例使用像素的硬连线的位置,这似乎给出不同的屏幕尺寸是不切实际的,但setCustomAnimations()需要动画资源,排除在Java中定义这些事情的可能性
  • 我很茫然,以与事物对象的动画师规模的领带怎么样android:layout_weightLinearLayout百分比分配空间迷惑不解
  • 我一开始就对片段C的处理方式感到困惑(将GONEandroid:layout_weight0?预先动画化为0的比例?

@Roman Nurik指出,您可以为任何属性设置动画,包括您自己定义的属性。这可以帮助解决固定位置的问题,而以发明自己的自定义布局管理器子类为代价。这对某些人有所帮助,但是我仍然对Reto的其余解决方案感到困惑。


pastebin条目的作者显示了一些诱人的伪代码,基本上是说所有三个片段最初都将驻留在容器中,而片段C首先通过hide()事务操作隐藏。然后我们在UI事件发生时使用show()C和hide()A。但是,我看不到如何处理B更改大小这一事实。它还依赖于一个事实,即您显然可以将多个片段添加到同一容器中,而且我不确定从长远来看这是否是可靠的行为(更不用说它应该破坏了findFragmentById(),尽管我可以接受)。


这篇博文的作者指出,Gmail根本没有使用setCustomAnimations(),而是直接使用对象动画制作器(“您只需更改根视图的左边界+更改右视图的宽度”)。但是,这仍然是两段式解决方案AFAICT,实现再次显示了以像素为单位的硬接线尺寸。


我将继续解决这个问题,所以有一天我可能会自己回答,但我真的希望有人能为这个动画场景设计出三片段解决方案,并可以发布代码(或指向它的链接)。Android中的动画使我想拔头发,而那些看到我的人都知道这在很大程度上是徒劳的。


2
这不是Romain Guy发布的内容吗?curious-creature.org/2011/02/22/…–
费尔南多·加莱戈

1
@ ferdy182:他没有使用片段AFAICT。从视觉上看,这是正确的基本效果,我也许可以将其用作尝试创建基于片段的答案的另一个数据点。谢谢!
CommonsWare 2012年

1
只是想一想:标准电子邮件应用在Nexus 7上的行为类似(尽管不完全相同),应该是开源的。
Christopher Orr 2012年

4
电子邮件应用程序使用自定义的“ ThreePaneLayout”,该动画可对左侧视图的位置进行动画处理,以及其他两个视图的宽度/可见性-每个视图都包含相关的片段。
Christopher Orr 2012年

2
嘿@CommonsWare在这里我不会打扰我,但我有一个非常满意的答案,与您的问题类似。因此,建议您检查一下(还检查注释):stackoverflow.com/a/14155204/906362
Budius

Answers:


67

github上载了我的建议 (尽管强烈建议将视图硬件加速用于这种动画,但它适用于所有android版本。对于非硬件加速的设备,位图缓存实现应该更合适)

带有动画的演示视频在此处(导致屏幕投放速度缓慢的帧率。实际性能非常快)


用法:

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

说明

创建了一个新的自定义RelativeLayout(ThreeLayout)和2个自定义动画(MyScalAnimation,MyTranslateAnimation)

ThreeLayout假设另一个可见视图具有,则以参数的形式获取左窗格的权重weight=1

因此,new ThreeLayout(context,3)创建一个具有3个子级的新视图,左侧窗格的总屏幕数为1/3。另一个视图占据了所有可用空间。

它在运行时计算宽度,更安全的实现是在draw()中首次计算尺寸。而不是在post()中

缩放和平移动画实际上是调整视图的大小和移动视图,而不是伪[缩放,移动]。注意,fillAfter(true)任何地方都不会使用它。

View2是view1的right_of

View3是view2的right_of

设置了这些规则后,RelativeLayout会处理其他所有事情。动画改变margins(移动)和[width,height]缩放

要访问每个孩子(以便您可以用Fragment对其进行充气,可以调用

public FrameLayout getLeftLayout() {}

public FrameLayout getMiddleLayout() {}

public FrameLayout getRightLayout() {}

下面展示了2个动画


阶段1

--- IN屏幕----------!----- OUT ----

[View1] [_____ View2 _____] [_____ View3_____]

第二阶段

--OUT-!-------- IN屏幕------

[View1] [View2] [_____ View3______]


1
虽然我还没有实现比例动画,但我担心它不能很好地用于纯色领域,而不能说真正的内容。重新调整片段B的大小以适合片段A的空间的结果应该与将片段B最初放置在其中的结果没有什么不同。例如,AFAIK(一种缩放动画器)将缩放字体大小(通过将动画中的所有内容绘制得更View小),但是在Gmail /电子邮件中,我们不会得到更小的字体,只有更少的单词。
CommonsWare 2012年

1
@CommonsWare scaleAnimation只是一个抽象名称。实际上,没有缩放发生。实际上,它会调整视图的宽度。检查源。LayoutResizer可能是一个更好的名字。
弱线2012年

1
尽管我可能会误解事物,但这并不是我对scaleXon 的价值来源的View解释。
CommonsWare 2012年

1
@CommonsWare检查此github.com/weakwire/3PaneLayout/blob/master/src/com/example/…出来。我扩展Animation的唯一原因是可以访问interpolatedTime。p.width =(int)宽度; 做所有的魔术,而不是scaleX。它是在指定时间内更改的实际视图尺寸。
弱线2012年

2
您的视频显示了很好的帧率,但是示例中使用的视图非常简单。在动画期间执行requestLayout()的成本非常高。尤其是在子项很复杂的情况下(例如ListView)。这可能会导致动画断断续续。GMail应用程序不会调用requestLayout。取而代之的是,在动画开始之前,将另一个较小的视图放入中间面板。
Thierry-Dimitri Roy

31

好的,这是我自己的解决方案,它是根据问题评论中@Christopher的建议从Email AOSP应用程序派生的。

https://github.com/commonsguy/cw-omnibus/tree/master/Animation/ThreePane

@weakwire的解决方案让人想起我,尽管他使用经典Animation而不是动画师,并且使用RelativeLayout规则来强制定位。从赏金的角度来看,他可能会得到赏金,除非别人的解决方案较为模糊,但仍会给出答案。


简而言之,该ThreePaneLayout项目中的in是一个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-它没有具体联系的片段-三块是leftmiddleright。在您致电时hideLeft(),我们会记录和的大小,left并将middle这三个中的任何一个使用的权重归零,因此我们可以完全控制大小。在的时间点hideLeft(),我们将的大小设置right为的原始大小middle

动画有两个方面:

  • 使用,使用硬件层,ViewPropertyAnimator通过进行宽度为的左侧的三个小部件的翻译。left
  • 使用ObjectAnimator上的自定义伪属性middleWidth的改变middle宽度不管它开始于原始宽度left

(虽然现在可以使用,但所有这些都使用AnimatorSet和是更好的主意ObjectAnimators

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

绝对有可能我在动画理解方面仍然存在差距,并且我喜欢括号内的陈述)

最终效果是left滑出屏幕,middle滑至的原始位置和大小left,并right在的右后平移middle

showLeft() 只需颠倒方向,使用相同的动画师组合即可颠倒该过程。

该活动使用ThreePaneLayout来容纳一对ListFragment小部件和Button。在左侧片段中选择某些内容会添加(或更新)中间片段。在中间片段中选择某个内容会设置的标题Button,并在hideLeft()上执行ThreePaneLayout。如果我们隐藏左侧,则按BACK将执行showLeft();否则,BACK退出活动。由于这不会FragmentTransactions影响动画,因此我们不得不自己管理“后堆栈”。

上面链接到的项目使用本机片段和本机动画师框架。我有同一项目的另一个版本,该版本使用Android支持片段回传和NineOldAndroids作为动画:

https://github.com/commonsguy/cw-omnibus/tree/master/Animation/ThreePaneBC

尽管该动画在硬件规格较低和缺乏硬件加速支持的情况下有点生涩,但在第一代Kindle Fire上,backport的效果很好。在Nexus 7和其他当前型号的平板电脑上,两种实现方式似乎都很顺利。

我当然乐于接受有关如何改进此解决方案或其他解决方案的想法,这些解决方案比我在这里所做的工作(或@weakwire使用的内容)具有明显的优势。

再次感谢大家的贡献!


1
嗨!感谢您的解决方案,但我认为添加v.setLayerType()有时会使事情变慢。只需删除这些行(以及监听器也是如此),至少对于我刚刚在Galaxy Nexus上尝试过的Android 3+设备而言,这是正确的,如果没有这些行,它的运行效果会更好
Evgeny Nacu 2012年

1
“在Nexus 7和其他当前型号的平板电脑上,两种实现方式似乎都很顺利。” 我相信你; 但我正在使用Nexus 7上的本机ObjectAnimator测试您的实现,这有些滞后。最可能的原因是什么?我已经在AndroidMAnifest中拥有hardwareAccelerated:true。我真的不知道可能是什么问题。我也尝试过DanielGrech的变体,但它却滞后了。我可以看到动画中的差异(他的动画是基于权重的),但是我看不出为什么在两种情况下它都会滞后。
Bogdan Zurac

1
@Andrew:“有点滞后”-我不确定您在这里如何使用动词“ lag”。对我而言,“滞后”意味着进行比较的具体目标。因此,例如,与滑动动作有关的动画可能会落后于手指运动。在这种情况下,一切都是由水龙头触发的,因此我不清楚动画可能会落后。我猜可能“有点滞后”只是意味着“感觉迟钝”,但我没有看到,但是我并没有声称具有强烈的审美意识,因此对我来说可以接受的动画可能对您来说是不可接受的。
CommonsWare

2
@CommonsWare:我用4.2编译了您的代码,很棒的工作。当我轻按中间位置ListView并快速按下后退按钮并重复时,整个布局向右漂移。它开始在左侧显示额外的空格列。出现这种现象是因为我不会让第一个动画(通过在Middle上点击开始ListView)完成并按下后退按钮。我通过更换固定它translationXBytranslationXtranslateWidgets方法和translateWidgets(leftWidth, left, middle, right);translateWidgets(0, left, middle, right);showLeft()方法。
M-WaJeEh 2013年

2
我也requestLayout();middle.requestLayout();in setMiddleWidth(int value);方法代替。可能不会影响性能,因为我想GPU仍会进行完整的布局传递,有何评论?
M-WaJeEh 2013年

13

我们建立了一个名为PanesLibrary的库来解决此问题。它比以前提供的功能更加灵活,因为:

  • 每个窗格可以动态调整大小
  • 它允许任意数量的窗格(不仅是2或3)
  • 窗格内的片段在方向更改时会正确保留。

您可以在这里查看:https : //github.com/Mapsaurus/Android-PanesLibrary

这是一个演示:http : //www.youtube.com/watch?v= UA-lAGVXoLU&feature= youtu.be

基本上,它使您可以轻松添加任意数量的动态大小的窗格,并将片段附加到这些窗格。希望你觉得它有用!:)


6

建立您链接到的示例之一( http://android.amberfog.com/?p=758),如何对该layout_weight属性设置动画?这样,您可以将3个片段的权重动画在一起,并获得它们可以很好地滑动在一起的好处:

从简单的布局开始。由于我们要进行动画处理layout_weight,因此我们需要一个LinearLayout作为这三个面板的根视图。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
    android:id="@+id/panel1"
    android:layout_width="0dip"
    android:layout_weight="1"
    android:layout_height="match_parent"/>

<LinearLayout
    android:id="@+id/panel2"
    android:layout_width="0dip"
    android:layout_weight="2"
    android:layout_height="match_parent"/>

<LinearLayout
    android:id="@+id/panel3"
    android:layout_width="0dip"
    android:layout_weight="0"
    android:layout_height="match_parent"/>
</LinearLayout>

然后是演示类:

public class DemoActivity extends Activity implements View.OnClickListener {
    public static final int ANIM_DURATION = 500;
    private static final Interpolator interpolator = new DecelerateInterpolator();

    boolean isCollapsed = false;

    private Fragment frag1, frag2, frag3;
    private ViewGroup panel1, panel2, panel3;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        panel1 = (ViewGroup) findViewById(R.id.panel1);
        panel2 = (ViewGroup) findViewById(R.id.panel2);
        panel3 = (ViewGroup) findViewById(R.id.panel3);

        frag1 = new ColorFrag(Color.BLUE);
        frag2 = new InfoFrag();
        frag3 = new ColorFrag(Color.RED);

        final FragmentManager fm = getFragmentManager();
        final FragmentTransaction trans = fm.beginTransaction();

        trans.replace(R.id.panel1, frag1);
        trans.replace(R.id.panel2, frag2);
        trans.replace(R.id.panel3, frag3);

        trans.commit();
    }

    @Override
    public void onClick(View view) {
        toggleCollapseState();
    }

    private void toggleCollapseState() {
        //Most of the magic here can be attributed to: http://android.amberfog.com/?p=758

        if (isCollapsed) {
            PropertyValuesHolder[] arrayOfPropertyValuesHolder = new PropertyValuesHolder[3];
            arrayOfPropertyValuesHolder[0] = PropertyValuesHolder.ofFloat("Panel1Weight", 0.0f, 1.0f);
            arrayOfPropertyValuesHolder[1] = PropertyValuesHolder.ofFloat("Panel2Weight", 1.0f, 2.0f);
            arrayOfPropertyValuesHolder[2] = PropertyValuesHolder.ofFloat("Panel3Weight", 2.0f, 0.0f);
            ObjectAnimator localObjectAnimator = ObjectAnimator.ofPropertyValuesHolder(this, arrayOfPropertyValuesHolder).setDuration(ANIM_DURATION);
            localObjectAnimator.setInterpolator(interpolator);
            localObjectAnimator.start();
        } else {
            PropertyValuesHolder[] arrayOfPropertyValuesHolder = new PropertyValuesHolder[3];
            arrayOfPropertyValuesHolder[0] = PropertyValuesHolder.ofFloat("Panel1Weight", 1.0f, 0.0f);
            arrayOfPropertyValuesHolder[1] = PropertyValuesHolder.ofFloat("Panel2Weight", 2.0f, 1.0f);
            arrayOfPropertyValuesHolder[2] = PropertyValuesHolder.ofFloat("Panel3Weight", 0.0f, 2.0f);
            ObjectAnimator localObjectAnimator = ObjectAnimator.ofPropertyValuesHolder(this, arrayOfPropertyValuesHolder).setDuration(ANIM_DURATION);
            localObjectAnimator.setInterpolator(interpolator);
            localObjectAnimator.start();
        }
        isCollapsed = !isCollapsed;
    }

    @Override
    public void onBackPressed() {
        //TODO: Very basic stack handling. Would probably want to do something relating to fragments here..
        if(isCollapsed) {
            toggleCollapseState();
        } else {
            super.onBackPressed();
        }
    }

    /*
     * Our magic getters/setters below!
     */

    public float getPanel1Weight() {
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)     panel1.getLayoutParams();
        return params.weight;
    }

    public void setPanel1Weight(float newWeight) {
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) panel1.getLayoutParams();
        params.weight = newWeight;
        panel1.setLayoutParams(params);
    }

    public float getPanel2Weight() {
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) panel2.getLayoutParams();
        return params.weight;
    }

    public void setPanel2Weight(float newWeight) {
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) panel2.getLayoutParams();
        params.weight = newWeight;
        panel2.setLayoutParams(params);
    }

    public float getPanel3Weight() {
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) panel3.getLayoutParams();
        return params.weight;
    }

    public void setPanel3Weight(float newWeight) {
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) panel3.getLayoutParams();
        params.weight = newWeight;
        panel3.setLayoutParams(params);
    }


    /**
     * Crappy fragment which displays a toggle button
     */
    public static class InfoFrag extends Fragment {
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle     savedInstanceState) {
            LinearLayout layout = new LinearLayout(getActivity());
            layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
            layout.setBackgroundColor(Color.DKGRAY);

            Button b = new Button(getActivity());
            b.setOnClickListener((DemoActivity) getActivity());
            b.setText("Toggle Me!");

            layout.addView(b);

            return layout;
        }
    }

    /**
     * Crappy fragment which just fills the screen with a color
     */
    public static class ColorFrag extends Fragment {
        private int mColor;

        public ColorFrag() {
            mColor = Color.BLUE; //Default
        }

        public ColorFrag(int color) {
            mColor = color;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            FrameLayout layout = new FrameLayout(getActivity());
            layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));

            layout.setBackgroundColor(mColor);
            return layout;
        }
    }
}

同样,此示例也不使用FragmentTransactions来实现动画(相反,它使片段所附接的视图具有动画效果),因此您需要自己完成所有的backstack / fragment事务,但是与使动画起作用的努力相比很好,这似乎不是一个坏的权衡:)

正在播放的可怕的低分辨率视频:http://youtu.be/Zm517j3bFCo


1
嗯,Gmail肯定是我所说的Fragment A的翻译版本。我担心将其权重降低到0可能会引入视觉伪像(例如,TextView随着wrap_content动画的进行而垂直拉伸自身)。但是,动画的权重可能就是它们如何调整我称为“片段B”的大小。谢谢!
CommonsWare 2012年

1
别担心!不过,这很不错,这可能会导致视觉假象,具体取决于“片段A”中的子视图。希望有人能找到更可靠的方法
DanielGrech 2012年

5

这不是使用片段...。这是一个带有3个子项的自定义布局。当您单击一条消息时,使用offsetLeftAndRight()和动画师来偏移3个孩子。

JellyBean您可以启用“显示布局边界”,在“Developper选项”设置。幻灯片动画制作完成后,您仍然可以看到左侧菜单仍在中间菜单下方。

类似于Cyril Mottier的Fly-in应用菜单,但具有3个元素而不是2个元素。

另外,ViewPager第三个孩子的代表了这种行为:ViewPager通常使用Fragments(我知道他们没有必要,但我从未见过其他实现Fragment),并且由于您不能Fragments在另一个Fragment孩子中使用这三个孩子可能不是碎片...


我以前从未使用过“显示布局范围”,这对于像这样的闭源应用程序非常有用(谢谢!)。看来,我描述成一个片段是什么翻译离屏(你可以看到它的右边缘滑动从右到左),在弹出下面收回我描述为片段B.这就像以前的感觉TranslateAnimation,虽然我确保有使用动画师实现相同目的的方法。我将不得不对此进行一些实验。谢谢!
CommonsWare 2012年

2

我目前正在尝试做类似的事情,除了Fragment B可缩放以占用可用空间,并且如果有足够的空间,则可以同时打开3窗格。到目前为止,这是我的解决方案,但是我不确定是否要坚持下去。我希望有人能给出正确答案的答案。

我没有使用LinearLayout和设置权重动画,而是使用了RelativeLayout并为边距设置动画。我不确定这是最好的方法,因为它每次更新都需要调用requestLayout()。不过,在我所有的设备上都很流畅。

因此,我为布局设置了动画,我没有使用片段事务。我手动处理后退按钮以关闭片段C(如果打开)。

FragmentB使用layout_toLeftOf / ToRightOf使其与片段A和C对齐。

当我的应用触发显示片段C的事件时,我将片段C滑入,同时我将片段A滑出。(2个独立的动画)。相反,当片段A打开时,我同时关闭了C。

在纵向模式或较小的屏幕上,我使用略有不同的布局,并在屏幕上滑动片段C。

要使用百分比作为片段A和C的宽度,我认为您必须在运行时对其进行计算...(?)

这是活动的布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rootpane"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <!-- FRAGMENT A -->
    <fragment
        android:id="@+id/fragment_A"
        android:layout_width="300dp"
        android:layout_height="fill_parent"
        class="com.xyz.fragA" />

    <!-- FRAGMENT C -->
    <fragment
        android:id="@+id/fragment_C"
        android:layout_width="600dp"
        android:layout_height="match_parent"
        android:layout_alignParentRight="true"
        class="com.xyz.fragC"/>

    <!-- FRAGMENT B -->
    <fragment
        android:id="@+id/fragment_B"
        android:layout_width="match_parent"
        android:layout_height="fill_parent"
        android:layout_marginLeft="0dip"
        android:layout_marginRight="0dip"
        android:layout_toLeftOf="@id/fragment_C"
        android:layout_toRightOf="@id/fragment_A"
        class="com.xyz.fragB" />

</RelativeLayout>

将FragmentC滑入或滑出的动画:

private ValueAnimator createFragmentCAnimation(final View fragmentCRootView, boolean slideIn) {

    ValueAnimator anim = null;

    final RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) fragmentCRootView.getLayoutParams();
    if (slideIn) {
        // set the rightMargin so the view is just outside the right edge of the screen.
        lp.rightMargin = -(lp.width);
        // the view's visibility was GONE, make it VISIBLE
        fragmentCRootView.setVisibility(View.VISIBLE);
        anim = ValueAnimator.ofInt(lp.rightMargin, 0);
    } else
        // slide out: animate rightMargin until the view is outside the screen
        anim = ValueAnimator.ofInt(0, -(lp.width));

    anim.setInterpolator(new DecelerateInterpolator(5));
    anim.setDuration(300);
    anim.addUpdateListener(new AnimatorUpdateListener() {

        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            Integer rightMargin = (Integer) animation.getAnimatedValue();
            lp.rightMargin = rightMargin;
            fragmentCRootView.requestLayout();
        }
    });

    if (!slideIn) {
        // if the view was sliding out, set visibility to GONE once the animation is done
        anim.addListener(new AnimatorListenerAdapter() {

            @Override
            public void onAnimationEnd(Animator animation) {
                fragmentCRootView.setVisibility(View.GONE);
            }
        });
    }
    return anim;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.