Merge pull request #2222 from NativeScript/hardware-animations

Enable hardware acceleration for views animations in Android
This commit is contained in:
Rossen Hristov
2016-06-01 09:15:15 +03:00
3 changed files with 48 additions and 25 deletions

View File

@ -7,6 +7,7 @@ import types = require("utils/types");
import enums = require("ui/enums"); import enums = require("ui/enums");
import styleModule = require("ui/styling/style"); import styleModule = require("ui/styling/style");
import lazy from "utils/lazy"; import lazy from "utils/lazy";
import { CacheLayerType } from "utils/utils";
global.moduleMerge(common, exports); global.moduleMerge(common, exports);
@ -41,29 +42,22 @@ export class Animation extends common.Animation implements definition.Animation
public play(): definition.AnimationPromise { public play(): definition.AnimationPromise {
let animationFinishedPromise = super.play(); let animationFinishedPromise = super.play();
let i: number;
let length: number;
this._animators = new Array<android.animation.Animator>(); this._animators = new Array<android.animation.Animator>();
this._propertyUpdateCallbacks = new Array<Function>(); this._propertyUpdateCallbacks = new Array<Function>();
this._propertyResetCallbacks = new Array<Function>(); this._propertyResetCallbacks = new Array<Function>();
i = 0; for (let i = 0, length = this._propertyAnimations.length; i < length; i++) {
length = this._propertyAnimations.length;
for (; i < length; i++) {
this._createAnimators(this._propertyAnimations[i]); this._createAnimators(this._propertyAnimations[i]);
} }
this._nativeAnimatorsArray = (<any>Array).create(android.animation.Animator, this._animators.length); this._nativeAnimatorsArray = (<any>Array).create(android.animation.Animator, this._animators.length);
i = 0; for (let i = 0, length = this._animators.length; i < length; i++) {
length = this._animators.length;
for (; i < length; i++) {
this._nativeAnimatorsArray[i] = this._animators[i]; this._nativeAnimatorsArray[i] = this._animators[i];
} }
this._animatorSet = new android.animation.AnimatorSet(); this._animatorSet = new android.animation.AnimatorSet();
this._animatorSet.addListener(this._animatorListener); this._animatorSet.addListener(this._animatorListener);
if (length > 0) { if (this._animators.length > 0) {
if (this._playSequentially) { if (this._playSequentially) {
this._animatorSet.playSequentially(this._nativeAnimatorsArray); this._animatorSet.playSequentially(this._nativeAnimatorsArray);
} }
@ -72,6 +66,8 @@ export class Animation extends common.Animation implements definition.Animation
} }
} }
this._enableHardwareAcceleration();
if (trace.enabled) { if (trace.enabled) {
trace.write("Starting " + this._nativeAnimatorsArray.length + " animations " + (this._playSequentially ? "sequentially." : "together."), trace.categories.Animation); trace.write("Starting " + this._nativeAnimatorsArray.length + " animations " + (this._playSequentially ? "sequentially." : "together."), trace.categories.Animation);
} }
@ -79,7 +75,7 @@ export class Animation extends common.Animation implements definition.Animation
this._animatorSet.start(); this._animatorSet.start();
return animationFinishedPromise; return animationFinishedPromise;
} }
public cancel(): void { public cancel(): void {
super.cancel(); super.cancel();
if (trace.enabled) { if (trace.enabled) {
@ -133,6 +129,7 @@ export class Animation extends common.Animation implements definition.Animation
for (; i < length; i++) { for (; i < length; i++) {
this._propertyUpdateCallbacks[i](); this._propertyUpdateCallbacks[i]();
} }
this._disableHardwareAcceleration();
this._resolveAnimationFinishedPromise(); this._resolveAnimationFinishedPromise();
} }
@ -142,11 +139,11 @@ export class Animation extends common.Animation implements definition.Animation
for (; i < length; i++) { for (; i < length; i++) {
this._propertyResetCallbacks[i](); this._propertyResetCallbacks[i]();
} }
this._disableHardwareAcceleration();
this._rejectAnimationFinishedPromise(); this._rejectAnimationFinishedPromise();
} }
private _createAnimators(propertyAnimation: common.PropertyAnimation): void { private _createAnimators(propertyAnimation: common.PropertyAnimation): void {
if (!propertyAnimation.target._nativeView) { if (!propertyAnimation.target._nativeView) {
return; return;
} }
@ -168,7 +165,7 @@ export class Animation extends common.Animation implements definition.Animation
} }
let nativeArray; let nativeArray;
let nativeView: android.view.View = (<android.view.View>propertyAnimation.target._nativeView); let nativeView = <android.view.View>propertyAnimation.target._nativeView;
let animators = new Array<android.animation.Animator>(); let animators = new Array<android.animation.Animator>();
let propertyUpdateCallbacks = new Array<Function>(); let propertyUpdateCallbacks = new Array<Function>();
let propertyResetCallbacks = new Array<Function>(); let propertyResetCallbacks = new Array<Function>();
@ -375,6 +372,27 @@ export class Animation extends common.Animation implements definition.Animation
private static _getAndroidRepeatCount(iterations: number): number { private static _getAndroidRepeatCount(iterations: number): number {
return (iterations === Number.POSITIVE_INFINITY) ? android.view.animation.Animation.INFINITE : iterations - 1; return (iterations === Number.POSITIVE_INFINITY) ? android.view.animation.Animation.INFINITE : iterations - 1;
} }
private _enableHardwareAcceleration() {
for (let i = 0, length = this._propertyAnimations.length; i < length; i++) {
let cache = <CacheLayerType>this._propertyAnimations[i].target._nativeView;
let layerType = cache.getLayerType();
if (layerType !== android.view.View.LAYER_TYPE_HARDWARE) {
cache.layerType = layerType;
cache.setLayerType(android.view.View.LAYER_TYPE_HARDWARE, null);
}
}
}
private _disableHardwareAcceleration() {
for (let i = 0, length = this._propertyAnimations.length; i < length; i++) {
let cache = <CacheLayerType>this._propertyAnimations[i].target._nativeView;
if (cache.layerType !== undefined) {
cache.setLayerType(cache.layerType, null);
cache.layerType = undefined;
}
}
}
} }
let easeIn = lazy(() => new android.view.animation.AccelerateInterpolator(1)); let easeIn = lazy(() => new android.view.animation.AccelerateInterpolator(1));
@ -422,4 +440,4 @@ export function _resolveAnimationCurve(curve: any): any {
} }
return curve; return curve;
} }
} }

