Skip to content

Commit 0a91948

Browse files
committed
fix: added strech property. Sizing now behaves like N Image
1 parent df3de2a commit 0a91948

File tree

5 files changed

+756
-245
lines changed

5 files changed

+756
-245
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package com.nativescript.lottie;
2+
3+
import androidx.core.view.ViewCompat;
4+
import android.view.View.MeasureSpec;
5+
import android.graphics.drawable.Drawable;
6+
7+
public class LottieAnimationView extends com.airbnb.lottie.LottieAnimationView {
8+
private double scaleW = 1;
9+
private double scaleH = 1;
10+
11+
public LottieAnimationView(android.content.Context context) {
12+
super(context);
13+
}
14+
15+
@Override
16+
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
17+
18+
int width = MeasureSpec.getSize(widthMeasureSpec);
19+
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
20+
21+
int height = MeasureSpec.getSize(heightMeasureSpec);
22+
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
23+
24+
Drawable drawable = this.getDrawable();
25+
int measureWidth;
26+
int measureHeight;
27+
if (drawable != null) {
28+
measureWidth = drawable.getIntrinsicWidth();
29+
measureHeight = drawable.getIntrinsicHeight();
30+
} else {
31+
measureWidth = 0;
32+
measureHeight = 0;
33+
}
34+
35+
boolean finiteWidth = widthMode != MeasureSpec.UNSPECIFIED;
36+
boolean finiteHeight = heightMode != MeasureSpec.UNSPECIFIED;
37+
38+
if (measureWidth != 0 && measureHeight != 0 && (finiteWidth || finiteHeight)) {
39+
this.computeScaleFactor(width, height, finiteWidth, finiteHeight, measureWidth, measureHeight);
40+
int resultW = (int) Math.round(measureWidth * this.scaleW);
41+
int resultH = (int) Math.round(measureHeight * this.scaleH);
42+
43+
measureWidth = finiteWidth ? Math.min(resultW, width) : resultW;
44+
measureHeight = finiteHeight ? Math.min(resultH, height) : resultH;
45+
}
46+
47+
measureWidth += this.getPaddingLeft() + this.getPaddingRight();
48+
measureHeight += this.getPaddingTop() + this.getPaddingBottom();
49+
50+
measureWidth = Math.max(measureWidth, getSuggestedMinimumWidth());
51+
measureHeight = Math.max(measureHeight, getSuggestedMinimumHeight());
52+
53+
int widthSizeAndState = ViewCompat.resolveSizeAndState(measureWidth, widthMeasureSpec, 0);
54+
int heightSizeAndState = ViewCompat.resolveSizeAndState(measureHeight, heightMeasureSpec, 0);
55+
56+
this.setMeasuredDimension(widthSizeAndState, heightSizeAndState);
57+
}
58+
59+
private void computeScaleFactor(int measureWidth, int measureHeight, boolean widthIsFinite, boolean heightIsFinite, double nativeWidth, double nativeHeight) {
60+
61+
this.scaleW = 1;
62+
this.scaleH = 1;
63+
64+
ScaleType scale = this.getScaleType();
65+
if ((scale == ScaleType.CENTER_CROP || scale == ScaleType.FIT_CENTER || scale == ScaleType.FIT_XY) &&
66+
(widthIsFinite || heightIsFinite)) {
67+
68+
this.scaleW = (nativeWidth > 0) ? measureWidth / nativeWidth : 0d;
69+
this.scaleH = (nativeHeight > 0) ? measureHeight / nativeHeight : 0d;
70+
71+
if (!widthIsFinite) {
72+
this.scaleW = scaleH;
73+
} else if (!heightIsFinite) {
74+
this.scaleH = scaleW;
75+
} else {
76+
// No infinite dimensions.
77+
switch (scale) {
78+
case FIT_CENTER:
79+
this.scaleH = this.scaleW < this.scaleH ? this.scaleW : this.scaleH;
80+
this.scaleW = this.scaleH;
81+
break;
82+
case CENTER_CROP:
83+
this.scaleH = this.scaleW > this.scaleH ? this.scaleW : this.scaleH;
84+
this.scaleW = this.scaleH;
85+
break;
86+
default:
87+
break;
88+
}
89+
}
90+
}
91+
}
92+
}

