动画效果

动画效果-分享链接
(想做成gif图的,尝试各种工具无果)

ObjectAnimator简介及实现思路

 ObjectAnimator是从api level 11 (Android3.0x)增加的类。在11已下版本使用,你可以在工程中引入nineoldandroids包。
  这里直接翻译android文档的内容。
 This subclass of ValueAnimator provides support for animating properties on target objects. The constructors of this class take parameters to define the target object that will be animated as well as the name of the property that will be animated. Appropriate set/get functions are then determined internally and the animation will call these functions as necessary to animate the property.
 它是ValueAnimator的子类,提供对目标对象属性动画的支持。它的构造方法的参数包括要进行动画的目标对象以及进行动画的属性名。相应的set/get方法将在内部确定,必要时将调用它们进行动画。

  比如打开第一张图粉红色位置的书,第二张图是中间过程。
  动画分为三部分,褐色部分为背景(可以为背景设置不同的纯色),黄色部分为封面,灰色部分为加载图标
  这三部分分别要新建一个ImageView,加入到FrameLayout中,使用WindowManager显示在屏幕上。

  • 背景动画:从A移动到B(translationX, translationY属性),同时放大(scaleX,scaleY属性);
  • 封面动画:从A移动到B,同时放大,旋转(rotationY);
  • 加载图标:在屏幕居中,从0放大到1;

关闭动画属性值与打开动画真好相反。

遇到的问题:

  1. 动画轴点(pivotX, pivotY)使用默认的(0, 0)即可;
  2. y方向放大比例大时,x方向需要做平移,使view居中;
  3. 背景控件的布局参数不能为wrap_content, 否则,因其使用ColorDrawable作为背景,它将不可见;也不能是match_parent, 否则,它将填充屏幕。所以要为它指定宽、高。

主要代码

BookView.java

