使用ObjectAnimator开发打开、关闭书本动画
动画效果
动画效果-分享链接
(想做成gif图的,尝试各种工具无果)
ObjectAnimator简单介绍及实现思路
ObjectAnimator是从api level 11 (Android3.0x)添加的类。在11已下版本号使用。你能够在project中引入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;
关闭动画属性值与打开动画真好相反。
遇到的问题:
- 动画轴点(pivotX, pivotY)使用默认的(0, 0)就可以;
- y方向放大比例大时,x方向须要做平移,使view居中;
- 背景控件的布局參数不能为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 duration
private static final int OPEN_ANIMATION_DURATION = 500;
// Closing book animation duration
public static final int CLOSE_ANIMATION_DURATION = 500;
// Animation background scales
private float mBgScaleX;
private float mBgScaleY;
// Animation cover scales
private float mCoverScaleX;
private float mCoverScaleY;
// BookView's location in the screen
private int[] mLocation = new int[2];
private WindowManager mWindowManager;
// Parent of animation views(cover, background and loading icon)
private FrameLayout mWmRootView;
// cover
private ImageView mCover = null;
// Animation cover
private ImageView mAnimCover;
// Animation background
private ImageView mAnimBackground;
// Animation loading icon
private ImageView mLoadingIcon;
// If opening animation has played.
private AtomicBoolean mIsOpen = new AtomicBoolean(false);
// Listener of opening book animation ending
private OpenBookAnimEndListener mOpenBookAnimEndListener;
// Total animations played
private AtomicInteger mAnimationCount = new AtomicInteger(0);
// Total opening animations scheduled
private 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() {
@Override
public 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 views
mAnimCover = new ImageView(mContext);
mAnimCover.setScaleType(mCover.getScaleType());
mAnimCover.setImageDrawable(mCover.getDrawable());
mAnimBackground = new ImageView(mContext);
mAnimBackground.setScaleType(mCover.getScaleType());
// background
Drawable readPageBgDrawable = new ColorDrawable(mContext.getResources().getColor(
R.color.loading_book_bg_color));
mAnimBackground.setBackgroundDrawable(readPageBgDrawable);
// loading icon
mLoadingIcon = 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 scale
DisplayMetrics 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 animation
startFlipCoverAnimation();
}
}
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 icon
startIndividualAnim(mLoadingIcon, "scaleX", 0.0f, 1, true);
startIndividualAnim(mLoadingIcon, "scaleY", 0.0f, 1, true);
// background animation
startIndividualAnim(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 animation
startIndividualAnim(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 icon
startIndividualAnim(mLoadingIcon, "scaleX", 1, 0.0f, false);
startIndividualAnim(mLoadingIcon, "scaleY", 1, 0.0f, false);
// background animation
startIndividualAnim(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 animation
startIndividualAnim(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;
}
}
@Override
public 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();
}
}
}
@Override
public void onAnimationRepeat(Animator arg0) {
}
@Override
public void onAnimationStart(Animator arg0) {
}
@Override
public 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);
}
}
资源下载地址
欢迎提出改进意见~
使用ObjectAnimator开发打开、关闭书本动画的更多相关文章
- [deviceone开发]-打开新页动画效果
一.简介 do_App的openPage支持16种过场动画,这个示例直观的展示16种动画的效果.适合初学者. 二.效果图 三.相关下载 https://github.com/do-project/co ...
- activity的打开关闭动画
Activity的打开关闭或者说相互跳转之间可以设置动画的.默认的打开关闭直接消失或出现,比较不优美,但是有的手机Rom对这个默认做了修改,比如红米HM1,默认的就是新页面自右向左滑动出现,自左向右滑 ...
- 教你win7关闭开机动画,大幅度加快开机时间
Win7系统如何关闭开机动画 Win7系统开机动画关闭教程,以前我们说过很多种帮助Win7开机加速的方法,比如减少Win7开机启动的程序.服务或计划任务等.不过这些都优化都是针对已经进入Win7系统后 ...
- CentOS7使用firewalld打开关闭防火墙与端口(转载)
1.firewalld的基本使用 启动: systemctl start firewalld 查看状态: systemctl status firewalld 停止: systemctl disabl ...
- Windows 7个性化配置,关闭Win7动画效果,设置窗口背景为“ 豆绿色”
减少眼睛疲劳配色(豆绿色): RGB:, , ,颜色名称:#C7EDCC 1.任务栏设置 2.关闭Win7动画效果 控制面板 -> 轻松访问 -> 优化视频显示 3.去掉窗口阴影 右键单击 ...
- iOS开发UI篇—核心动画(UIView封装动画)
iOS开发UI篇—核心动画(UIView封装动画) 一.UIView动画(首尾) 1.简单说明 UIKit直接将动画集成到UIView类中,当内部的一些属性发生改变时,UIView将为这些改变提供动画 ...
- iOS开发UI篇—核心动画(转场动画和组动画)
转自:http://www.cnblogs.com/wendingding/p/3801454.html iOS开发UI篇—核心动画(转场动画和组动画) 一.转场动画简单介绍 CAAnimation的 ...
- iOS开发UI篇—核心动画(关键帧动画)
转自:http://www.cnblogs.com/wendingding/p/3801330.html iOS开发UI篇—核心动画(关键帧动画) 一.简单介绍 是CApropertyAnimatio ...
- iOS开发UI篇—核心动画(基础动画)
转自:http://www.cnblogs.com/wendingding/p/3801157.html 文顶顶 最怕你一生碌碌无为 还安慰自己平凡可贵 iOS开发UI篇—核心动画(基础动画) iOS ...
随机推荐
- 【Python】Coding the Matrix:Week 5: Dimension Homework 5
这一周的作业,刚压线写完.Problem3 没有写,不想证明了.从Problem 9 开始一直到最后难度都挺大的,我是在论坛上看过了别人的讨论才写出来的,挣扎了很久. Problem 9在给定的基上分 ...
- structs2标签
Struts2常用标签总结 一 介绍 1.Struts2的作用 Struts2标签库提供了主题.模板支持,极大地简化了视图页面的编写,而且,struts2的主题.模板都提供了很好的扩展性.实现了更好的 ...
- C的|、||、&、&&、异或、~、!运算(转)
位运算 位运算的运算分量只能是整型或字符型数据,位运算把运算对象看作是由二进位组成的位串信息,按位完成指定的运算,得到位串信息的结果. 位运算符有: &(按位与).|(按位或) ...
- 用 oracle vitual box 克隆虚拟机,找不到eth0的解决方案
用 oracle vitual box 克隆虚拟机 当我们需要使用多台虚拟机的时候,如果一台一台的安装,实在是太过麻烦了.所以一般的虚拟机软件都为我们提供了克隆已有虚拟机状态的功能.Oracle vi ...
- R学习笔记
把学习过程记载下来,加深印象,到时要是忘了也容易查,有需要的同学也可以参考: 1.包的安装:两种方法:一种通过R的菜单,先设定cran镜像,然后安装程序包,会出来一个列表,选择相应程序包安装,安装完毕 ...
- 《JavaScript 闯关记》之语句
表达式在 JavaScript 中是短语,那么语句就是整句命令.表达式用来计算出一个值,语句用来执行以使某件事发生.从本质上看,语句定义了 JavaScript 中的主要语法,语句通常使用一或多个关键 ...
- Charles的使用教程
Charles是mac os和windows下的另外一个抓包软件(均收费,可破解),功能与fiddler类似,优点是可以自定义上下行网速.External Proxy.反向代理配置简单.可解析AMF协 ...
- Jquery Enter事件
//IE或fireFox Event不同,所有要消除浏览器差异问题 <script type="text/javascript"> $('#<%=txtKeyWo ...
- win10下安装通过Hyper-v安装Ubuntu
一直也来在做C#的开发,Winform及Web都有所涉及,想在闲暇之余学习下Python,拓展一下自己的知识.既然决定学习Python那么就直接在Linux下进行吧,由于Ubuntu最近很火而且也有方 ...
- c# 调用EXCEL在VS上能正常运行,部署在IIS上不能实现,在VS中运行页面和发布之后在IIS中运行的区别
发现一篇文章,很好,解决了这个问题:感谢原博主!特此做个笔记. 地址:http://www.cnblogs.com/zhongxinWang/p/3275154.html 发布在IIS上的Web程序, ...