项目主页
项目结构
项目结构清晰,以介绍的模块对应起来
BaseActivity
这里会发现一个奇怪的Activity,每个Activity都继承这个BaseActivity,其作用就是增加标题和返回键的功能
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
@Override
public boolean onSupportNavigateUp() {
onBackPressed();
return true;
}
}
Tween动画
Tween动画比较简单,我们常说的平移、旋转、缩放、透明度这四个动画,其中可以将每个动画都搭配起来成为集合,Tween动画的展示形式有两种,一种通过xml形式,另一种是代码方式,由于比较简单,就不详细介绍了
代码形式
public class TweenActivity extends BaseActivity implements View.OnClickListener {
private ImageView rectangle_red;
private Switch isXml;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tween);
rectangle_red = (ImageView) findViewById(R.id.rectangle_red);
isXml = (Switch) findViewById(R.id.isXml);
findViewById(R.id.translate).setOnClickListener(this);
findViewById(R.id.rotate).setOnClickListener(this);
findViewById(R.id.scale).setOnClickListener(this);
findViewById(R.id.alpha).setOnClickListener(this);
findViewById(R.id.set).setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.translate:
if (isXml.isChecked()) {
Animation anim = AnimationUtils.loadAnimation(this, R.anim.tween_translate);
rectangle_red.startAnimation(anim);
return;
}
//平移动画
TranslateAnimation translateAnimation = new TranslateAnimation(0, 100, 0, 100);
translateAnimation.setRepeatMode(Animation.REVERSE);
translateAnimation.setFillAfter(true);
translateAnimation.setDuration(2000);
translateAnimation.setRepeatCount(1);
rectangle_red.startAnimation(translateAnimation);
break;
case R.id.rotate:
if (isXml.isChecked()) {
Animation anim = AnimationUtils.loadAnimation(this, R.anim.tween_rotate);
rectangle_red.startAnimation(anim);
return;
}
//旋转动画
RotateAnimation rotateAnimation = new RotateAnimation(0, 180, Animation.RELATIVE_TO_SELF, Animation.RELATIVE_TO_SELF);
rotateAnimation.setRepeatMode(Animation.REVERSE);
rotateAnimation.setFillAfter(true);
rotateAnimation.setDuration(2000);
rotateAnimation.setRepeatCount(1);
rectangle_red.startAnimation(rotateAnimation);
break;
case R.id.scale:
if (isXml.isChecked()) {
Animation anim = AnimationUtils.loadAnimation(this, R.anim.tween_scale);
rectangle_red.startAnimation(anim);
return;
}
//缩放动画
ScaleAnimation scaleAnimation = new ScaleAnimation(1, 2, 1, 2, Animation.RELATIVE_TO_SELF, Animation.RELATIVE_TO_SELF);
scaleAnimation.setRepeatMode(Animation.REVERSE);
scaleAnimation.setFillAfter(true);
scaleAnimation.setDuration(2000);
scaleAnimation.setRepeatCount(1);
rectangle_red.startAnimation(scaleAnimation);
break;
case R.id.alpha:
if (isXml.isChecked()) {
Animation anim = AnimationUtils.loadAnimation(this, R.anim.tween_alpha);
rectangle_red.startAnimation(anim);
return;
}
//透明度动画
AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0.5f);
alphaAnimation.setRepeatMode(Animation.REVERSE);
alphaAnimation.setFillAfter(true);
alphaAnimation.setDuration(2000);
alphaAnimation.setRepeatCount(1);
rectangle_red.startAnimation(alphaAnimation);
break;
case R.id.set:
if (isXml.isChecked()) {
Animation anim = AnimationUtils.loadAnimation(this, R.anim.tween_set);
rectangle_red.startAnimation(anim);
return;
}
//动画集合
ScaleAnimation scaleAnim = new ScaleAnimation(1, 2, 1, 2, Animation.RELATIVE_TO_SELF, Animation.RELATIVE_TO_SELF);
scaleAnim.setRepeatMode(Animation.REVERSE);
scaleAnim.setFillAfter(true);
scaleAnim.setDuration(2000);
scaleAnim.setRepeatCount(1);
AlphaAnimation alphaAnim = new AlphaAnimation(1, 0.5f);
alphaAnim.setRepeatMode(Animation.REVERSE);
alphaAnim.setFillAfter(true);
alphaAnim.setDuration(2000);
alphaAnim.setRepeatCount(1);
AnimationSet set = new AnimationSet(true);
set.addAnimation(scaleAnim);
set.addAnimation(alphaAnim);
rectangle_red.startAnimation(set);
break;
}
}
}
Xml形式
如果是Xml形式的话,就必须在res目录下的anim目录下指定对应的动画xml文件,具体的内容可以查看源码
Drawable动画
Drawable也可以称为帧动画,就是通过一帧一帧的图片展示出来的动画,代码很简单,只需要开启动画就可以了
public class DrawableActivity extends BaseActivity implements View.OnClickListener {
private AnimationDrawable background;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_drawable);
findViewById(R.id.start).setOnClickListener(this);
ImageView iv_arrow = (ImageView) findViewById(R.id.iv_arrow);
iv_arrow.setBackgroundResource(R.drawable.drawable_anim);
background = (AnimationDrawable) iv_arrow.getBackground();
}
@Override
public void onClick(View v) {
background.start();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (background.isRunning()) {
background.stop();
}
}
}
在布局文件中使用background属性即可
<ImageView
android:id="@+id/iv_arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@drawable/drawable_anim" />
这个drawable_anim就是一长串的动画图片的声明
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="true">
<item android:duration="16">
<bitmap android:src="@drawable/ic_done_anim_000" />
</item>
<item android:duration="16">
<bitmap android:src="@drawable/ic_done_anim_001" />
</item>
<item android:duration="16">
<bitmap android:src="@drawable/ic_done_anim_002" />
</item>
<item android:duration="16">
<bitmap android:src="@drawable/ic_done_anim_003" />
</item>
<item android:duration="16">
<bitmap android:src="@drawable/ic_done_anim_004" />
</item>
<item android:duration="16">
<bitmap android:src="@drawable/ic_done_anim_005" />
</item>
......
</animation-list>
Property动画
Property动画分为两种
- ObjectAnimator:ObjectAnimator将View的属性就行修改,使其产生动画,比如x坐标y坐标修改,就能形成平移
- ValueAnimator:ValueAnimator会分配一个区间的值,随着值的增长或者下降,使用其产生的值进行更新动画
public class PropertyActivity extends BaseActivity implements View.OnClickListener {
private ImageView rectangle_red;
private Switch isXml;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_property);
rectangle_red = (ImageView) findViewById(R.id.rectangle_red);
isXml = (Switch) findViewById(R.id.isXml);
findViewById(R.id.translate).setOnClickListener(this);
findViewById(R.id.rotate).setOnClickListener(this);
findViewById(R.id.scale).setOnClickListener(this);
findViewById(R.id.alpha).setOnClickListener(this);
findViewById(R.id.set).setOnClickListener(this);
findViewById(R.id.setWay2).setOnClickListener(this);
findViewById(R.id.translate2).setOnClickListener(this);
findViewById(R.id.rotate2).setOnClickListener(this);
findViewById(R.id.scale2).setOnClickListener(this);
findViewById(R.id.alpha2).setOnClickListener(this);
findViewById(R.id.set2).setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.translate:
if (isXml.isChecked()) {
Animator anim = AnimatorInflater.loadAnimator(this, R.animator.value_translate);
anim.setTarget(rectangle_red);
anim.start();
return;
}
ObjectAnimator.ofFloat(rectangle_red, "translationX", 0.0F, 150.0F)
.setDuration(1000)
.start();
break;
case R.id.rotate:
if (isXml.isChecked()) {
Animator anim = AnimatorInflater.loadAnimator(this, R.animator.value_rotate);
anim.setTarget(rectangle_red);
anim.start();
return;
}
ObjectAnimator.ofFloat(rectangle_red, "rotationX", 0.0F, 360.0F)
.setDuration(1000)
.start();
break;
case R.id.scale:
if (isXml.isChecked()) {
Animator anim = AnimatorInflater.loadAnimator(this, R.animator.value_scale);
anim.setTarget(rectangle_red);
anim.start();
return;
}
ObjectAnimator.ofFloat(rectangle_red, "scaleX", 1.0F, 3.0F)
.setDuration(1000)
.start();
break;
case R.id.alpha:
if (isXml.isChecked()) {
Toast.makeText(this, "这个不行", Toast.LENGTH_SHORT).show();
return;
}
ObjectAnimator.ofFloat(rectangle_red, "alpha", 1F, 0.5F)
.setDuration(1000)
.start();
break;
case R.id.set:
//动画集合
if (isXml.isChecked()) {
Animator anim = AnimatorInflater.loadAnimator(this, R.animator.value_set);
anim.setTarget(rectangle_red);
anim.start();
return;
}
PropertyValuesHolder pvhScaleX = PropertyValuesHolder.ofFloat("scaleX", 1f, 0f, 1f);
PropertyValuesHolder pvhScaleY = PropertyValuesHolder.ofFloat("scaleY", 1f, 0f, 1f);
ObjectAnimator customAnim = ObjectAnimator.ofPropertyValuesHolder(rectangle_red, pvhScaleX, pvhScaleY);
customAnim.setDuration(4000);
customAnim.start();
break;
case R.id.setWay2:
//动画集合2
if (isXml.isChecked()) {
Animator anim = AnimatorInflater.loadAnimator(this, R.animator.value_set);
anim.setTarget(rectangle_red);
anim.start();
return;
}
ObjectAnimator objectAnimator1 = ObjectAnimator.ofFloat(rectangle_red, "translationX", 0.0F, 150.0F).setDuration(1000);
ObjectAnimator objectAnimator2 = ObjectAnimator.ofFloat(rectangle_red, "translationY", 0.0F, 150.0F).setDuration(1000);
ObjectAnimator objectAnimator3 = ObjectAnimator.ofFloat(rectangle_red, "rotationX", 0.0F, 360.0F).setDuration(1000);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(3000);
// 随机设置
animatorSet.play(objectAnimator1)
.with(objectAnimator2)
.after(objectAnimator3);
// // 一起播放
// animatorSet.playTogether(objectAnimator1, objectAnimator2, objectAnimator3);
// // 顺序播放
// animatorSet.playSequentially(objectAnimator1, objectAnimator2, objectAnimator3);
animatorSet.start();
break;
case R.id.translate2:
startValueAnimator(0);
break;
case R.id.rotate2:
startValueAnimator(1);
break;
case R.id.scale2:
startValueAnimator(2);
break;
case R.id.alpha2:
startValueAnimator(3);
break;
case R.id.set2:
startValueAnimator(4);
break;
}
}
/**
* ValueAnimator
*/
public void startValueAnimator(final int id) {
ValueAnimator valueAnimator = ValueAnimator.ofFloat(150)
.setDuration(1000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
if (id == 0) {
rectangle_red.setTranslationX((Float) animation.getAnimatedValue());
} else if (id == 1) {
rectangle_red.setRotation((Float) animation.getAnimatedValue());
} else if (id == 2) {
rectangle_red.setScaleX((Float) animation.getAnimatedValue());
} else if (id == 3) {
rectangle_red.setAlpha((Float) animation.getAnimatedValue());
} else if (id == 4) {
rectangle_red.setTranslationX((Float) animation.getAnimatedValue());
rectangle_red.setScaleX((Float) animation.getAnimatedValue());
rectangle_red.setAlpha((Float) animation.getAnimatedValue());
}
}
});
valueAnimator.start();
}
}
MaterialDesign动画
MaterialDesign动画分为两种
- 水波纹动画
- 揭露动画
水波纹动画
水波纹就是通过水波纹动画在xml上的编写,然后通过View设置background即可
<Button
android:id="@+id/ripple"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/materia_ripple"
android:text="Ripple" />
<Button
android:id="@+id/ripple1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/materia_ripple1"
android:text="Ripple1" />
<Button
android:id="@+id/ripple2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/materia_ripple2"
android:text="Ripple2" />
materia_ripple.xml
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/colorAccent">
</ripple>
materia_ripple1.xml
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/colorAccent">
<item
android:id="@android:id/mask"
android:drawable="@android:color/holo_green_dark"/>
</ripple>
materia_ripple2.xml
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/colorAccent">
<item
android:id="@android:id/mask"
android:drawable="@android:color/holo_green_dark"/>
<item android:drawable="@color/colorPrimary"/>
</ripple>
揭露动画
揭露动画只要在通过代码生成即可
public class MaterialDesignActivity extends BaseActivity implements View.OnClickListener {
private ImageView show_pic;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_material_design);
show_pic = (ImageView) findViewById(R.id.show_pic);
findViewById(R.id.reveal).setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.reveal:
//直径
int d = Math.max(show_pic.getHeight(), show_pic.getWidth()) * 2;
//参数1:view 参数2:x坐标 参数3:y坐标 参数4:动画开始的半径 参数:动画结束的半径
Animator animator = ViewAnimationUtils.createCircularReveal(show_pic, 0, 0, 0, d);
animator.setDuration(1000);
animator.start();
break;
}
}
}
Transition动画
Transition动画是目前比较流行的动画,包含以下内容
- 转场动画
- 共享元素动画
转场动画
系统默认为我们提供了三种转场动画,其实现步骤如下
1、在需要跳转的界面中,启动Activity的时候调用新的API
public class TransitionActivity extends BaseActivity implements View.OnClickListener {
private Intent mIntent = new Intent();
private Switch isXml;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_transition);
isXml = (Switch) findViewById(R.id.isXml);
findViewById(R.id.explode).setOnClickListener(this);
findViewById(R.id.slide).setOnClickListener(this);
findViewById(R.id.fade).setOnClickListener(this);
}
/**
* 启动Activity
*/
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.explode:
mIntent.setClass(this, TransitionToThisActivity.class);
mIntent.putExtra("transition", "explode");
mIntent.putExtra("isXml", isXml.isChecked());
startActivity(mIntent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle());
break;
case R.id.slide:
mIntent.setClass(this, TransitionToThisActivity.class);
mIntent.putExtra("transition", "slide");
mIntent.putExtra("isXml", isXml.isChecked());
startActivity(mIntent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle());
break;
case R.id.fade:
mIntent.setClass(this, TransitionToThisActivity.class);
mIntent.putExtra("transition", "fade");
mIntent.putExtra("isXml", isXml.isChecked());
startActivity(mIntent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle());
break;
}
}
}
2、在跳转过去的界面中,设置进入动画和退出动画即可
public class TransitionToThisActivity extends BaseActivity {
private String transition;
private TextView textView;
private boolean isXml;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_transition_to_this);
transition = getIntent().getStringExtra("transition");
isXml = getIntent().getBooleanExtra("isXml", false);
initAnimation();
}
/**
* 设置进入动画和退出动画
*/
private void initAnimation() {
textView = (TextView) findViewById(R.id.share_text);
textView.setText(transition);
switch (transition) {
case "explode":
if (isXml) {
Explode explode = (Explode) TransitionInflater.from(this).inflateTransition(R.transition.explode);
explode.setDuration(1000L);
getWindow().setEnterTransition(explode);
return;
}
Explode explode = new Explode();
explode.setDuration(1000L);
getWindow().setEnterTransition(explode);
break;
case "slide":
if (isXml) {
Slide slide = (Slide) TransitionInflater.from(this).inflateTransition(R.transition.slide);
slide.setDuration(1000L);
getWindow().setEnterTransition(slide);
return;
}
Slide slide = new Slide(Gravity.BOTTOM);
slide.setDuration(1000L);
getWindow().setEnterTransition(slide);
break;
case "fade":
if (isXml) {
Fade fade = (Fade) TransitionInflater.from(this).inflateTransition(R.transition.fade);
fade.setDuration(1000L);
getWindow().setEnterTransition(fade);
return;
}
Fade fade = new Fade();
fade.setDuration(1000L);
getWindow().setEnterTransition(fade);
break;
default:
break;
}
}
}
共享元素
共享元素,其实现步骤如下
1、布局文件中设置view元素的共享字段,通过transitionName声明共享元素的字段
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/rectangle_red"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="@drawable/rectangle_red"
android:transitionName="share" />
<TextView
android:id="@+id/share_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="18dp"
android:gravity="center"
android:text="Explode"
android:transitionName="share_text" />
</LinearLayout>
2、启动Activity指定所有共享view元素
mIntent.setClass(this, TransitionToThisActivity.class);
mIntent.putExtra("transition", "share");
//5.0以上兼容的API
ActivityOptions transitionActivityOptions = ActivityOptions.makeSceneTransitionAnimation(this
, Pair.create(findViewById(R.id.rectangle_red), "share")
, Pair.create(findViewById(R.id.share_text), "share_text"));
//5.0以下兼容的API
ActivityOptionsCompat activityOptionsCompat1 = ActivityOptionsCompat.makeSceneTransitionAnimation(this
, Pair.create(findViewById(R.id.rectangle_red), "share")
, Pair.create(findViewById(R.id.share_text), "share_text"));
startActivity(mIntent, transitionActivityOptions.toBundle());