Android CircularReveal揭露动画三种实现
来源:互联网 发布:个人征信所用数据 编辑:程序博客网 时间:2024/05/20 20:05
1.概述
上一篇原本是准备介绍下揭露动画的,但是无奈没有5.0设备,于是乎装genymotion下载了半天。
genymotion配置
今天来介绍下揭露动画,首先看下效果。
这2个使用系统ViewAnimationUtils.createCircularReveal实现的:
接下来2个使用属性动画以及自定义圆形控件+属性动画实现:
以上通过FloatingActionButton点击变化的是单个View的效果,通过三个美女圆形ImageView点击是activity的跳转(应用设置了透明主题)。
还不知道圆形头像怎么制作的可以看:
http://blog.csdn.net/ch867179244/article/details/53522679
2.思路
揭露动画个人理解有点像layer,类似于一个东西上面铺了一层,显示时让上面那层有个揭开的效果去显示。
按照这个思路怎么实现了?
2.1 我们先设置需要View不可见,
2.2 在我们需要显示的时候,让View可见并且执行动画效果,
2.3 动画结束后,隐藏动画的View ,然后显示我们需要View(当然这个时候需要显示的view也可以给个出场动画)
注意点:
如果想让View刚加载出来就有揭露动画效果(比如activity切换时),这个时候需要注意2点。
1. 给View添加preDraw监听,在onPreDraw的回调中去执行动画,并且记住remove监听,这么做的原因是,如果直接在oncreate中给View添加动画,这个时候view可能还没完全加载。清除监听是为了防止多次调用。
2. 如果是activity时,需要覆盖系统默认的界面跳转动画效果。 overridePendingTransition
3. 使用属性动画,别忘记提供set(必须设置) ; get方法(属性动画没设置初始值时需要get)
3.代码实现
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener { FloatingActionButton fab; CirclePic ivCenter; //中间大图片 CirclePic cpLeft, cpCenter, cpRight; // 左中右依次是:系统默认 ;属性动画 ; 属性动画+自定义控件 ViewTreeObserver.OnPreDrawListener listener; int centerX, centerY; float startRadiu, endRadiu; private static final int ANIMA_DURATION = 1 * 1000; //默认动画时间 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } private void initView() { fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(this); ivCenter = (CirclePic) findViewById(R.id.iv); listener = new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { //这里一定要remove 避免执行多次 ivCenter.getViewTreeObserver().removeOnPreDrawListener(listener); centerX = (ivCenter.getLeft() + ivCenter.getRight()) / 2; //开始裁剪的中心点Y坐标 centerY = (ivCenter.getTop() + ivCenter.getBottom()) / 2; //结束半径对角线 endRadiu = (float) Math.hypot(centerX, centerY); //开始半径 startRadiu = 0; anim(centerX, centerY, endRadiu, 0, ivCenter, ivCenter.getVisibility() == View.INVISIBLE ? true : false); return false; } }; ivCenter.getViewTreeObserver().addOnPreDrawListener(listener); cpCenter = (CirclePic) findViewById(R.id.circlePic); cpCenter.setOnClickListener(this); cpLeft = (CirclePic) findViewById(R.id.circlePicLeft); cpLeft.setOnClickListener(this); cpRight = (CirclePic) findViewById(R.id.circlePicRight); cpRight.setOnClickListener(this); } /** * @param isShow 动画结束是否显示View */ private void anim(int centerX, int centerY, float endRadiu, float startRadiu, final ImageView iv, final boolean isShow) { // fab的动画 //fab.setPivotY((iv.getHeight() + iv.getBottom()) / 2); //final Animation rotationAnima = new RotateAnimation(isShow ? 0 : 360, isShow ? 360 : 0); //fab.setAnimation(rotationAnima); //rotationAnima.setDuration(ANIMA_DURATION); //rotationAnima.setInterpolator(new AccelerateDecelerateInterpolator()); //rotationAnima.start(); //rotationAnima.setAnimationListener(new Animation.AnimationListener() { // @Override public void onAnimationStart(Animation animation) { // // } // // @Override public void onAnimationEnd(Animation animation) { // rotationAnima.cancel(); // rotationAnima.setAnimationListener(null); // } // // @Override public void onAnimationRepeat(Animation animation) { // // } //}); //ivCenter的动画 final Animator animator = ViewAnimationUtils.createCircularReveal(iv, centerX, centerY, isShow ? startRadiu : endRadiu, isShow ? endRadiu : startRadiu); animator.setInterpolator(new AccelerateDecelerateInterpolator()); animator.setDuration(ANIMA_DURATION); animator.setStartDelay(0); iv.setVisibility(View.VISIBLE); animator.start(); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (!isShow) { iv.setVisibility(View.INVISIBLE); } } }); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.fab: anim(centerX, centerY, endRadiu, startRadiu, ivCenter, ivCenter.getVisibility() == View.INVISIBLE ? true : false); break; case R.id.circlePic: toSecondActivity(Constant.CENTER); break; case R.id.circlePicLeft: toSecondActivity(Constant.LEFT); break; case R.id.circlePicRight: toSecondActivity(Constant.RIGHT); break; } } /** * 跳转secondActivity */ private void toSecondActivity(int whichAnima) { Intent intent = new Intent(MainActivity.this, SecondActivity.class); intent.putExtra("param", whichAnima); startActivity(intent); //取消默认的activity切换动画 overridePendingTransition(0, 0); }}
activity_main.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" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" > <demos.ch.com.animationdemo.CirclePic android:id="@+id/iv" android:layout_width="@dimen/circle_radiu_big" android:layout_height="@dimen/circle_radiu_big" app:radiu="@dimen/circle_radiu_big" android:layout_centerInParent="true" android:src="@mipmap/d" android:visibility="invisible" /> <android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/iv" android:layout_centerHorizontal="true" android:src="@mipmap/ic_launcher" /> <demos.ch.com.animationdemo.CirclePic android:layout_width="@dimen/circle_radiu" android:layout_height="@dimen/circle_radiu" app:radiu="@dimen/circle_radiu" android:src="@mipmap/f" android:id="@+id/circlePic" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" /> <demos.ch.com.animationdemo.CirclePic android:layout_width="@dimen/circle_radiu" android:layout_height="@dimen/circle_radiu" app:radiu="@dimen/circle_radiu" android:src="@mipmap/e" android:id="@+id/circlePicRight" android:layout_alignParentRight="true" /> <demos.ch.com.animationdemo.CirclePic android:layout_width="@dimen/circle_radiu" android:layout_height="@dimen/circle_radiu" app:radiu="@dimen/circle_radiu" android:src="@mipmap/g" android:id="@+id/circlePicLeft" android:layout_alignParentLeft="true" /></RelativeLayout>
SecondActivity.java
public class SecondActivity extends AppCompatActivity implements View.OnClickListener { Button button; TextView tv; CircleBackGround cgb; ViewTreeObserver.OnPreDrawListener listener; int width, height; int whichAnima; private static final int ANIMA_DURATION = 1 * 1000; //默认动画时间 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm); width = dm.widthPixels; height = dm.heightPixels; if (null != getIntent()) { whichAnima = getIntent().getIntExtra("param", Constant.CENTER); } initView(); } private void initView() { button = (Button) findViewById(R.id.btn); button.setOnClickListener(this); tv = (TextView) findViewById(R.id.tv); cgb = (CircleBackGround) findViewById(R.id.cbg); cgb.setCx(width / 2); cgb.setCy(height / 2); listener = new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { //这里一定要remove 避免执行多次 startWhichAnima(); return false; } }; if (whichAnima == Constant.RIGHT) { cgb.getViewTreeObserver().addOnPreDrawListener(listener); } else { tv.getViewTreeObserver().addOnPreDrawListener(listener); } } /** * 启动动画 */ private void startWhichAnima() { switch (whichAnima) { case Constant.CENTER: //2.属性动画实现 tv.getViewTreeObserver().removeOnPreDrawListener(listener); tv.setVisibility(View.VISIBLE); propertyAnima(); break; case Constant.LEFT: //1.系统揭露动画实现 tv.getViewTreeObserver().removeOnPreDrawListener(listener); tv.setVisibility(View.VISIBLE); circularAnima(); break; case Constant.RIGHT: //3.自定义控件+属性动画实现 cgb.getViewTreeObserver().removeOnPreDrawListener(listener); cgb.setVisibility(View.VISIBLE); CirclePropertyAnima(); break; } } /** * 自定义控件+属性动画 圆形 */ private void CirclePropertyAnima() { //代实现 ObjectAnimator animaRadiu = ObjectAnimator.ofFloat(cgb, "radiu", 0.0f, (float) Math.hypot(width / 2, height / 2)); animaRadiu.setDuration(ANIMA_DURATION); animaRadiu.setInterpolator(new AccelerateDecelerateInterpolator()); animaRadiu.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { animaEndAction(); } }); animaRadiu.start(); } /** * 使用属性动画实现揭露 效果不是圆形的 */ public void propertyAnima() { final AnimatorSet animaSet = new AnimatorSet(); ObjectAnimator animaX = ObjectAnimator.ofFloat(tv, "x", width, 0); ObjectAnimator animaY = ObjectAnimator.ofFloat(tv, "y", height, 0); //ObjectAnimator animaAlpha = ObjectAnimator.ofFloat(tv,"alpha",1.0f,0.0f); animaSet.setDuration(ANIMA_DURATION); animaSet.setInterpolator(new AccelerateDecelerateInterpolator()); animaSet.playTogether(animaX, animaY/*,animaAlpha*/); animaSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { animaEndAction(); } }); animaSet.setStartDelay(0); animaSet.start(); } /** * 使用createCircularReveal实现揭露效果 */ private void circularAnima() { int centerX = (tv.getLeft() + tv.getRight()) / 2; //开始裁剪的中心点Y坐标 int centerY = (tv.getTop() + tv.getBottom()) / 2; //开始半径 float startRadiu = (float) Math.hypot(centerX, centerY); //结束半径对角线 float endRadiu = (float) Math.min(tv.getWidth(), tv.getHeight()) / 2; final Animator animator = ViewAnimationUtils.createCircularReveal(tv, centerX, centerY, startRadiu, 0); animator.setInterpolator(new AccelerateDecelerateInterpolator()); animator.setDuration(ANIMA_DURATION); animator.setStartDelay(0); animator.start(); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { animaEndAction(); } }); } /** * 动画结束后续动作 */ private void animaEndAction() { tv.setVisibility(View.INVISIBLE); cgb.setVisibility(View.INVISIBLE); button.setVisibility(View.VISIBLE); //ObjectAnimator anima = ObjectAnimator.ofFloat(button,"alpha",0.0f,1.0f); //ObjectAnimator anima1 = ObjectAnimator.ofFloat(button,"rotation",0,360); //anima1.setDuration(ANIMA_DURATION); //anima1.setInterpolator(new AccelerateDecelerateInterpolator()); //anima1.start(); } @Override public void onClick(View view) { if (view.getId() == R.id.btn) { onBackPressed(); } }}
sceond_activity.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" android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:id="@+id/tv" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="invisible" android:background="@color/colorAccent" /> <demos.ch.com.animationdemo.CircleBackGround android:id="@+id/cbg" android:layout_width="match_parent" android:layout_height="match_parent" app:radiuCbg="@dimen/circle_radiu_big" app:pColor="#FFE7CE0F" android:visibility="invisible" /> <Button android:id="@+id/btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:background="#c8ccef" android:textColor="#FFB5AB15" android:textSize="24dp" android:text="onBackPressed" android:visibility="invisible" /></RelativeLayout>
里面有2个自定义控件,一个CircleImageView这个就是上篇博客中copy过来,代码就不贴了,CircleBackGround很简单,就是自定义ImageView画实心圆。
CircleBackGround.java
public class CircleBackGround extends ImageView { private float radiu = 0; //背景圆半径 用于属性动画提供set get方法 private int color = Color.RED; //画笔颜色 private float cx, cy; public float getCx() { return cx; } public void setCx(float cx) { this.cx = cx; } public float getCy() { return cy; } public void setCy(float cy) { this.cy = cy; } public float getRadiu() { return radiu; } public void setRadiu(float radiu) { this.radiu = radiu; invalidate(); } public CircleBackGround(Context context) { this(context, null); } public CircleBackGround(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CircleBackGround(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(attrs); } private void init(AttributeSet attrs) { TypedArray a = getResources().obtainAttributes(attrs, R.styleable.CircleBackGround); radiu = (int) a.getDimension(R.styleable.CircleBackGround_radiuCbg, 0); color = a.getColor(R.styleable.CircleBackGround_pColor,0xffffff); a.recycle(); } @Override protected void onDraw(Canvas canvas) { Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setStyle(Paint.Style.FILL_AND_STROKE); paint.setTypeface(Typeface.DEFAULT_BOLD); paint.setColor(color); canvas.drawCircle(getCx(), getCy(), radiu, paint); }}
当然揭露动画结束之后可以给需要显示的View设置些出场动画,类似于Activity切换有个enter_anim 和out_anim,以上就是实现代码,比较简单所以就不一一讲解了。按照上述的思路和注意点来实现就行了。大家有什么别的好的ideal也可以留言一起学习。
完整代码地址:
https://github.com/ChenHaoLw/AnimationDemo
- Android CircularReveal揭露动画三种实现
- Android 创建CircularReveal揭露动画的实现
- Android 动画-CircularReveal
- Android 5.0 动画之CircularReveal
- Android Reveal Animation(揭露动画)实现
- Android开发 之 揭露动画
- Android Activity转场 -- 揭露动画
- AndroidMaterialDesign动画之CircularReveal
- Android Material Design(6) CircularReveal圆形扩散动画的使用
- Android动画学习(六)之View揭露效果和SurfaceView实现动画
- Android三种动画的实现
- Android三种动画的实现
- CircularReveal
- android开发游记:meterial design 使用circularReveal仿哔哩哔哩(bilibili)搜索框动画
- android开发游记:meterial design 使用circularReveal仿哔哩哔哩(bilibili)搜索框动画
- 使用CircularReveal动画效果切换页面
- Android 三种动画
- android三种动画
- Android 动画详细总结
- 郝斌的C语言基础 164 学生管理系统(输入,排序,输出)
- Spring笔记(第四弹:使用Maven创建一个Spring MVC工程)
- Android进阶——性能优化之多线程总结及简单应用(一)
- CentOS 7.2下安装mysql
- Android CircularReveal揭露动画三种实现
- Python入门
- ListView多选操作模式详解CHOICE_MODE_MULTIPLE与CHOICE_MODE_MULTIPLE_MODAL
- quartz在java,javaweb中的使用
- JAVA源码分析--LinkedList
- PAT(basic level) 1054求平均值(20)
- 行内元素的特性
- Jquery那些事
- 五子棋