转自:Android 布局平铺展开效果的属性动画 - 博客频道 - CSDN.NET http://blog.csdn.net/debbytang/article/details/68496728
刚在整理项目代码,看到之前写的一个布局延伸平铺展开效果的代码堆积在Activity里面,然后整理之后简单封装了一下,在此记录。有需要的童鞋可以参考一下~
其实这里直接用布局的 gone 和 visible 属性也可以实现,而且最方便快捷,不过一瞬间的显示隐藏显得没那么好看,也不是我们想要的,所以用了属性动画去实现。Android 3.0 之后,Google 加入了属性动画框架,关于属性动画的详细介绍网上相关文章很多,具体这里可以参考鸿洋大神的文章:
[文章链接] http://blog.csdn.net/lmj623565791/article/details/38067475
布局文件就不贴了,没什么必要。主要的布局结构展示动画效果部分如下图:
这里写图片描述
外层为一个相对布局,然后红色矩形框内为两个线性布局,最下面的箭头为一个 Textview 。
然后主要效果如上文动图所示,其动画效果我抽取了一个简单的工具类,方便复用。
主要功能代码如下:
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
/**
* Created by debbytang.
* Description:显示隐藏布局的属性动画(铺展)
* Date:2017/3/30.
*/
public class HiddenAnimUtils {
private int mHeight;//伸展高度
private View hideView,down;//需要展开隐藏的布局,开关控件
private RotateAnimation animation;//旋转动画
/**
* 构造器(可根据自己需要修改传参)
* @param context 上下文
* @param hideView 需要隐藏或显示的布局view
* @param down 按钮开关的view
* @param height 布局展开的高度(根据实际需要传)
*/
public static HiddenAnimUtils newInstance(Context context,View hideView,View down,int height){
return new HiddenAnimUtils(context,hideView,down,height);
}
private HiddenAnimUtils(Context context,View hideView,View down,int height){
this.hideView = hideView;
this.down = down;
float mDensity = context.getResources().getDisplayMetrics().density;
mHeight = (int) (mDensity * height + 0.5);//伸展高度
}
/**
* 开关
*/
public void toggle(){
startAnimation();
if (View.VISIBLE == hideView.getVisibility()) {
closeAnimate(hideView);//布局隐藏
} else {
openAnim(hideView);//布局铺开
}
}
/**
* 开关旋转动画
*/
private void startAnimation() {
if (View.VISIBLE == hideView.getVisibility()) {
animation = new RotateAnimation(180, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
} else {
animation = new RotateAnimation(0, 180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
}
animation.setDuration(30);//设置动画持续时间
animation.setInterpolator(new LinearInterpolator());
animation.setRepeatMode(Animation.REVERSE);//设置反方向执行
animation.setFillAfter(true);//动画执行完后是否停留在执行完的状态
down.startAnimation(animation);
}
private void openAnim(View v) {
v.setVisibility(View.VISIBLE);
ValueAnimator animator = createDropAnimator(v, 0, mHeight);
animator.start();
}
private void closeAnimate(final View view) {
int origHeight = view.getHeight();
ValueAnimator animator = createDropAnimator(view, origHeight, 0);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
view.setVisibility(View.GONE);
}
});
animator.start();
}
private ValueAnimator createDropAnimator(final View v, int start, int end) {
ValueAnimator animator = ValueAnimator.ofInt(start, end);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator arg0) {
int value = (int) arg0.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = v.getLayoutParams();
layoutParams.height = value;
v.setLayoutParams(layoutParams);
}
});
return animator;
}
}
使用:
在你需要的点击事件那里直接调用上面的工具类的 toggle() 方法就行了,就一行代码,如下:
@Override
public void onClick(View v) {
super.onClick(v);
switch (v.getId()) {
...
case R.id.down:
HiddenAnimUtils.newInstance(xxx.this,xxx,down,77).toggle();
breake;
...
}
}
补充:不想通过指定高度显示,而是根据控件自身的高度来设置平铺展开的高度
通过学习 当View为GONE状态时获取View的宽高 - zhuhai__yizhi的专栏 - CSDN博客 http://blog.csdn.net/zhuhai__yizhi/article/details/50897554得知当View为GONE状态时获取View的宽高方法,通过View.post()获取View的宽高引发的两个问题:1post的Runnable何时被执行,2为何View需要layout两次;以及发现Android的一个小bug - scnuxisan225的专栏 - CSDN博客 http://blog.csdn.net/scnuxisan225/article/details/49815269得知其原理逻辑。
步骤如下:
1.在xml布局把想平铺展开的控件设置为 android:visibility=”visible”
2.通过代码设置其不可见:
public class HomeActivity extends AppCompatActivity {
@BindView(R.id.tv_showLayout)
TextView tvShowLayout;
@BindView(R.id.layout_info)
LinearLayout layoutInfo;
private int height = 10;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
ButterKnife.bind(this);
// Android是消息驱动的模式,View.post的Runnable任务会被加入任务队列,
// 并且等待第一次TraversalRunnable执行结束后才执行,此时已经执行过一次measure、layout过程了,
// 所以在后面执行post的Runnable时,已经有measure的结果,因此此时可以获取到View的宽高
layoutInfo.post(new Runnable() {
@Override
public void run() {
//获取需要平铺展开的控件高度
height = layoutInfo.getHeight();
layoutInfo.setVisibility(View.GONE);
}
});
}
@OnClick({R.id.tv_showLayout})
public void onViewClicked(View view) {
HiddenAnimUtils.newInstance(HomeActivity.this, layoutInfo, tvShowLayout, height).toggle();
}
}