关于view的visible和gone动画的坑
2016-06-13 本文已影响6652人
肉团先生
title: "关于view的visible和gone动画的坑"
date: 2016-05-31 00:38:33
categories: android
tags:[android,Animator]
需求:如图所示,我需要做一个View
的显示和消失的动画。看似简单,但是坑还是不少。消失的动画很简单,难点在visible
的动画。因为动画是发生在visible
之后,所以导致会生硬的将其他的view移到右边,然后在进行动画.
从传统动画到属性动画,各种尝试,结果失败告终。
- 直接让整个view进行左移让checkbox移动到屏幕外,然后再需要的时候进行移动回来,结果还是很生硬,不知道为什么
跟同事讨论下,他的建议是:visible
后快速的一个动画左移,然后再一个动画缓慢的右移回来。实践下效果能够缓慢出来,但是view会一闪而过,体验十分不好。
按着这个思路,我们可以移动其他的view,通过遮住这个checkbox,来达到效果,那么只需要改变布局即可。
效果: enter image description here<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/bg_white"
android:minHeight="@dimen/dp_90">
<CheckBox
android:id="@+id/cb_select"
android:layout_width="@dimen/dp_36"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="@dimen/dp_12"
android:button="@null"
android:checked="@{data.isSelected}"
android:drawableLeft="@drawable/selector_checkbox_red"
android:maxHeight="@dimen/dp_90"
android:onClick="@{data.onClick()}"
bind:visibility="@{data.isEditMode}" />
<LinearLayout
bind:move="@{data.isEditMode}"
android:layout_centerVertical="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dp_9">
<ImageView
android:id="@+id/iv_goods_pic"
android:layout_width="@dimen/dp_61"
android:layout_height="@dimen/dp_61"
android:layout_centerVertical="true"
android:layout_gravity="center_vertical"
android:background="@drawable/shape_imageview_bg"
android:src="@{data.img}" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dp_13"
android:layout_marginTop="@dimen/dp_21"
android:layout_weight="1"
android:text="@{data.name}"
android:textColor="@color/black_43"
android:textSize="@dimen/font_11" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:layout_marginBottom="@dimen/dp_12"
android:layout_marginTop="@dimen/dp_14"
android:text="@{XmlUtil.formatPrice(data.skuPrice)}"
android:textColor="@color/font_red_41"
android:textSize="@dimen/font_11" />
</LinearLayout>
</RelativeLayout>
对应的绑定代码:
@BindingAdapter("bind:visibility")
public static void showVisibility(final View view, boolean visible) {
if (view.getTag() == null) {
view.setTag(true);
}
if (visible) {
view.animate().alpha(1);
} else {
view.animate().alpha(0);
}
}
@BindingAdapter("bind:move")
public static void animMove(final ViewGroup viewGroup, boolean visible) {
if (viewGroup.getTag() == null) {
viewGroup.setTag(true);
}
if (visible) {
viewGroup.animate().translationX(Systems.dpToPx(viewGroup.getContext(), 36));
} else {
viewGroup.animate().translationX(0);
}
}
另一种实现方式思路--通过改变高度达到要求
通过改变将要显示view的高度/宽度来达到要求。即:宽度/高度 从0开始逐步变成指定的高度。因为visible的时候,高度为0所以看不到一闪而过的情况。
来源:android群英传
效果图:
下面提供关键代码:
private void animOpen(final View view){
view.setVisibility(View.VISIBLE);
ValueAnimator va = createDropAnim(view,0,mHiddenViewMeasuredHeight);
va.start();
}
private void animClose(final View view){
int origHeight = view.getHeight();
ValueAnimator va = createDropAnim(view,origHeight,0);
va.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
view.setVisibility(View.GONE);
}
});
va.start();
}
/**
* 使用动画的方式来改变高度解决visible不一闪而过出现
* @param view
* @param start 初始状态值
* @param end 结束状态值
* @return
*/
private ValueAnimator createDropAnim(final View view,int start,int end) {
ValueAnimator va = ValueAnimator.ofInt(start, end);
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (int) animation.getAnimatedValue();//根据时间因子的变化系数进行设置高度
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
layoutParams.height = value;
view.setLayoutParams(layoutParams);//设置高度
}
});
return va;
}
这种方式的缺点很明显:只能从上而下出现,为什么?
因为android的view默认的坐标系是左上角,也就意味着高度只能从上而下扩展。
当遇到从下而上出现则束手无策。开发还真的遇到这样的需求。但有之前的经验+转场动画setAlpha
的思路,想到以下方式:
- xml设置view为gone
- 当设置为visible之前,将view设置为
setAlpha(0)
- 快速的动画(
setDuration(1)
),将view移动到当前显示屏幕之外并设置setAlpha(1)
- 正常的动画,从下移动到上出现。
效果图:
实现代码:
binding.btnShow.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//bottom layout的出现于消失
if(isFirst){
binding.llLayoutBottom.setAlpha(0);
binding.llLayoutBottom.setVisibility(View.VISIBLE);
}
Tasks.handler().postDelayed(new Runnable() {
@Override
public void run() {
if(isEdit&&isFirst){
logger.e("bottom="+binding.llLayoutBottom.getBottom());
binding.llLayoutBottom.animate()
.y(binding.llLayoutBottom.getBottom())
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if(isFirst){
binding.llLayoutBottom.setAlpha(1);
logger.e("getTop="+binding.llLayoutBottom.getTop());
binding.llLayoutBottom.animate().y(binding.llLayoutBottom.getTop()).setDuration(1000);
isFirst=false;
}
}
})
.setDuration(1);
}else if(isEdit){//非第一次的情况执行
logger.e("getTop="+binding.llLayoutBottom.getTop());
binding.llLayoutBottom.animate()
.y(binding.llLayoutBottom.getTop()).setDuration(1000);
}
else {
logger.e("getBottom="+binding.llLayoutBottom.getBottom()+"getHeight"+binding.llLayoutBottom.getHeight());
binding.llLayoutBottom.animate()
.y(binding.llLayoutBottom.getBottom()).setDuration(1000);
}
}
}, 500);
}
});
上述代码总结:
- 为了偷懒直接使用
animate()
方法,却忘记了一旦进行监听AnimatorListener
后,只要每执行一次动画,对应的方法就会执行一次,导致在打log中onAnimationEnd
一直执行,迫使之前实现的效果一直都不正确。动画总是移动回来再移动回去。
- 关于坐标系的问题,发现使用
屏幕的高度-llLayoutBottom.getHeight()
不靠谱,对应的view只出现一半。暂时不明,所以直接使用getBottom、getTop
来设置。可以发现动画的移动并没有其真实的坐标:getBottom、getTop
来时原来的。 - 打
log+debug
会加快调试的时间