View File

@ -5,6 +5,7 @@ import view = require("ui/core/view");
import types = require("utils/types"); import types = require("utils/types");
import * as styleModule from "./style"; import * as styleModule from "./style";
import * as buttonModule from "ui/button"; import * as buttonModule from "ui/button";
import { CacheLayerType } from "utils/utils";
//@private //@private
declare module "ui/styling/background" { declare module "ui/styling/background" {
@ -238,10 +239,6 @@ export module ad {
var _defaultBackgrounds = new Map<string, android.graphics.drawable.Drawable>(); var _defaultBackgrounds = new Map<string, android.graphics.drawable.Drawable>();
interface CacheLayerType {
layerType: number;
}
export function onBackgroundOrBorderPropertyChanged(v: view.View) { export function onBackgroundOrBorderPropertyChanged(v: view.View) {
var nativeView = <android.view.View>v._nativeView; var nativeView = <android.view.View>v._nativeView;
var cache = <CacheLayerType>v._nativeView; var cache = <CacheLayerType>v._nativeView;
@ -266,8 +263,8 @@ export module ad {
let backgroundColor = bkg.backgroundColor = v.style._getValue(style.backgroundColorProperty).android; let backgroundColor = bkg.backgroundColor = v.style._getValue(style.backgroundColorProperty).android;
bkg.setColorFilter(backgroundColor, android.graphics.PorterDuff.Mode.SRC_IN); bkg.setColorFilter(backgroundColor, android.graphics.PorterDuff.Mode.SRC_IN);
bkg.backgroundColor = backgroundColor; bkg.backgroundColor = backgroundColor;
} else if (v.borderWidth !== 0 || v.borderRadius !== 0 || !backgroundValue.isEmpty() || clipPathValue) { }
else if (v.borderWidth || v.borderRadius || clipPathValue || !backgroundValue.isEmpty()) {
if (!(bkg instanceof BorderDrawableClass)) { if (!(bkg instanceof BorderDrawableClass)) {
bkg = new BorderDrawableClass(); bkg = new BorderDrawableClass();
let viewClass = types.getClass(v); let viewClass = types.getClass(v);
@ -284,11 +281,11 @@ export module ad {
bkg.background = backgroundValue; bkg.background = backgroundValue;
bkg.clipPath = clipPathValue; bkg.clipPath = clipPathValue;
if ((v.borderWidth !== 0 || v.borderRadius !== 0 || clipPathValue) && getSDK() < 18) { if ((v.borderWidth || v.borderRadius || clipPathValue) && getSDK() < 18) {
// Switch to software because of unsupported canvas methods if hardware acceleration is on: // Switch to software because of unsupported canvas methods if hardware acceleration is on:
// http://developer.android.com/guide/topics/graphics/hardware-accel.html // http://developer.android.com/guide/topics/graphics/hardware-accel.html
cache.layerType = nativeView.getLayerType(); cache.layerType = cache.getLayerType();
nativeView.setLayerType(android.view.View.LAYER_TYPE_SOFTWARE, null); cache.setLayerType(android.view.View.LAYER_TYPE_SOFTWARE, null);
} }
} }
else { else {
@ -305,8 +302,7 @@ export module ad {
} }
if (cache.layerType !== undefined) { if (cache.layerType !== undefined) {
// Reset layer type cache.setLayerType(cache.layerType, null);
nativeView.setLayerType(cache.layerType, null);
cache.layerType = undefined; cache.layerType = undefined;
} }
} }

View File

@ -10,6 +10,15 @@
interface Owned { interface Owned {
owner: any; owner: any;
} }
/**
* Used to cache and restore Android views' layer type, i.e. android.view.View.getLayerType and android.view.View.setLayerType.
*/
interface CacheLayerType {
layerType: number;
setLayerType(layerType: number, paint: any): void;
getLayerType(): number;
}
//@endprivate //@endprivate
/** /**