实现思路
此效果实现用到了: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.缩放比例需要计算数字区的宽度与整个键盘区宽度的比值。