src/lottie.android.ts

Lines changed: 60 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,24 @@
55
* Version 1.0.0 [email protected]
66
**********************************************************************************/
77

8-
import { View } from '@nativescript/core/ui/core/view';
98
import { Color } from '@nativescript/core/color';
9+
import { File } from '@nativescript/core/file-system';
10+
import { View } from '@nativescript/core/ui/core/view';
11+
import { RESOURCE_PREFIX } from '@nativescript/core/utils/utils';
1012
import {
1113
LottieViewBase,
1214
autoPlayProperty,
1315
loopProperty,
1416
progressProperty,
1517
renderModeProperty,
1618
srcProperty,
19+
stretchProperty,
1720
} from './lottie.common';
18-
import { RESOURCE_PREFIX } from '@nativescript/core/utils/utils';
19-
import { File, knownFolders, path } from '@nativescript/core/file-system';
2021
import { clamp } from './utils';
21-
import { profile } from '@nativescript/core/profiling';
22-
const appPath = knownFolders.currentApp().path;
2322

2423
let LottieProperty;
2524
let LottieKeyPath;
2625
let LottieValueCallback;
27-
let LottieAnimationView;
2826

2927
const cache = new Map();
3028
function loadLottieJSON(iconSrc) {
@@ -51,17 +49,10 @@ export const RenderMode = {
5149
};
5250

5351
export class LottieView extends LottieViewBase {
54-
nativeView: com.airbnb.lottie.LottieAnimationView;
55-
//@ts-ignore
56-
get android(): any {
57-
return this.nativeView;
58-
}
52+
nativeViewProtected: com.airbnb.lottie.LottieAnimationView;
5953

60-
public createNativeView(): View {
61-
if (!LottieAnimationView) {
62-
LottieAnimationView = com.airbnb.lottie.LottieAnimationView;
63-
}
64-
return new LottieAnimationView(this._context);
54+
public createNativeView() {
55+
return new com.nativescript.lottie.LottieAnimationView(this._context);
6556
}
6657

6758
animatorListener;
@@ -92,20 +83,20 @@ export class LottieView extends LottieViewBase {
9283
// noop
9384
},
9485
});
95-
if (this.nativeView) {
96-
this.nativeView.addAnimatorListener(this.animatorListener);
86+
if (this.nativeViewProtected) {
87+
this.nativeViewProtected.addAnimatorListener(this.animatorListener);
9788
}
9889
}
9990
} else if (this.animatorListener) {
100-
if (this.nativeView) {
101-
this.nativeView.removeAnimatorListener(this.animatorListener);
91+
if (this.nativeViewProtected) {
92+
this.nativeViewProtected.removeAnimatorListener(this.animatorListener);
10293
}
10394
this.animatorListener = null;
10495
}
10596
}
10697
public initNativeView(): void {
10798
if (this.animatorListener) {
108-
this.nativeView.addAnimatorListener(this.animatorListener);
99+
this.nativeViewProtected.addAnimatorListener(this.animatorListener);
109100
}
110101
// if (this.src) {
111102
// this[srcProperty.setNative](this.src);
@@ -122,20 +113,20 @@ export class LottieView extends LottieViewBase {
122113

123114
public disposeNativeView(): void {
124115
if (this.animatorListener) {
125-
this.nativeView.removeAnimatorListener(this.animatorListener);
116+
this.nativeViewProtected.removeAnimatorListener(this.animatorListener);
126117
}
127118
super.disposeNativeView();
128119
}
129120

130-
@profile
131-
setSrc(src: string) {
121+
[srcProperty.setNative](src: string) {
122+
const view = this.nativeViewProtected;
132123
if (!src) {
133-
this.nativeView.setAnimation(null);
124+
view.setAnimation(null);
134125
} else if (src[0] === '{') {
135-
this.nativeView.setAnimationFromJson(src, null);
126+
view.setAnimationFromJson(src, null);
136127
} else if (src.startsWith(RESOURCE_PREFIX)) {
137128
const resName = src.replace(RESOURCE_PREFIX, '');
138-
this.nativeView.setAnimation(resName);
129+
view.setAnimation(resName);
139130
} else {
140131
if (!/.(json|zip|lottie)$/.test(src)) {
141132
src += '.json';
@@ -156,15 +147,11 @@ export class LottieView extends LottieViewBase {
156147
}
157148
}
158149

159-
[srcProperty.setNative](src: string) {
160-
this.setSrc(src);
161-
}
162-
163150
[loopProperty.setNative](loop: boolean) {
164-
this.nativeView.loop(loop);
151+
this.nativeViewProtected.loop(loop);
165152
}
166153
[renderModeProperty.setNative](renderMode) {
167-
this.nativeView.setRenderMode(renderMode);
154+
this.nativeViewProtected.setRenderMode(renderMode);
168155
}
169156

170157
[autoPlayProperty.setNative](autoPlay: boolean) {
@@ -177,14 +164,14 @@ export class LottieView extends LottieViewBase {
177164
// if (this.isAnimating()) {
178165
this.cancelAnimation();
179166
if (this.progress) {
180-
this.nativeView.setProgress(this.progress);
167+
this.nativeViewProtected.setProgress(this.progress);
181168
}
182169
// }
183170
}
184171
}
185172

186173
public setColorValueDelegateForKeyPath(value: Color, keyPath: string[]): void {
187-
if (this.nativeView && value && keyPath && keyPath.length) {
174+
if (this.nativeViewProtected && value && keyPath && keyPath.length) {
188175
if (keyPath[keyPath.length - 1].toLowerCase() === 'color') {
189176
keyPath.pop(); // android specifies the property as an enum parameter.
190177
if (keyPath.length === 0) {
@@ -205,7 +192,7 @@ export class LottieView extends LottieViewBase {
205192
if (!LottieKeyPath) {
206193
LottieKeyPath = com.airbnb.lottie.model.KeyPath;
207194
}
208-
this.nativeView.addValueCallback(
195+
this.nativeViewProtected.addValueCallback(
209196
new LottieKeyPath(nativeKeyPath),
210197
LottieProperty.COLOR,
211198
new LottieValueCallback(new java.lang.Integer(value.android))
@@ -214,7 +201,7 @@ export class LottieView extends LottieViewBase {
214201
}
215202

216203
public setOpacityValueDelegateForKeyPath(value: number, keyPath: string[]): void {
217-
if (this.nativeView && value && keyPath && keyPath.length) {
204+
if (this.nativeViewProtected && value && keyPath && keyPath.length) {
218205
if (keyPath[keyPath.length - 1].toLowerCase() === 'opacity') {
219206
keyPath.pop();
220207
if (keyPath.length === 0) {
@@ -236,7 +223,7 @@ export class LottieView extends LottieViewBase {
236223
if (!LottieKeyPath) {
237224
LottieKeyPath = com.airbnb.lottie.model.KeyPath;
238225
}
239-
this.nativeView.addValueCallback(
226+
this.nativeViewProtected.addValueCallback(
240227
new LottieKeyPath(nativeKeyPath),
241228
LottieProperty.OPACITY,
242229
new LottieValueCallback(new java.lang.Integer(value * 100))
@@ -245,27 +232,27 @@ export class LottieView extends LottieViewBase {
245232
}
246233

247234
public playAnimation(): void {
248-
if (this.nativeView) {
249-
this.nativeView.setMinAndMaxProgress(0, 1);
250-
this.nativeView.playAnimation();
235+
if (this.nativeViewProtected) {
236+
this.nativeViewProtected.setMinAndMaxProgress(0, 1);
237+
this.nativeViewProtected.playAnimation();
251238
}
252239
}
253240

254241
public playAnimationFromProgressToProgress(startProgress: number, endProgress: number): void {
255-
if (this.nativeView) {
242+
if (this.nativeViewProtected) {
256243
startProgress = startProgress ? clamp(startProgress) : 0;
257244
endProgress = endProgress ? clamp(endProgress) : 1;
258-
this.nativeView.setMinAndMaxProgress(startProgress, endProgress);
259-
this.nativeView.playAnimation();
245+
this.nativeViewProtected.setMinAndMaxProgress(startProgress, endProgress);
246+
this.nativeViewProtected.playAnimation();
260247
}
261248
}
262249

263250
public isAnimating(): boolean {
264-
return this.nativeView ? this.nativeView.isAnimating() : false;
251+
return this.nativeViewProtected ? this.nativeViewProtected.isAnimating() : false;
265252
}
266253

267254
[progressProperty.setNative](value: number) {
268-
this.nativeView.setProgress(value);
255+
this.nativeViewProtected.setProgress(value);
269256
}
270257
// public set progress(value: number) {
271258
// if (this.nativeView && value) {
@@ -278,22 +265,43 @@ export class LottieView extends LottieViewBase {
278265
// }
279266

280267
public get speed(): number | undefined {
281-
return this.nativeView ? this.nativeView.getSpeed() : undefined;
268+
return this.nativeViewProtected ? this.nativeViewProtected.getSpeed() : undefined;
282269
}
283270

284271
public set speed(value: number) {
285-
if (this.nativeView && value) {
286-
this.nativeView.setSpeed(value);
272+
if (this.nativeViewProtected && value) {
273+
this.nativeViewProtected.setSpeed(value);
287274
}
288275
}
289276

290277
public get duration(): number | undefined {
291-
return this.nativeView ? this.nativeView.getDuration() : undefined;
278+
return this.nativeViewProtected ? this.nativeViewProtected.getDuration() : undefined;
292279
}
293280

294281
public cancelAnimation(): void {
295-
if (this.nativeView) {
296-
this.nativeView.cancelAnimation();
282+
if (this.nativeViewProtected) {
283+
this.nativeViewProtected.cancelAnimation();
284+
}
285+
}
286+
287+
[stretchProperty.getDefault](): 'aspectFit' {
288+
return 'aspectFit';
289+
}
290+
[stretchProperty.setNative](value: 'none' | 'aspectFill' | 'aspectFit' | 'fill') {
291+
switch (value) {
292+
case 'aspectFit':
293+
this.nativeViewProtected.setScaleType(android.widget.ImageView.ScaleType.FIT_CENTER);
294+
break;
295+
case 'aspectFill':
296+
this.nativeViewProtected.setScaleType(android.widget.ImageView.ScaleType.CENTER_CROP);
297+
break;
298+
case 'fill':
299+
this.nativeViewProtected.setScaleType(android.widget.ImageView.ScaleType.FIT_XY);
300+
break;
301+
case 'none':
302+
default:
303+
this.nativeViewProtected.setScaleType(android.widget.ImageView.ScaleType.MATRIX);
304+
break;
297305
}
298306
}
299307
}

src/lottie.common.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
* Version 1.0.0 [email protected]
66
**********************************************************************************/
77

8+
import { CoreTypes } from '@nativescript/core/core-types';
89
import { Property } from '@nativescript/core/ui/core/properties';
910
import { View } from '@nativescript/core/ui/core/view';
1011
import { booleanConverter } from '@nativescript/core/ui/core/view-base';
1112

1213
export class LottieViewBase extends View {
14+
public stretch: CoreTypes.ImageStretchType;
1315
public src: string;
1416
public loop: boolean;
1517
public autoPlay: boolean;
@@ -45,3 +47,10 @@ export const progressProperty = new Property<LottieViewBase, number>({
4547
name: 'progress',
4648
});
4749
progressProperty.register(LottieViewBase);
50+
51+
export const stretchProperty = new Property<LottieViewBase, CoreTypes.ImageStretchType>({
52+
name: 'stretch',
53+
defaultValue: 'aspectFit',
54+
affectsLayout: global.isIOS,
55+
});
56+
stretchProperty.register(LottieViewBase);

0 commit comments

Comments
 (0)