好的,这是我自己的解决方案,它是根据问题评论中@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
-它没有具体联系的片段-三块是left
,middle
和right
。在您致电时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使用的内容)具有明显的优势。
再次感谢大家的贡献!