package com.example.openbookanimationproj;import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;import com.example.openbookanimationproj.R;
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.Animator.AnimatorListener;
import com.nineoldandroids.animation.ObjectAnimator;
import com.nineoldandroids.view.ViewHelper;import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.ListView;
import android.widget.RelativeLayout;/*** A view to show the cover of a book. With method* {@link BookView#startOpenBookAnimation(OpenBookAnimEndListener, ViewParent)} and method* {@link BookView#startCloseBookAnimation()} to play opening and closing book animations.* * @author wenping0820@163.com* @date 2015-07-13*/
public class BookView extends RelativeLayout implements AnimatorListener {public static BookView sOpenedBookView;// Opening book animation durationprivate static final int OPEN_ANIMATION_DURATION = 500;// Closing book animation durationpublic static final int CLOSE_ANIMATION_DURATION = 500;// Animation background scalesprivate float mBgScaleX;private float mBgScaleY;// Animation cover scalesprivate float mCoverScaleX;private float mCoverScaleY;// BookView's location in the screenprivate int[] mLocation = new int[2];private WindowManager mWindowManager;// Parent of animation views(cover, background and loading icon)private FrameLayout mWmRootView;// coverprivate ImageView mCover = null;// Animation coverprivate ImageView mAnimCover;// Animation backgroundprivate ImageView mAnimBackground;// Animation loading iconprivate ImageView mLoadingIcon;// If opening animation has played.private AtomicBoolean mIsOpen = new AtomicBoolean(false);// Listener of opening book animation endingprivate OpenBookAnimEndListener mOpenBookAnimEndListener;// Total animations playedprivate AtomicInteger mAnimationCount = new AtomicInteger(0);// Total opening animations scheduledprivate int mTotalOpenBookAnim;// The parent of BookView(maybe a GridView or ListView or others)private static GridView mGridParent = null;private static ListView mListParent = null;// The opening book animation's end x value.private float mOpenBookEndBgX = 0;// The opening book animation's end x value. It will always be zero.private float mOpenBookEndBgY = 0;private Context mContext;public interface OpenBookAnimEndListener {public void onOpenBookAnimEnd();}public BookView(Context context) {this(context, null);mContext = context;initListener();}public BookView(Context context, AttributeSet attrs) {this(context, attrs, 0);mContext = context;initListener();}public BookView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);mContext = context;mWindowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);initListener();}private void initListener() {setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View arg0) {if (!mIsOpen.get()) {sOpenedBookView = BookView.this;startOpenBookAnimation();}}});}public synchronized void startOpenBookAnimation() {startOpenBookAnimation(null, getParent());}/*** Start opening book animation.* * @param l Listener of opening book animation ending* @param parent*/@SuppressLint("NewApi")public synchronized void startOpenBookAnimation(OpenBookAnimEndListener l, ViewParent parent) {mOpenBookAnimEndListener = l;try {mGridParent = parent != null ? (GridView) parent : null;} catch (ClassCastException e) {mListParent = parent != null ? (ListView) parent : null;}if (!mIsOpen.get()) {mCover = (ImageView) findViewById(R.id.iv_book_cover);if (mCover == null/* || mBackground == null */) {return;}mWmRootView = new FrameLayout(mContext);getLocationInWindow(mLocation);mWindowManager.addView(mWmRootView, getDefaultWindowParams());// new animation viewsmAnimCover = new ImageView(mContext);mAnimCover.setScaleType(mCover.getScaleType());mAnimCover.setImageDrawable(mCover.getDrawable());mAnimBackground = new ImageView(mContext);mAnimBackground.setScaleType(mCover.getScaleType());// backgroundDrawable readPageBgDrawable = new ColorDrawable(mContext.getResources().getColor(R.color.loading_book_bg_color));mAnimBackground.setBackgroundDrawable(readPageBgDrawable);// loading iconmLoadingIcon = new ImageView(mContext);mLoadingIcon.setBackgroundResource(R.drawable.ic_book_loading);mLoadingIcon.setScaleType(ScaleType.CENTER_CROP);FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);lp.gravity = Gravity.CENTER;ViewGroup.LayoutParams params = mCover.getLayoutParams();// Add view to root. Be careful that the height and width of 'params' should be// specified values. WRAP_CONTENT or MATCH_PARENT will lead to wrong effect.mWmRootView.addView(mAnimBackground, params);mWmRootView.addView(mAnimCover, params);mWmRootView.addView(mLoadingIcon, lp);// view scaleDisplayMetrics dm = mContext.getResources().getDisplayMetrics();int screenWidth = dm.widthPixels;int screenHeight = dm.heightPixels;float scaleW = screenWidth / (float) mCover.getWidth();float scaleH = screenHeight / (float) mCover.getHeight();float baseScale = Math.max(scaleW, scaleH);mBgScaleX = baseScale;mBgScaleY = baseScale;mCoverScaleX = baseScale / 3;mCoverScaleY = baseScale;// If scaleH is larger than scaleW, the ending x should be smaller to show the loading icon in the middle of// the screen.if (scaleW < scaleH) {mOpenBookEndBgX = (screenWidth - mCover.getWidth() * scaleH) / 2;}// start animationstartFlipCoverAnimation();}}private void startFlipCoverAnimation() {ViewHelper.setPivotX(mAnimBackground, 0);ViewHelper.setPivotY(mAnimBackground, 0);ViewHelper.setPivotX(mAnimCover, 0);ViewHelper.setPivotY(mAnimCover, 0);// Reset total opening animations scheduled.mTotalOpenBookAnim = 0;// loading iconstartIndividualAnim(mLoadingIcon, "scaleX", 0.0f, 1, true);startIndividualAnim(mLoadingIcon, "scaleY", 0.0f, 1, true);// background animationstartIndividualAnim(mAnimBackground, "translationX", mLocation[0], mOpenBookEndBgX, true);startIndividualAnim(mAnimBackground, "translationY", mLocation[1], mOpenBookEndBgY, true);startIndividualAnim(mAnimBackground, "scaleX", 1.0f, mBgScaleX, true);startIndividualAnim(mAnimBackground, "scaleY", 1.0f, mBgScaleY, true);// cover animationstartIndividualAnim(mAnimCover, "translationX", mLocation[0], mOpenBookEndBgX, true);startIndividualAnim(mAnimCover, "translationY", mLocation[1], mOpenBookEndBgY, true);startIndividualAnim(mAnimCover, "scaleX", 1.0f, mCoverScaleX, true);startIndividualAnim(mAnimCover, "scaleY", 1.0f, mCoverScaleY, true);startIndividualAnim(mAnimCover, "rotationY", 0, -100, true);}private WindowManager.LayoutParams getDefaultWindowParams() {WindowManager.LayoutParams params = new WindowManager.LayoutParams(WindowManager.LayoutParams.MATCH_PARENT,WindowManager.LayoutParams.MATCH_PARENT, 0, 0, WindowManager.LayoutParams.TYPE_APPLICATION_PANEL,WindowManager.LayoutParams.FLAG_FULLSCREEN, PixelFormat.RGBA_8888);return params;}/*** Close book animation.* * @param gridview BookItemView's parent.*/public synchronized void startCloseBookAnimation() {if (mIsOpen.get()) {/** You can change mLocation here. */// if (mGridParent != null) {// View firstView = mGridParent.getChildAt(0);// firstView.getLocationInWindow(mLocation);// mGridParent = null;// } else if (mListParent != null) {// View firstView = mListParent.getChildAt(0);// firstView.getLocationInWindow(mLocation);// mListParent = null;// } else {// getLocationInWindow(mLocation);// }if (mLoadingIcon != null) {mLoadingIcon.setVisibility(View.GONE);}if (CLOSE_ANIMATION_DURATION > 0) {// Reset open animation count.mTotalOpenBookAnim = 0;// loading iconstartIndividualAnim(mLoadingIcon, "scaleX", 1, 0.0f, false);startIndividualAnim(mLoadingIcon, "scaleY", 1, 0.0f, false);// background animationstartIndividualAnim(mAnimBackground, "translationX", mOpenBookEndBgX, mLocation[0], false);startIndividualAnim(mAnimBackground, "translationY", mOpenBookEndBgY, mLocation[1], false);startIndividualAnim(mAnimBackground, "scaleX", mBgScaleX, 1.0f, false);startIndividualAnim(mAnimBackground, "scaleY", mBgScaleY, 1.0f, false);// cover animationstartIndividualAnim(mAnimCover, "translationX", mOpenBookEndBgX, mLocation[0], false);startIndividualAnim(mAnimCover, "translationY", mOpenBookEndBgY, mLocation[1], false);startIndividualAnim(mAnimCover, "scaleX", mCoverScaleX, 1.0f, false);startIndividualAnim(mAnimCover, "scaleY", mCoverScaleY, 1.0f, false);startIndividualAnim(mAnimCover, "rotationY", -100, 0, false);} else {removeWindowView();}}}/*** Play one individual animation.* * @param target* @param property* @param startValue* @param endValue*/private void startIndividualAnim(View target, String property, float startValue, float endValue, boolean isOpen) {// Increase total opening animations scheduled.mTotalOpenBookAnim++;ObjectAnimator animator = ObjectAnimator.ofFloat(target, property, startValue, endValue).setDuration(isOpen ? OPEN_ANIMATION_DURATION : CLOSE_ANIMATION_DURATION);animator.addListener(this);animator.start();}public AtomicBoolean isOpen() {return mIsOpen;}public void hideLoadingView() {if (mLoadingIcon != null) {mLoadingIcon.setVisibility(View.GONE);}}public void removeWindowView() {mIsOpen.set(false);if (mWmRootView != null) {mWindowManager.removeView(mWmRootView);mWmRootView = null;}}@Overridepublic void onAnimationEnd(Animator arg0) {if (!mIsOpen.get()) {if (mAnimationCount.incrementAndGet() >= mTotalOpenBookAnim) {mIsOpen.set(true);if (mOpenBookAnimEndListener != null) {mOpenBookAnimEndListener.onOpenBookAnimEnd();} else {startActivity();}}} else {if (mAnimationCount.decrementAndGet() <= 0) {removeWindowView();}}}@Overridepublic void onAnimationRepeat(Animator arg0) {}@Overridepublic void onAnimationStart(Animator arg0) {}@Overridepublic void onAnimationCancel(Animator arg0) {}private void startActivity() {mContext.startActivity(new Intent(mContext, BookActivity.class));// Go to BookActivity smoothly.((Activity) mContext).overridePendingTransition(R.anim.fade_in, R.anim.fade_out);}
}

