对于这一章来说,主要是来讲解属性动画的

真实的世界里面有许多的动态元素,那么如果我们也想让用户界面动起来,那么我们也应该需要让界面的元素从一个位置动态移动到另一个位置

下面是来开发一个模拟落日的动画,当我们按住屏幕的时候,太阳就会下山

(1)首先,我们需要创建一个项目,和保证这个项目的minSdkVersion设置为API19以上的,这边我们需要有色彩,所以我们需要在color.xml文件中去添加我们需要的色彩资源

(2)创建圆形的太阳,所以我们需要在res/drawable/目录下面,新建立一个sun.xml的椭圆drawable资源

<shape xml:android = "http://schemas.android.com/apk/res/android"
android:shape = "oval">
<solid android:color = "@color/bright_sun"/>
</shape>

现在就已经完成了一个圆形的创建

(3)使用一个整体的布局文件来构建整个场景,该布局会在下面创建的SunsetFragment中使用,所以可以直接命名为fragment_sunset.xml

<LinaerLayout xmln:android="http://schemas.android.com/apk/res/android"
  android:orientation = "vertical"
 android:layout_width = "match_parent"
  android:layout_height = "match_parent">
<FrameLayout
   android:id="@+id/sky"
   android:layout_width="match_parent"
  android:layout_height = "odp"
  android:layout_weight = "0.61"
  android:background="@color/">
<ImageView
android:id="@+id/sun"
android:layout_width="100dp"
android:layout_height = "100dp"
android:layout_gravity = "center"
android:src = "@drawable/sun"/>
</FrameLayout>
<View 
  android:layout_width="match_parent"
  android:layout_hegiht = "0dp"//想要设置哪一个的比重,那么就需要设置哪一个为0dp
  android:weight = "0.39"//运用比重来设置了我们所需要的元素在总界面所占的比重
  android:background = "@color/"/>
</LinearLayout>
然后我们创建一个SunsetFragment类,在里面新加一个newInstance(),然后再onCreateView()中去实例化fragment_sunset布局并返回结果视图
public class SunsetFragment extends Fragment {
public static SunsetFragment newInstance(){
return new SunsetFragment();
}
public View onCreateView(LayoutInflater inflater,ViewGroup container,Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_sunset,container,false);
return view;
}
 
}
 
为了显示SunFragment,那么就需要我们让SunsetActivity类继承SingleFragmgmentActivity类
二.简单的属性动画
创建完落日场景,接下来就需要去获取视图引用了
在onCreateView()中获取要控制的视图并存入相对应得变量中去备用
public class SunsetFragment extends Fragment {
private View mSceneView;
private View mSunView;
private View mSkyView'
public static SunsetFragment newInstance(){
return new SunsetFragment();
}
public View onCreateView(LayoutInflater inflater,ViewGroup container,Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_sunset,container,false);
mSceneView = view;
mSunView = view.findViewById(R.id.sun);
mSkyView = view.findViewById(R.id.sky);
return view;
}
}
现在已经是做好了准备工作,那么接下就是

编码实现功能了,从技术上来说,所谓的太阳落下海平面,实际上就平滑地移动mSunView的视图

这就需要知道移动的开始和结束点,所以我们这边可以新建立一个方法用来实现移动的功能

private void startAnimation(){
float sunYStart = mSunView.getTop();//getxx这些方法可以返回自己的local layout rect,视图的local layout rect 是相对于父视图的位置和尺寸的大小的描述,视图一旦实例化,那么这些值就是相对固定的
float sunYEnd = mSkyView.getHeight();
}

 

(1)知道了动画的开始点和结束点,那么就可以创建一个ObjectAnimator对象来执行动画
private void startAnimation(){
float sunYStart = mSunView.getTop();//getxx这些方法可以返回自己的local layout rect,视图的local layout rect 是相对于父视图的位置和尺寸的大小的描述,视图一旦实例化,那么这些值就是相对固定的
float sunYEnd = mSkyView.getHeight();
ObjectAnimator heightAnimator = ObjectAnimator.ofFloat(mSunView,"y",sunYStart,sunYEnd).setDuration(3200);
heightAnimator.start();
}
(2)然后在onCreateView()中为mSceneView视图设置监听器,只要用户点击它,那么就会调用startAnimation()方法来执行动画
mSceneView.setOnClickListener(new View.OnClickListener(){
public void onClick(View v) {
startAnimator();
}
})

