android动画篇——仿小米计算器切换动画效果

在这里插入图片描述

实现思路

此效果实现用到了:LayoutTransition和ObjectAnimator的相关知识
思路:当点击切换按钮时顶部ViewGroup和左侧ViewGroup可见性设置为Gone,并自动开始执行LayoutTransition的DISAPPEARING动画,同时中间的数字键盘区执行放大动画效果,反之同理。

代码实现

/**
* 实现小米计算器科学计算器与普通计算器的切换动画效果
*/
public class XiaomiActivity extends AppCompatActivity {
   @BindView(R.id.calculator_rel)
   RelativeLayout calculatorRel;       //总布局,需要指定LayoutTransition
   @BindView(R.id.calculator_top_lin)
   LinearLayout calculatorTopLin;      //三角函数计算区
   @BindView(R.id.calculator_left_lin)
   LinearLayout calculatorLeftLin;     //其他计算区
   @BindView(R.id.calculator_num_lin)
   LinearLayout calculatorNumLin;      //数字键盘区
   @BindView(R.id.change_btn)
   Button translateBtn;                //转换按钮

   //width缩放比例
   private float widthZoomMultiple = 0f;
   //height缩放比例
   private float heightZoomMultiple = 0f;

   /** 动画时长 */
   private static final int ANIMATOR_DURATION = 300;
   //设置Interpolator,动画效果上更接近小米计算器动画效果
   private Interpolator interpolator = new DecelerateInterpolator();

   /**
    * 启动Activity
    * @param context context
    */
   public static void startSelf(Context context){
       Intent intent = new Intent(context, XiaomiActivity.class);
       context.startActivity(intent);
   }

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_xiaomi);

       ButterKnife.bind(this);
   }

   @Override
   public void onWindowFocusChanged(boolean hasFocus) {
       super.onWindowFocusChanged(hasFocus);
       if(hasFocus){
           setLayoutTransition();
       }
   }

   @OnClick(R.id.change_btn)
   protected void onClick(){
       if(widthZoomMultiple == 0 || heightZoomMultiple == 0){
           //获取缩放比例
           widthZoomMultiple = getWidthZoomMultiple();
           heightZoomMultiple = getHeightZoomMultiple();
       }

       if(calculatorTopLin.getVisibility() == View.VISIBLE){
           calculatorTopLin.setVisibility(View.GONE);
           calculatorLeftLin.setVisibility(View.GONE);

           startEnlargeAnimator();
       } else{
           calculatorTopLin.setVisibility(View.VISIBLE);
           calculatorLeftLin.setVisibility(View.VISIBLE);

           startNarrowAnimator();
       }
   }

   /**
    * 获取width缩放比例
    * @return
    */
   private float getWidthZoomMultiple(){
       return (float) calculatorRel.getWidth() / calculatorNumLin.getWidth();
   }

   /**
    * 获取height缩放比例
    * @return
    */
   private float getHeightZoomMultiple(){
       return (float) calculatorRel.getHeight() / calculatorNumLin.getHeight();
   }

   /**
    * 放大动画
    */
   private void startEnlargeAnimator(){
       calculatorRel.setPivotX(calculatorRel.getWidth());
       calculatorRel.setPivotY(calculatorRel.getHeight());

       AnimatorSet animatorSet = new AnimatorSet();
       //放大动画
       Animator enlargeAnimatorX = ObjectAnimator.ofFloat(calculatorRel,"scaleX",1,widthZoomMultiple);
       Animator enlargeAnimatorY = ObjectAnimator.ofFloat(calculatorRel,"scaleY",1,widthZoomMultiple);

       animatorSet.setDuration(ANIMATOR_DURATION);
       animatorSet.play(enlargeAnimatorX).with(enlargeAnimatorY);
       animatorSet.setInterpolator(interpolator);
       animatorSet.start();
   }

   /**
    * 缩小动画
    */
   private void startNarrowAnimator(){
       calculatorRel.setPivotX(calculatorRel.getWidth());
       calculatorRel.setPivotY(calculatorRel.getHeight());

       AnimatorSet animatorSet = new AnimatorSet();
       //缩小动画
       Animator narrowAnimatorX = ObjectAnimator.ofFloat(calculatorRel,"scaleX",widthZoomMultiple, 1);
       Animator narrowAnimatorY = ObjectAnimator.ofFloat(calculatorRel,"scaleY",widthZoomMultiple, 1);
       animatorSet.setDuration(ANIMATOR_DURATION);
       animatorSet.play(narrowAnimatorX).with(narrowAnimatorY);
       animatorSet.setInterpolator(interpolator);
       animatorSet.start();
   }

   /**
    * 设置切换动画
    */
   private void setLayoutTransition(){
       LayoutTransition mTransition = new LayoutTransition();
       mTransition.setDuration(LayoutTransition.DISAPPEARING,ANIMATOR_DURATION);
       mTransition.setDuration(LayoutTransition.APPEARING, ANIMATOR_DURATION);

       mTransition.setStagger(LayoutTransition.DISAPPEARING, 0);
       mTransition.setStagger(LayoutTransition.APPEARING, 0);

       //View移除动画
       PropertyValuesHolder alphaHolder = PropertyValuesHolder.ofFloat("alpha",1, 0);
       ObjectAnimator disAppearingAnimator = ObjectAnimator.ofPropertyValuesHolder(this,alphaHolder);
       disAppearingAnimator.setInterpolator(interpolator);
       disAppearingAnimator.setDuration(mTransition.getDuration(LayoutTransition.DISAPPEARING));
       mTransition.setAnimator(LayoutTransition.DISAPPEARING, disAppearingAnimator);

       //View显示动画
       PropertyValuesHolder alphaAppearingHolder = PropertyValuesHolder.ofFloat("alpha",0, 1);
       ObjectAnimator appearingAnimator = ObjectAnimator.ofPropertyValuesHolder(this,alphaAppearingHolder);
       appearingAnimator.setInterpolator(interpolator);
       appearingAnimator.setDuration(mTransition.getDuration(LayoutTransition.APPEARING));
       mTransition.setAnimator(LayoutTransition.APPEARING, appearingAnimator);

       calculatorRel.setLayoutTransition(mTransition);
   }
}