资源下载地址

csdn下载

欢迎提出改进意见~

使用ObjectAnimator开发打开、关闭书本动画相关推荐

  1. android IReader打开书本,关闭书本动画的自定义控件(带源码)

    一,实现思路 实现的效果: 此控件主要是模仿IReader打开书本翻转以及放大特效,在看文章之前可以找本书来翻转加深理解,呵呵,开个玩笑,进入正题. 实现的效果: 打开书本: 上层为cover,下层为 ...

  2. IReader打开书本,关闭书本动画的自定义控件

    一,实现思路 实现的效果: 此控件主要是模仿IReader打开书本翻转以及放大特效,在看文章之前可以找本书来翻转加深理解,呵呵,开个玩笑,进入正题. 实现的效果: 打开书本: 上层为cover,下层为 ...

  3. 【小松教你手游开发】【unity实用技能】给每个GameObject的打开关闭加上一个渐变...

    在游戏开发中,经常会因为直接将GameObject,setActive的方式打开关闭,这种方式效果太过生硬而给它加上一个Tween 可能是AlphaTween或者ScaleTween. 再加上一个Pl ...

  4. 开关灯效果HTML,H5+CSS3打开关闭灯泡开关动画特效

    H5+CSS3打开关闭灯泡开关动画特效 html, body { font-family: Georgia, serif; font-style: italic; font-size: 4.2vmin ...

  5. WPF:仿WIN7窗体打开关闭效果

    WIN7系统里面有很多很炫的动画效果,今天来模仿一下最常见的窗体打开关闭时的动画效果,比如打开一窗体,顶部靠外倾斜,透明渐变显示,关闭窗口,则反之的效果. 先新建一窗体,因为有向前向后倾斜效果,为了省 ...

  6. 华为云防火墙-firewall 打开关闭

    华为云防火墙-firewall 打开关闭 (1)查看对外开放的端口状态 ##查询已开放的端口 netstat -anp ##查询指定端口是否已开 firewall-cmd --query-port=6 ...

  7. android 登录注册动画,Android开发(14)——动画实战:炫酷登录

    本节内容 1.第三方库实现虚化 2.添加输入框和按钮 3.按钮状态 4.键盘隐藏 5.监听焦点改变的事件 6.手臂旋转动画 7.手掌和手臂动画 Demo简介 1.做一个炫酷登录的界面. image.p ...

  8. Android 点击图片放大至全屏 再次点击关闭过度动画 Shared Element效果(共享元素效果)

    Android 点击图片放大至全屏 再次点击关闭过度动画 最近项目需要给用户一个体验优化,各种查阅,然后改了很多地方,类似于图片的点击预览,消息列表的点击流畅过渡. Shared Element效果( ...

  9. android开发打开wifi密码,【Android开发】wifi开关与wifi连接(密码连接)

    过放荡不羁的生活,容易得像顺水推舟,但是要结识良朋益友,却难如登天.-- 巴尔扎克 本文demo来自网络,找了好久找到的,后面自己做了些许修改,这里对源码解析,愧于忘记哪里出来了,感谢作者! 接下来就 ...

最新文章

  1. (用微信扫的静态链接二维码)微信native支付模式官方提供的demo文件中的几个bug修正...
  2. 如何导入pytorch包_PyTorch 目前的运行途径
  3. MAT之NN:实现BP神经网络的回归拟合,基于近红外光谱的汽油辛烷值含量预测结果对比
  4. PowerShell脚本遇到的问题汇总
  5. 通过ssl调用远程WebService
  6. Java中sleep,wait,yield,join的区别
  7. 树状数组维护区间和的模型及其拓广的简单总结
  8. 京东回应拖欠神州 3 亿多元货款;苹果考虑将第三方浏览器和邮件设为默认;PS 诞生 30 周年| 极客头条...
  9. 查看IIS哪个应用程序池占用CPU过高
  10. Jmeter之BeanShell详解
  11. 各大搜索引擎站点提交入口大全
  12. 【DVB】【ATSC】ATSC和DVB数字电视系统的比较
  13. windows日趋苹果化?win11到Win12,妥妥MacOS的复刻版
  14. C语言基础学习——简单的C程序格式
  15. 程序员小助手 | Emacs,最强编辑器,没有之一
  16. 一篇文章带你了解网页框架——Vue简单入门
  17. java 套接字关联的通道_java.nio.channels.SocketChannel
  18. Bandizip下载网盘地址
  19. 论文笔记 -- Fast-LIO -- ESIKF溯源
  20. 2021年中国机构调研概况分析(附总次数、行业分布、投融资情况)[图]

热门文章

  1. 【色彩L5笔记:环境光】
  2. 生物专业学生如何逆袭为大厂程序员?| 程序员有话说
  3. godaddy ssl证书配置
  4. 面向对象高级(内部类)
  5. 三年吸粉超4000万,从普通游戏玩家到爆红网络,林颜狗子是如何做到的?
  6. JavaScript 滑动验证
  7. 邀请好友我赚了1026个集分宝 分享庆祝下
  8. 饥荒怎么把离线服务器改成在线,饥荒离线模式更改 | 手游网游页游攻略大全
  9. 灵动小兔跃然纸上——同人立绘征集大赛链小兔·金奖
  10. 漫画:量子计算为什么这么牛?