好的,这是我自己的解决方案,根据@Christopher在问题 comments 中的建议,来自邮箱AOSP应用程序.
https://github.com/commonsguy/cw-omnibus/tree/master/Animation/ThreePane
@weakwire的解决方案让人想起了我的解决方案,尽管他使用的是classic Animation
而不是动画师,他使用RelativeLayout
条规则来强制定位.从赏金的Angular 来看,他很可能会得到赏金,除非其他有更巧妙解决方案的人给出答案.
简而言之,该项目中的ThreePaneLayout
个是LinearLayout
个子类,设计用于与三个子元素一起从事景观工作.这些子 node 的宽度可以在布局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;
}
}
然而,在运行时,无论您最初如何设置宽度,当您第一次使用hideLeft()
从显示问题所指的片段A和B切换到片段B和C时,宽度管理将由ThreePaneLayout
接管.在ThreePaneLayout
的术语中——它与片段没有特定的联系——这三个片段是left
、middle
和right
.当你拨打hideLeft()
时,我们会记录left
和middle
的尺寸,并将三者中任何一个使用的任何重量归零,这样我们就可以完全控制尺寸.在时间点hideLeft()
时,我们将right
的大小设置为原来的middle
.
动画有两个方面:
- 使用硬件层,使用
ViewPropertyAnimator
向左转换宽度为left
的三个小部件
- 在自定义伪属性
middleWidth
上使用ObjectAnimator
将middle
宽度从最初的宽度更改为left
(对于所有这些,使用AnimatorSet
和ObjectAnimators
可能是一个更好的主意,尽管这目前还有效)
(也有可能middleWidth
ObjectAnimator
否定硬件层的价值,因为这需要相当连续的无效)
(我对动画的理解可能仍有差距,而且我喜欢附加说明)
最终的效果是,left
张幻灯片从屏幕上滑下,middle
张幻灯片滑到left
的原始位置和大小,right
张幻灯片在middle
的正后方平移.
showLeft()
只是简单地颠倒过程,使用相同的动画师组合,只是方向颠倒.
该活动使用一个ThreePaneLayout
来容纳一对ListFragment
小部件和一个Button
. Select 左侧片段中的某个内容会添加(或更新)中间片段.在中间片段中 Select 某物设置Button
的标题,再加上ThreePaneLayout
的执行hideLeft()
.如果我们把左边藏起来,按一下,就会执行showLeft()
;否则,返回将退出活动.由于这并没有使用FragmentTransactions
来影响动画,所以我们只能自己管理"后堆栈".
上面链接的项目使用本机片段和本机animator框架.我有同一个项目的另一个版本,使用Android Support fragments backport和NineOldAndroids来制作动画:
https://github.com/commonsguy/cw-omnibus/tree/master/Animation/ThreePaneBC
后端口在第一代Kindle Fire上运行良好,尽管考虑到较低的硬件规格和缺乏硬件加速支持,动画有点不稳定.在Nexus7和其他当前一代的平板电脑上,这两种实现似乎都很顺利.
我当然愿意接受关于如何改进这个解决方案的 idea ,或者其他比我在这里所做的工作(或者使用的@WINKWIRE)提供明显优势的解决方案.
再次感谢所有做出贡献的人!