ObjectAnimator是一个属性动画的制作对象,属性动画是以一组不同的参数值反复调用属性设置方法,比如从0到1的参数值之间的确定过程就是叫做 imterpolation,在这个过程中即使很短暂,确定相邻的参数也是需要时间的,由于人眼的视觉短暂现象,动画的效果就出现了

(3)视图属性转换

想要视图动起来,只靠属性动画制作对象是不合实际的,尽管它确实有用,这边有了  属性转换  就会更有效力

前面已经说了,视图有  视图属性值(local layout rect)(它在实例化的时候被赋予了位置和大小尺寸参数值),知道了视图属性值那么就可以来改变这些属性值,从而来实现四处移动的移动视图,这种做法就叫做属性转换,如利用rotation pivotX pivotY这三个参数可以旋转视图

二.使用不同的interpolator

如果添加下面的代码,那么就可以实现一个加速的过程

heightAnimator.setInterpolator(new AcclerateInterpolator());

还有不同的加速器,那么就需要我们自己来看官方文档了

三.色彩渐变

如果我们想要完成当太阳落下的时候,天空也随着变化,那么我们就需要在onCreateView()中去获取color.xml文件中定制的色彩资源并存入相应的实例变量
private int mBlueSkyColor;
private int mSunsetSkyColor;
private int mNightSkyColor;
在onCreateView中
Resources resources = getResources();
mBlueSkyColor = ressources.getColor(R.color.blue_sky);
mSunsetSkyColor = ressources.getColor(R.color.sunset_sky);
mNightSkyColor = resources.getColor(R.color.night_sky);
 
然后再添加一个ObjectAnimator,实现天空色彩从mBlueSkyColor到mSunsetSkyColor变换的效果
ObjectAnimator sunsetAninator = ObjectAnimator.ofInt(mSkyView,"backgroundColor",mBlueSkyColor,mSunsetSkyColor).setDuration(3000);
sunsetAnimator.start();
但此时仔细分析的话,这边还存在着一些问题,就是颜色过渡还不够严密,颜色int数值不是简单的数字,它实际上是由四个比较小的小数字转换而来的,只有知道颜色的组成奥秘,ObjectAnimator才能合理地安排过渡,所以ObjectAnimator还需要一个TypeEvaluator子类的协助,它可以精确地计算出开始到结束间的递增值,添加下面的代码
sunsetSkyAnimator.setEvaluator(new ArgbEvaluator());

此时再次运行Sunset应用,夕阳西下,颜色得过渡会变得自然

四.播放多个动画

有时我们需要同时执行一些动画,那么我们就同时调用start()方法就可以了

但是如果我们想要向编写舞步那样去编排多个动画,那么就有一定得难度了,就像为了实现完整得日落景象,太阳落下后,天空就应该从橘黄色再转化为午蓝色,对此我们可以选择使用AmimatorListener,AnimatorListener会让你知道动画什么时候结束,这样执行完第一个动画后,就可以接力执行第二个夜空的动画了,这样理论上是可以的,但是这样太过麻烦,需要准备多个监听器,还好Android准备了AnimatorSet

(1)首先需要创建每一个动画,如在这边我们就需要创建一个夜空的动画

ObjectAnimator nightSkyAnimator = ObjectAnimator.ofInt(mSkyView,"backgroundColor",mSunsetSkyColor,mNightSkyColor).setDuration(1500);
nightSkyAnimator.setEvaluator(new ArgbEvaluator());
(2)然后创建一个动画集
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(heightAnimator).with(sunsetSkyAnimator).before(nightSkyAnimator);//协同执行heightAnimator和sunsetSkyAnimator动画,在nightSkyAnimator之前执行heightAnimator动画
animatorSet.start();

在上面的代码可以看出在调用play()方法之前就需要去创建一个AnimatorSet.Builder对象,然后利用它来进行创建链式方法调用,传入play()的是链首

五.了解其他动画API

(1)转场框架

从一个activity小视图动态弹出另一个放大版的activity视图,例如想要实现以弹窗展示放大版图片的这样的动画效果,那么就首先需要知道照片放在哪里,其次是如何在对话框里面布置新图片