activity_xiaomi.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.zhong.property_animation.XiaomiActivity" >

    <RelativeLayout
        android:id="@+id/calculator_rel"
        android:layout_width="match_parent"
        android:layout_height="400dp"
        android:animateLayoutChanges="true"
        android:layout_alignParentBottom="true"
        android:background="#FFFFFF">

        <include
            layout="@layout/layout_xiaomi_num"
            android:id="@+id/calculator_num_lin"
            android:layout_width="280dp"
            android:layout_height="280dp"
            android:background="@drawable/stroke_bg"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"/>

        <LinearLayout
            android:id="@+id/calculator_top_lin"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            android:layout_above="@id/calculator_num_lin"
            android:background="#ededed"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent">
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:orientation="horizontal">
                <Button
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:text="deg"
                    android:textAllCaps="false"
                    android:textSize="20sp"/>
                <Button
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:text="sin"
                    android:textAllCaps="false"
                    android:textSize="20sp"/>
                <Button
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:text="cos"
                    android:textAllCaps="false"
                    android:textSize="20sp"/>
                <Button
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:text="tan"
                    android:textAllCaps="false"
                    android:textSize="20sp"/>
            </LinearLayout>
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:orientation="horizontal">
                <Button
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:text="lg"
                    android:textAllCaps="false"
                    android:textSize="20sp"/>
                <Button
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:text="ln"
                    android:textAllCaps="false"
                    android:textSize="20sp"/>
                <Button
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:text="("
                    android:textAllCaps="false"
                    android:textSize="20sp"/>
                <Button
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:text=")"
                    android:textAllCaps="false"
                    android:textSize="20sp"/>
            </LinearLayout>
        </LinearLayout>

        <LinearLayout
            android:id="@+id/calculator_left_lin"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_below="@id/calculator_top_lin"
            android:layout_toLeftOf="@id/calculator_num_lin"
            android:background="#ededed"
            android:orientation="vertical"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/calculator_top_lin">
            <Button
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:text="X!"
                android:textAllCaps="false"
                android:textSize="20sp"/>
            <Button
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:text="1/X"
                android:textAllCaps="false"
                android:textSize="20sp"/>
            <Button
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:text="π"
                android:textAllCaps="false"
                android:textSize="20sp"/>
            <Button
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:text="e"
                android:textAllCaps="false"
                android:textSize="20sp"/>
        </LinearLayout>

    </RelativeLayout>


    <TextView
        android:id="@+id/title_tv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="1324"
        android:background="#FFFFFF"
        android:gravity="bottom|right"
        android:textColor="@android:color/black"
        android:textSize="35sp"
        android:padding="10dp"
        android:layout_above="@id/calculator_rel"/>
</RelativeLayout>

layout_xiaomi_num.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">
        <Button
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:textSize="20sp"
            android:text="1"/>
			
        ...省略四个按钮
		
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">
        <Button
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:textSize="20sp"
            android:text="4"/>
			
        ...省略四个按钮
		
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">
        <Button
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:textSize="20sp"
            android:text="7"/>
			
        ...省略四个按钮
		
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">
        <Button
            android:id="@+id/change_btn"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:textSize="20sp"
            android:text="切换"/>
			
        ...省略四个按钮
		
    </LinearLayout>

</LinearLayout>

代码并不复杂,如果对属性动画掌握不全面的可以看上一篇博客Android动画篇——Property Animation(属性动画)。对属性动画稍微熟悉点的都可以看懂,而且可以发现这代码简单到没什么可说的。那为什么还要单独开一篇来写呢?其实实现过程中还是碰到一些问题的,单独一篇写下来一来为了加深对属性动画应用的理解,二来也为了之后有类似的实现时可以避开这些坑。

只关注实现方式的看到这里就可以了,代码注解比较清楚也很好理解,下面都是些关于实现思路和实现过程中的一些坑。

选定方案

要实现这种效果其实方案有多种,最简单的一种用Animator对整个按键布局进行放大动画,并同时对顶部与左侧ViewGroup执行alpha动画,监听动画结束为顶部与左侧View设置可见性为Gone。这样实现需要自己通过设置动画监听设置View的可见性未免稍显复杂而且不够优雅。所以我选择了LayoutTransition + ObjectAnimator的实现方案。当View的可见性发生改变时LayoutTransition会自动执行对应的动画效果。

布局编写

选定方案之后,我们来实现布局。布局乍看很简单,不过是控件的堆砌用LinearLayout、RelativeLayout和ConstraintLayout都可以很简单的实现这种UI效果,但是因为我们在上一步选定了实现方案,而且顶部和左侧View会GONE或者VISIBLE,所以整个布局应该是以右下角的数字键盘为基准、为锚点进行编写(要不然当顶部和左侧View可见性发生改变时会影响数字键盘的相对位置)。

动画实现

分为两部分:

  • 1.LayoutTransition实现顶部与左侧View的渐隐渐现效果。
  • 2.整个按键区的缩放动画效果。

LayoutTransition部分比较简单,代码都交代的比较清楚了而且上一篇我们已经详细讲解过LayoutT的使用了。关于按键区布局的缩放动画需要注意的是:1.缩放锚点定在屏幕右下角 2.缩放比例需要计算数字区的宽度与整个键盘区宽度的比值。

源码:animation-project

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值