├── .gitignore
├── README.md
├── build.gradle
├── coreanimator2androidlib
├── .gitignore
├── build.gradle
├── libs
│ └── okio-1.6.0.jar
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── org
│ │ └── limlee
│ │ └── coreanimator2androidlib
│ │ └── ApplicationTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── org
│ │ │ └── limlee
│ │ │ └── coreanimator2androidlib
│ │ │ ├── AnimatorImageView.java
│ │ │ ├── AnimatorLayout.java
│ │ │ ├── AnimatorTextView.java
│ │ │ ├── AnimatorUIGroup.java
│ │ │ ├── AnimatorUIImage.java
│ │ │ ├── AnimatorUIView.java
│ │ │ ├── AnimatorViewGroup.java
│ │ │ ├── BitmapLoader.java
│ │ │ ├── KeyFrameAnimation.java
│ │ │ └── UIViewHelper.java
│ └── res
│ │ └── values
│ │ └── strings.xml
│ └── test
│ └── java
│ └── org
│ └── limlee
│ └── coreanimator2androidlib
│ └── ExampleUnitTest.java
├── demo.gif
├── demo
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── demo
│ │ └── org
│ │ └── limlee
│ │ └── demo
│ │ └── ApplicationTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── assets
│ │ └── huiji
│ │ │ ├── huabanfen.png
│ │ │ ├── huabanfen_1.png
│ │ │ ├── huabanfen_2.png
│ │ │ ├── huabanfen_3.png
│ │ │ ├── huabanfen_4.png
│ │ │ ├── huabanlv.png
│ │ │ ├── huabanlv_1.png
│ │ │ ├── huabanlv_2.png
│ │ │ ├── huabanzi.png
│ │ │ ├── huabanzi_1.png
│ │ │ ├── huiji.png
│ │ │ ├── luoxuanjiang_1.png
│ │ │ └── luoxuanjiang_2.png
│ ├── java
│ │ └── demo
│ │ │ └── org
│ │ │ └── limlee
│ │ │ └── demo
│ │ │ ├── MainActivity.java
│ │ │ └── gift
│ │ │ └── HuijiGiftAnimatorView.java
│ └── res
│ │ ├── layout
│ │ └── activity_main.xml
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.png
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── demo
│ └── org
│ └── limlee
│ └── demo
│ └── ExampleUnitTest.java
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
10 | .idea/compiler.xml
11 |
12 | .idea/copyright/profiles_settings.xml
13 |
14 | .idea/gradle.xml
15 |
16 | .idea/misc.xml
17 |
18 | .idea/modules.xml
19 |
20 | .idea/runConfigurations.xml
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CoreAnimator2Android
2 | 直播大礼物实现方案,使用core aminator制作动画,把导出的oc代码翻译为android这边的动画实现
3 |
4 | 
5 |
6 | ## 飞机大礼物实现解析
7 |
8 | ````
9 | public class HuijiGiftAnimatorView extends AnimatorLayout { //所有的大动画礼物都需要继承AnimatorLayout
10 |
11 | ....
12 | @Override
13 | protected RectF initFrame() { //初始化动画画布
14 | return new RectF(0, 0, 750f, 680f);
15 | }
16 |
17 | @Override
18 | protected long duration() { //动画时长
19 | return 5700;
20 | }
21 |
22 | @Override
23 | protected boolean setupHierarchy() { //添加动画视图,这些参数都是由core aminator生成(导出OC代码)的,在这里就直接搬过来用就行了
24 | AnimatorImageView huabanfen_2 = mUIViewHelper.newImageView();
25 | huabanfen_2.bounds = new RectF(0, 0, 62.0f, 60.0f);
26 | huabanfen_2.position = new PointF(375.000f, 340.000f);
27 | huabanfen_2.alpha = 0.0f;
28 | huabanfen_2.image = "huabanfen_2";
29 | addViewWithName(huabanfen_2, "huabanfen_2");
30 |
31 | /**
32 | **这部分是core aminator导出的代码,上面的是翻译使用Android api实现的代码
33 | **/
34 | //UIImageView *花瓣粉2 = [UIImageView new];
35 | //花瓣粉2.bounds = CGRectMake(0, 0, 62.0, 60.0);
36 | //UIImage *img花瓣粉2 = [UIImage imageWithContentsOfFile:[bundle pathForResource:@"花瓣粉_2.png" ofType:nil]];
37 | //if ( img花瓣粉2 == nil ) { NSLog(@"** Warning: Could not create image from '花瓣粉_2.png'.
38 | //Please make sure that it is added to the project directly (not in a folder reference)."); }
39 | //花瓣粉2.image = img花瓣粉2;
40 | //花瓣粉2.contentMode = UIViewContentModeCenter;
41 | //花瓣粉2.layer.position = CGPointMake(375.000, 340.000);
42 | //[__scaling__ addSubview:花瓣粉2];
43 | //viewsByName[@"花瓣粉_2"] = 花瓣粉2;
44 |
45 | ....
46 | return true;
47 | }
48 |
49 | @Override
50 | protected boolean addAnimation() { //为每一个动画视图添加动画属性
51 | KeyFrameAnimation 花瓣绿1RotationAnimation = KeyFrameAnimation.newKeyFrameAnimationWithIOSKeyPath("transform.rotation.z");
52 | 花瓣绿1RotationAnimation.duration = 0.750f;
53 | 花瓣绿1RotationAnimation.values = new float[]{0.000f, 6.283f};
54 | 花瓣绿1RotationAnimation.keyTimes = new float[]{0.000f, 1.000f};
55 | 花瓣绿1RotationAnimation.timingFunctions = new TimeInterpolator[]{null, linearTiming};
56 | 花瓣绿1RotationAnimation.repeatCount = KeyFrameAnimation.HUGE_VALF;
57 | addAnimation(findViewsByName("huabanlv_1"), 花瓣绿1RotationAnimation);
58 |
59 | /**
60 | **这部分是core aminator导出的代码,上面的是翻译使用Android api实现的代码
61 | **/
62 | //CAKeyframeAnimation *花瓣绿1RotationAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"];
63 | //花瓣绿1RotationAnimation.duration = 0.750;
64 | //花瓣绿1RotationAnimation.values = @[@(0.000), @(6.283)];
65 | //花瓣绿1RotationAnimation.keyTimes = @[@(0.000), @(1.000)];
66 | //花瓣绿1RotationAnimation.timingFunctions = @[linearTiming];
67 | //花瓣绿1RotationAnimation.repeatCount = HUGE_VALF;
68 | //花瓣绿1RotationAnimation.beginTime = beginTime;
69 | //花瓣绿1RotationAnimation.fillMode = fillMode;
70 | //花瓣绿1RotationAnimation.removedOnCompletion = removedOnCompletion;
71 | //[[self.viewsByName[@"花瓣绿_1"] layer] addAnimation:花瓣绿1RotationAnimation forKey:@"Untitled Animation_Rotation"];
72 |
73 | ....
74 | return true;
75 | }
76 | }
77 | ````
78 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.2.3'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/coreanimator2androidlib/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/coreanimator2androidlib/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "23.0.3"
6 |
7 | defaultConfig {
8 | minSdkVersion 16
9 | targetSdkVersion 23
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | compile fileTree(include: ['*.jar'], dir: 'libs')
23 | testCompile 'junit:junit:4.12'
24 | compile 'com.android.support:appcompat-v7:23.4.0'
25 | compile files('libs/okio-1.6.0.jar')
26 |
27 | compile 'io.reactivex:rxjava:1.1.6'
28 | compile 'io.reactivex:rxandroid:1.2.1'
29 | }
30 |
--------------------------------------------------------------------------------
/coreanimator2androidlib/libs/okio-1.6.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hidaron/CoreAnimator2Android/55cc5da62a09c5fe9d920c30e4d7bf3235e7848a/coreanimator2androidlib/libs/okio-1.6.0.jar
--------------------------------------------------------------------------------
/coreanimator2androidlib/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Volumes/files1/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/coreanimator2androidlib/src/androidTest/java/org/limlee/coreanimator2androidlib/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package org.limlee.coreanimator2androidlib;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/coreanimator2androidlib/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/coreanimator2androidlib/src/main/java/org/limlee/coreanimator2androidlib/AnimatorImageView.java:
--------------------------------------------------------------------------------
1 | package org.limlee.coreanimator2androidlib;
2 |
3 | import android.content.Context;
4 | import android.graphics.PointF;
5 | import android.graphics.RectF;
6 | import android.renderscript.Float2;
7 | import android.util.AttributeSet;
8 | import android.widget.ImageView;
9 |
10 | public class AnimatorImageView extends ImageView implements AnimatorUIImage {
11 | public RectF bounds;
12 | public PointF position;
13 | public Float2 scale;
14 | public String image;
15 | public float rotation;
16 | public PointF anchorPoint;
17 | public float alpha = 1.0f;
18 |
19 | public AnimatorImageView(Context context) {
20 | super(context);
21 | }
22 |
23 | public AnimatorImageView(Context context, AttributeSet attrs) {
24 | super(context, attrs);
25 | }
26 |
27 | public AnimatorImageView(Context context, AttributeSet attrs, int defStyleAttr) {
28 | super(context, attrs, defStyleAttr);
29 | }
30 |
31 | @Override
32 | public RectF getUIBounds() {
33 | return bounds;
34 | }
35 |
36 | @Override
37 | public Float2 getUIScale() {
38 | return scale;
39 | }
40 |
41 | @Override
42 | public PointF getUIPosition() {
43 | return position;
44 | }
45 |
46 | @Override
47 | public float getUIRotation() {
48 | return rotation;
49 | }
50 |
51 | @Override
52 | public PointF getUIAnchorPoint() {
53 | return anchorPoint;
54 | }
55 |
56 | @Override
57 | public float getUIAlpha() {
58 | return alpha;
59 | }
60 |
61 | @Override
62 | public String getImage() {
63 | return image;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/coreanimator2androidlib/src/main/java/org/limlee/coreanimator2androidlib/AnimatorLayout.java:
--------------------------------------------------------------------------------
1 | package org.limlee.coreanimator2androidlib;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.animation.AnimatorSet;
6 | import android.animation.ObjectAnimator;
7 | import android.animation.PropertyValuesHolder;
8 | import android.animation.TimeAnimator;
9 | import android.animation.ValueAnimator;
10 | import android.annotation.TargetApi;
11 | import android.content.Context;
12 | import android.graphics.Bitmap;
13 | import android.graphics.Color;
14 | import android.graphics.PointF;
15 | import android.graphics.RectF;
16 | import android.os.Build;
17 | import android.renderscript.Float2;
18 | import android.support.annotation.IntDef;
19 | import android.support.annotation.NonNull;
20 | import android.support.v4.view.animation.PathInterpolatorCompat;
21 | import android.text.TextUtils;
22 | import android.util.AttributeSet;
23 | import android.util.DisplayMetrics;
24 | import android.util.Log;
25 | import android.view.Gravity;
26 | import android.view.View;
27 | import android.view.ViewGroup;
28 | import android.view.ViewTreeObserver;
29 | import android.view.animation.DecelerateInterpolator;
30 | import android.view.animation.Interpolator;
31 | import android.view.animation.LinearInterpolator;
32 | import android.widget.FrameLayout;
33 | import android.widget.ImageView;
34 |
35 | import java.lang.annotation.Retention;
36 | import java.lang.annotation.RetentionPolicy;
37 | import java.util.ArrayList;
38 | import java.util.HashMap;
39 | import java.util.List;
40 | import java.util.Map;
41 |
42 | import rx.Observable;
43 | import rx.Subscription;
44 | import rx.android.schedulers.AndroidSchedulers;
45 | import rx.functions.Action1;
46 | import rx.functions.Func1;
47 | import rx.schedulers.Schedulers;
48 |
49 | public abstract class AnimatorLayout extends FrameLayout implements AnimatorUIGroup {
50 | private static final String TAG = AnimatorLayout.class.getSimpleName();
51 | @ContentMode.Type
52 | private int contentMode;
53 | private AnimatorViewGroup mScalingView;
54 | protected UIViewHelper mUIViewHelper;
55 | protected BitmapLoader mBitmapLoader;
56 | protected boolean mIsSetupFinished;
57 | protected boolean mIsAddedAnimator;
58 | protected boolean mIsPlayingAnim;
59 | private List mViewAnimators = new ArrayList<>();
60 | private AnimatorSet mAnimatorSet = new AnimatorSet();
61 | private Runnable mPostAnimRunnable = new Runnable() {
62 | @Override
63 | public void run() {
64 | mAnimatorSet.start();
65 | }
66 | };
67 | private Subscription mLoadUIResSubscription;
68 | private AnimatorTextView mTitleView;
69 |
70 | /**
71 | * abstract
72 | */
73 | /**
74 | * 动画播放区域
75 | *
76 | * @return
77 | */
78 | protected abstract RectF initFrame();
79 |
80 | /**
81 | * 动画名字
82 | *
83 | * @return
84 | */
85 | protected abstract String name();
86 |
87 | /**
88 | * 在这里添加动画所需要的视图
89 | *
90 | * @return
91 | */
92 | protected abstract boolean setupHierarchy();
93 |
94 | /**
95 | * 为已经添加的视图添加动画
96 | *
97 | * @return
98 | */
99 | protected abstract boolean addAnimation();
100 |
101 | /**
102 | * 动画的时长
103 | *
104 | * @return
105 | */
106 | protected abstract long duration();
107 |
108 |
109 | /**
110 | * do not implement
111 | */
112 | @Override
113 | public RectF getUIBounds() {
114 | return null;
115 | }
116 |
117 | @Override
118 | public Float2 getUIScale() {
119 | return null;
120 | }
121 |
122 | @Override
123 | public PointF getUIPosition() {
124 | return null;
125 | }
126 |
127 | @Override
128 | public float getUIRotation() {
129 | return 0;
130 | }
131 |
132 | @Override
133 | public PointF getUIAnchorPoint() {
134 | return null;
135 | }
136 |
137 | @Override
138 | public float getUIAlpha() {
139 | return 0;
140 | }
141 |
142 | /**
143 | * timing funs
144 | */
145 | protected final Interpolator linearTiming = new LinearInterpolator();
146 | protected final Interpolator defaultTiming = PathInterpolatorCompat.create(0.25f, 0.1f, 0.25f, 1f);
147 | protected final Interpolator easeInOutTiming = PathInterpolatorCompat.create(0.42f, 0f, 0.58f, 1f);
148 | protected final Interpolator easeInTiming = PathInterpolatorCompat.create(0.42f, 0f, 1f, 1f);
149 | protected final Interpolator easeOutTiming = PathInterpolatorCompat.create(0f, 0f, 0.58f, 1.0f);
150 | protected final Interpolator instantTiming = PathInterpolatorCompat.create(0.25f, 0.10f, 0.25f, 1.0f);
151 | protected final Interpolator anticipateTiming = PathInterpolatorCompat.create(0.42f, 0f, 1.00f, 1.00f);
152 | protected final Interpolator accelerateTiming = new DecelerateInterpolator();
153 |
154 | public AnimatorLayout(Context context) {
155 | this(context, null);
156 | }
157 |
158 | public AnimatorLayout(Context context, AttributeSet attrs) {
159 | this(context, attrs, 0);
160 | }
161 |
162 | public AnimatorLayout(Context context, AttributeSet attrs, int defStyleAttr) {
163 | super(context, attrs, defStyleAttr);
164 | if (isInEditMode()) return;
165 | if (isDebug()) {
166 | setClipChildren(false);
167 | setClipToPadding(false);
168 | }
169 | mUIViewHelper = new UIViewHelper(context);
170 | mBitmapLoader = new BitmapLoader(context, name(), DisplayMetrics.DENSITY_XHIGH); //默认是2倍图资源
171 | mScalingView = new AnimatorViewGroup(getContext());
172 | mIsSetupFinished = setupHierarchy();
173 | if (mIsSetupFinished) {
174 | addScalingView();
175 | mIsAddedAnimator = addAnimation() && !mViewAnimators.isEmpty();
176 | initAnimator();
177 | }
178 | }
179 |
180 | protected int contentMode() {
181 | return contentMode;
182 | }
183 |
184 | protected boolean isDebug() {
185 | return true;
186 | }
187 |
188 | /**
189 | * 动画播放速率
190 | *
191 | * @return
192 | */
193 | protected int rate() {
194 | return 1;
195 | }
196 |
197 | public void setTitle(CharSequence title) {
198 | if (mTitleView != null) {
199 | mTitleView.setText(title);
200 | }
201 | }
202 |
203 | private void initAnimator() {
204 | mAnimatorSet.playTogether(mViewAnimators);
205 | mAnimatorSet.addListener(new AnimatorListenerAdapter() {
206 | private TimeAnimator mTimeAnimator = new TimeAnimator();
207 |
208 | @Override
209 | public void onAnimationStart(Animator animation) {
210 | final long duration = duration() * rate();
211 | mTimeAnimator.setTimeListener(new TimeAnimator.TimeListener() {
212 | @Override
213 | public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
214 | if (totalTime >= duration) {
215 | mAnimatorSet.cancel();
216 | }
217 | }
218 | });
219 | mTimeAnimator.addListener(new AnimatorListenerAdapter() {
220 | @Override
221 | public void onAnimationEnd(Animator animation) {
222 | mTimeAnimator.setTimeListener(null);
223 | mTimeAnimator.removeAllListeners();
224 | }
225 |
226 | @Override
227 | public void onAnimationCancel(Animator animation) {
228 | mTimeAnimator.setTimeListener(null);
229 | mTimeAnimator.removeAllListeners();
230 | }
231 | });
232 | mTimeAnimator.start();
233 | }
234 |
235 | @Override
236 | public void onAnimationCancel(Animator animation) {
237 | }
238 |
239 | @Override
240 | public void onAnimationEnd(Animator animation) {
241 | clearUIImageBitmap();
242 | mTimeAnimator.cancel();
243 | mIsPlayingAnim = false;
244 | mLoadUIResSubscription = null;
245 | }
246 | });
247 | }
248 |
249 | @Override
250 | protected void onDetachedFromWindow() {
251 | super.onDetachedFromWindow();
252 | stop();
253 | mAnimatorSet.removeAllListeners();
254 | mViewAnimators.clear();
255 | }
256 |
257 | protected void addScalingView() {
258 | if (isDebug()) {
259 | mScalingView.setBackgroundColor(Color.parseColor("#0D000000"));
260 | }
261 | setViewsByName(mScalingView, "__scaling__");
262 | mScalingView.setClipChildren(false);
263 | mScalingView.setClipToPadding(false);
264 | LayoutParams scalingViewLp = generateDefaultLayoutParams();
265 | RectF scalingRect = initFrame();
266 | if (null != scalingRect) {
267 | scalingViewLp = new LayoutParams((int) scalingRect.width(),
268 | (int) scalingRect.height());
269 | }
270 | scalingViewLp.gravity = Gravity.CENTER;
271 | mScalingView.getViewTreeObserver().addOnGlobalLayoutListener(
272 | new ViewTreeObserver.OnGlobalLayoutListener() {
273 | @Override
274 | public void onGlobalLayout() {
275 | if (!mIsSetupFinished) return;
276 | mScalingView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
277 | final int parentWidth = getWidth();
278 | final int parentHeight = getHeight();
279 | float xScale = (float) parentWidth / mScalingView.getWidth();
280 | float yScale = (float) parentHeight / mScalingView.getHeight();
281 | float scale;
282 | switch (contentMode) {
283 | case ContentMode.CONTENT_MODE_SCALETOFILL:
284 | break;
285 | case ContentMode.CONTENT_MODE_SCALEASPECTFILL:
286 | scale = Math.max(xScale, yScale);
287 | xScale = scale;
288 | yScale = scale;
289 | break;
290 | default:
291 | scale = Math.min(xScale, yScale);
292 | xScale = scale;
293 | yScale = scale;
294 | break;
295 | }
296 | if (isDebug()) {
297 | xScale = 1.0f;
298 | yScale = 1.0f;
299 | }
300 | mScalingView.setScaleX(xScale);
301 | mScalingView.setScaleY(yScale);
302 | final float titleScaleX = 1.0f / xScale;
303 | final float titleScaleY = 1.0f / yScale;
304 | if (null != mTitleView) {
305 | mTitleView.setScaleX(titleScaleX);
306 | mTitleView.setScaleY(titleScaleY);
307 | }
308 | }
309 | });
310 | addView(mScalingView, scalingViewLp);
311 | }
312 |
313 | public void play() {
314 | if (!mIsSetupFinished
315 | || !mIsAddedAnimator
316 | || mIsPlayingAnim
317 | || mAnimatorSet.isStarted()
318 | || mAnimatorSet.isRunning()) {
319 | return;
320 | }
321 | mIsPlayingAnim = true;
322 | mLoadUIResSubscription = loadUIImageBitmap()
323 | .observeOn(AndroidSchedulers.mainThread())
324 | .subscribe(new Action1() {
325 | @Override
326 | public void call(Boolean aBoolean) {
327 | if (aBoolean) {
328 | postOnAnimation(mPostAnimRunnable);
329 | } else {
330 | mIsPlayingAnim = false;
331 | }
332 | }
333 | }, new Action1() {
334 | @Override
335 | public void call(Throwable throwable) {
336 | mIsPlayingAnim = false;
337 | }
338 | });
339 | }
340 |
341 | public void stop() {
342 | if (null != mLoadUIResSubscription
343 | && !mLoadUIResSubscription.isUnsubscribed()) {
344 | mLoadUIResSubscription.unsubscribe();
345 | }
346 | removeCallbacks(mPostAnimRunnable);
347 | mAnimatorSet.cancel();
348 | }
349 |
350 | public boolean isRunning() {
351 | return (mAnimatorSet.isStarted()
352 | || mAnimatorSet.isRunning())
353 | && mIsPlayingAnim;
354 | }
355 |
356 | @TargetApi(Build.VERSION_CODES.KITKAT)
357 | public void pause() {
358 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
359 | mAnimatorSet.pause();
360 | }
361 | }
362 |
363 | @TargetApi(Build.VERSION_CODES.KITKAT)
364 | public void resume() {
365 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
366 | mAnimatorSet.resume();
367 | }
368 | }
369 |
370 | @Override
371 | public View findViewsByName(@NonNull String name) {
372 | return findViewWithTag(name);
373 | }
374 |
375 | @Override
376 | public void setViewsByName(@NonNull View view, @NonNull String name) {
377 | view.setTag(name);
378 | }
379 |
380 | @Override
381 | public void addViewWithName(@NonNull View view, @NonNull String name) {
382 | setViewsByName(view, name);
383 | addViewToScalingView(view);
384 | }
385 |
386 | private Map loadChildUIImageBitmap(ViewGroup viewGroup) {
387 | final int childCount = viewGroup.getChildCount();
388 | final Map viewBitmapMap = new HashMap<>();
389 | for (int i = 0; i < childCount; i++) {
390 | View view = viewGroup.getChildAt(i);
391 | if (view instanceof ViewGroup
392 | && view instanceof AnimatorUIGroup) {
393 | Map viewGroupBitmapMap = loadChildUIImageBitmap((ViewGroup) view);
394 | if (!viewGroupBitmapMap.isEmpty()) {
395 | viewBitmapMap.putAll(viewGroupBitmapMap);
396 | }
397 | } else {
398 | if (view instanceof ImageView
399 | && view instanceof AnimatorUIImage) {
400 | final String imageName = ((AnimatorUIImage) view).getImage();
401 | if (!TextUtils.isEmpty(imageName)) {
402 | Bitmap imageBitmap = mBitmapLoader.load(imageName);
403 | if (null != imageBitmap) {
404 | viewBitmapMap.put(view, imageBitmap);
405 | } else {
406 | Log.w(TAG, "** Warning: Could not create image from " + imageName +
407 | ". Please make sure that it is added to the project directly (not in a folder reference).");
408 | }
409 | }
410 | }
411 | }
412 | }
413 | return viewBitmapMap;
414 | }
415 |
416 | private Observable loadUIImageBitmap() {
417 | return Observable.just((Void) null)
418 | .observeOn(Schedulers.io())
419 | .map(new Func1>() {
420 | @Override
421 | public Map call(Void aVoid) {
422 | return loadChildUIImageBitmap(mScalingView);
423 | }
424 | })
425 | .observeOn(AndroidSchedulers.mainThread())
426 | .map(new Func1