我有以下布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent">
<FrameLayout android:id="@+id/viewgroup_left"
android:layout_height="match_parent"
android:layout_weight="2"
android:layout_width="0dp">
... children ...
</FrameLayout>
<LinearLayout android:id="@+id/viewgroup_right"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_width="0dp"
android:orientation="vertical">
... children ...
</LinearLayout>
</LinearLayout>
我最终得到这样的东西:
+------------------------+------------+
| | |
| | |
| Left | Right |
| | |
| | |
+------------------------+------------+
当切换某个切换时,我想要为Left设置动画,使其宽度扩展以填充整个屏幕。同时,我想为Right的宽度设置动画,使其缩小到零。之后,当再次切换切换时,我需要将事物恢复到上述状态。
我尝试编写自己调用View.getWidth()
的动画但是当我将动画重新设置为该值时(通过设置View.getLayoutParams().width
),它比开始时更宽。我怀疑我做错了。我还阅读了有关Honeycomb动画内容的所有文档,但我不想翻译或缩放...我想为布局宽度属性设置动画。我找不到这样的例子。
这样做的正确方法是什么?
答案 0 :(得分:12)
由于没有人帮助你,我的第一个答案是如此混乱,这次我会试着给你正确答案; - )
其实我喜欢这个想法,我认为这是一个很好的视觉效果,可能对一群人有用。我会实现右视图的溢出(我认为缩小看起来很奇怪,因为文本扩展到底部)。
但无论如何,这里的代码完全正常(你甚至可以在动画时切换)。
快速解释:
您使用布尔值为您的方向调用toggle
,这将启动处理程序动画调用循环。这将根据方向和过去时间增加或减少两个视图的权重(用于平滑计算和动画)。只要动画调用循环没有到达开始或结束位置,它就会自动调用。
布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:weightSum="10"
android:id="@+id/slide_layout">
<TextView
android:layout_weight="7"
android:padding="10dip"
android:id="@+id/left"
android:layout_width="0dip"
android:layout_height="fill_parent"></TextView>
<TextView
android:layout_weight="3"
android:padding="10dip"
android:id="@+id/right"
android:layout_width="0dip"
android:layout_height="fill_parent"></TextView>
</LinearLayout>
活动:
public class TestActivity extends Activity {
private static final int ANIMATION_DURATION = 1000;
private View mSlidingLayout;
private View mLeftView;
private View mRightView;
private boolean mAnimating = false;
private boolean mLeftExpand = true;
private float mLeftStartWeight;
private float mLayoutWeightSum;
private Handler mAnimationHandler = new Handler();
private long mAnimationTime;
private Runnable mAnimationStep = new Runnable() {
@Override
public void run() {
long currentTime = System.currentTimeMillis();
float animationStep = (currentTime - mAnimationTime) * 1f / ANIMATION_DURATION;
float weightOffset = animationStep * (mLayoutWeightSum - mLeftStartWeight);
LinearLayout.LayoutParams leftParams = (LinearLayout.LayoutParams)
mLeftView.getLayoutParams();
LinearLayout.LayoutParams rightParams = (LinearLayout.LayoutParams)
mRightView.getLayoutParams();
leftParams.weight += mLeftExpand ? weightOffset : -weightOffset;
rightParams.weight += mLeftExpand ? -weightOffset : weightOffset;
if (leftParams.weight >= mLayoutWeightSum) {
mAnimating = false;
leftParams.weight = mLayoutWeightSum;
rightParams.weight = 0;
} else if (leftParams.weight <= mLeftStartWeight) {
mAnimating = false;
leftParams.weight = mLeftStartWeight;
rightParams.weight = mLayoutWeightSum - mLeftStartWeight;
}
mSlidingLayout.requestLayout();
mAnimationTime = currentTime;
if (mAnimating) {
mAnimationHandler.postDelayed(mAnimationStep, 30);
}
}
};
private void toggleExpand(boolean expand) {
mLeftExpand = expand;
if (!mAnimating) {
mAnimating = true;
mAnimationTime = System.currentTimeMillis();
mAnimationHandler.postDelayed(mAnimationStep, 30);
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.slide_test);
mLeftView = findViewById(R.id.left);
mRightView = findViewById(R.id.right);
mSlidingLayout = findViewById(R.id.slide_layout);
mLeftStartWeight = ((LinearLayout.LayoutParams)
mLeftView.getLayoutParams()).weight;
mLayoutWeightSum = ((LinearLayout) mSlidingLayout).getWeightSum();
}
}
答案 1 :(得分:2)
只需将我的2美分加到Knickedi的优秀答案中 - 以防有人需要它:
如果使用权重设置动画,则最终会在包含的视图和视图组中出现剪切/非剪切问题。如果使用具有权重的视图组作为片段容器,则尤其如此。为了克服它,您可能需要为有问题的子视图和视图组/片段容器的边距设置动画。
并且,要一起完成所有这些事情,最好选择ObjectAnimator和AnimatorSet(如果可以使用它们),以及一些实用程序类,如MarginProxy
答案 2 :(得分:1)
@ knickedi发布的解决方案的另一种方法是使用ObjectAnimator而不是Runnable。我们的想法是使用ObjectAnimator来调整左视图和右视图的权重。但是,视图需要进行自定义,以便权重可以作为ObjectAnimator动画的属性公开。
首先,定义一个自定义视图(使用LinearLayout作为示例):
public class CustomLinearLayout extends LinearLayout {
public CustomLinearLayout(Context context) {
super(context);
}
public CustomLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setMyWeight(float value) {
LinearLayout.LayoutParams p = (LinearLayout.LayoutParams)getLayoutParams();
p.weight = value;
requestLayout();
}
}
然后,更新布局XML以使用此自定义线性布局。
然后,当您需要切换动画时,请使用ObjectAnimator:
ObjectAnimator rightView = ObjectAnimator.ofFloat(viewgroup_right, "MyWeight", 0.5f, 1.0f);
ObjectAnimator leftView = ObjectAnimator.ofFloat(viewgroup_left, "MyWeight", 0.5f, 0.0f);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(1000); // 1 second of animation
animatorSet.playTogether(rightView, leftView);
animatorSet.start();
上面的代码假设两个视图都是线性布局,并且开始时的重量只有一半。动画会将右视图扩展为全重(因此左边的视图会被隐藏)。请注意,ObjectAnimator使用自定义线性布局的“MyWeight”属性进行动画处理。 AnimatorSet用于将左右ObjectAnimator绑定在一起,因此动画看起来很流畅。
这种方法减少了编写可运行代码及其中的权重计算的需要,但它需要定义一个自定义类。