`
wx1568145609
  • 浏览: 17536 次
文章分类
社区版块
存档分类
最新评论

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

 
阅读更多

效果Gif

实现思路

此效果实现用到了: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

转载于:https://my.oschina.net/zhongsm/blog/3040416

分享到:
评论

相关推荐

    安卓Android源码——高仿小米launcher(ZAKER)跨屏拖动item.zip

    本压缩包"安卓Android源码——高仿小米launcher(ZAKER)跨屏拖动item.zip"提供了对小米Launcher(ZAKER风格)跨屏拖动item功能的实现,这对于开发者来说是一个宝贵的资源,可以帮助他们学习如何构建类似的应用启动器...

    安卓Android源码——高仿小米launcher(ZAKER)跨屏拖动item.rar

    "安卓Android源码——高仿小米launcher(ZAKER)跨屏拖动item.rar" 这个标题揭示了我们即将探讨的核心内容:一个针对Android操作系统的源代码项目,该项目旨在模仿小米(Xiaomi)启动器(Launcher)的特性,特别是...

    安卓仿ios计算器

    在Android平台上,开发一款仿iOS风格的计算器应用是一项有趣且具有挑战性的任务。"安卓仿ios计算器"项目旨在实现一个与iOS系统计算器相似的界面和功能,为用户提供熟悉的使用体验。在这个项目中,主要涉及了两个关键...

    安卓Android源码——高仿墨迹天气背景动画效果-云,风,雪等.zip

    这个压缩包"安卓Android源码——高仿墨迹天气背景动画效果-云,风,雪等.zip"提供了实现类似墨迹天气应用中背景动画的技术,包括云、风、雪等元素。这些动画效果能够为用户营造出更生动、真实的天气感知,是移动应用...

    仿小米计算器

    6. 可能的动画效果:使用Android的Animation库或属性动画系统,提升用户体验。 总的来说,"仿小米计算器"项目是一个综合性的Android开发实践,涵盖了UI设计、事件处理、计算逻辑、数据存储等多个方面,对于提升...

    Android动画之——圆形进度条加波浪线

    本篇文章将探讨如何实现一个特殊的UI组件——圆形进度条加波浪线的效果。这个组件通常用于展示某个任务的进度,同时通过波浪线的变化为用户带来视觉上的动态反馈。 首先,我们需要创建一个新的View类,继承自...

    Android应用源码——仿美图秀秀和IOS系统的相机胶卷.zip

    这份名为“Android应用源码——仿美图秀秀和IOS系统的相机胶卷”的压缩包,显然是一个关于Android应用开发的学习资源。它包含了实现类似美图秀秀应用和iOS系统相机胶卷功能的源代码,这对于Android开发者,尤其是对...

    安卓Android源码——仿ios音量调节的效果.zip

    "安卓Android源码——仿ios音量调节的效果.zip"这个压缩包文件就是针对这一需求,提供了源代码,帮助开发者创建一个类似iOS设备上的音量调节界面。在iOS系统中,音量调节界面通常以滑块的形式出现,简洁且直观。以下...

    悬停抽屉控件 —— 仿知乎收藏夹

    综上所述,"悬停抽屉控件 —— 仿知乎收藏夹"涉及的知识点包括Android手势检测、动画处理、响应式布局、触摸反馈、性能优化和状态管理。通过深入理解和实践这些技术,开发者能够创建出具有高级交互体验的应用界面。

    安卓Android源码——仿Iphone抖动效果ShakeIcon.zip

    这个压缩包"安卓Android源码——仿Iphone抖动效果ShakeIcon.zip"提供了一个示例,它旨在模仿苹果iPhone上的图标抖动效果,这通常用于表示错误或者用户需要进行某种操作。通过这个示例,开发者可以学习如何在Android...

    安卓Android源码——高仿微信导航页开门效果源码.zip

    【标题】"安卓Android源码——高仿微信导航页开门效果源码.zip" 提供的是一段用于实现类似微信应用中导航页动态开门效果的源代码。这种效果通常出现在主界面与侧滑菜单的切换过程中,给人一种新颖且交互性强的用户...

    安卓Android源码——仿真翻页效果.zip

    本压缩包“安卓Android源码——仿真翻页效果.zip”包含了一个实现此类效果的示例源码,下面我们将详细探讨这一技术。 首先,仿真翻页效果的核心在于对动画的精确控制。在Android中,我们可以使用`...

    安卓Android源码——仿微信popupwindow.zip

    "安卓Android源码——仿微信popupwindow.zip" 这个标题表明了我们即将探讨的是一个关于Android平台的源代码项目,它的主要目的是实现类似微信应用中的PopupWindow功能。PopupWindow是Android系统中一个非常重要的UI...

    安卓Android源码——android 仿真翻页效果.rar

    这个压缩包文件"安卓Android源码——android 仿真翻页效果.rar"可能包含了一个示例项目,用于演示如何在Android平台上创建这种动态、逼真的翻页动画。以下将详细介绍实现这种效果的关键知识点。 1. **页面滑动...

    安卓Android源码——高仿墨迹天气背景动画效果-云,风,雪等.rar

    这篇文档将深入解析《安卓Android源码——高仿墨迹天气背景动画效果-云,风,雪等.rar》这个压缩包中的内容,该资源主要涵盖了Android平台上实现动态天气背景效果的技术,包括云、风和雪等元素的动画实现。...

    android 动画实现——ViewFilpper

    `ViewFlipper`是Android SDK提供的一种方便的布局组件,专门用于实现视图之间的切换效果,比如翻页、滑动等。相较于传统的`Animation`类,`ViewFlipper`具有更丰富的交互特性,比如能够暂停和恢复动画,还能自动记住...

    安卓Android源码——android 仿真翻页效果.zip

    这个压缩包文件“安卓Android源码——android 仿真翻页效果.zip”包含了实现这种效果的源代码,这对于开发者来说是一个很好的学习资源。 在Android平台上,模拟真实的翻页效果通常涉及到对视图(View)的复杂操作,...

    android自定义效果——随机抽奖

    总的来说,实现"android自定义效果——随机抽奖"涉及的知识点包括:自定义视图的创建与绘制、Android动画机制(特别是`ObjectAnimator`)、随机数生成、触摸事件处理以及视图的优化。掌握这些技能,开发者可以创建出...

    安卓Android源码——仿赶集生活android客户端的介绍动画界面.zip

    本资源"安卓Android源码——仿赶集生活android客户端的介绍动画界面.zip"提供了一个实例,用于讲解如何在Android应用中创建类似赶集生活客户端的启动介绍动画界面。通过对这份源码的学习,开发者可以深入了解Android...

Global site tag (gtag.js) - Google Analytics