diff --git a/lib/src/nativescript-ngx-shadow/common/shadow.ts b/lib/src/nativescript-ngx-shadow/common/shadow.ts index fa430c5..018676b 100755 --- a/lib/src/nativescript-ngx-shadow/common/shadow.ts +++ b/lib/src/nativescript-ngx-shadow/common/shadow.ts @@ -13,12 +13,48 @@ declare const UIScreen: any; declare const Array: any; declare const UIBezierPath: any; -let LayeredShadow; -let PlainShadow; +const ViewOutlineProvider: { new(); BACKGROUND?: any } = android.view.ViewOutlineProvider; + +let ShadowOutlineProvider: any; + +function initializeShadowOutlineProvider() { + if (ShadowOutlineProvider) { return; } + class ShadowOutlineProviderImpl extends ViewOutlineProvider { + owner: WeakRef; + /** + * Create an outline from a tns view + * @param owner tns view + */ + constructor(owner: any) { + super(); + this.owner = new WeakRef(owner); + return global.__native(this); + } -if (isAndroid) { - LayeredShadow = android.graphics.drawable.LayerDrawable.extend({}); - PlainShadow = android.graphics.drawable.GradientDrawable.extend({}); + getOutline(view: any, outline: any) { + const owner = this.owner.get(); + const viewBg = view.getBackground(); + if (viewBg) { + viewBg.getOutline(outline); + if (!outline.isEmpty() && outline.getAlpha() !== 0) { + return; + } + outline.setEmpty(); + outline.setAlpha(1); + } + if (owner) { + const outerRadii = Array.create("float", 8); + outerRadii[0] = outerRadii[1] = Length.toDevicePixels(owner.borderTopLeftRadius, 0); + outerRadii[2] = outerRadii[3] = Length.toDevicePixels(owner.borderTopRightRadius, 0); + outerRadii[4] = outerRadii[5] = Length.toDevicePixels(owner.borderBottomRightRadius, 0); + outerRadii[6] = outerRadii[7] = Length.toDevicePixels(owner.borderBottomLeftRadius, 0); + const backgroundPath = new android.graphics.Path(); + backgroundPath.addRoundRect(new android.graphics.RectF(0, 0, view.getWidth(), view.getHeight()), outerRadii, android.graphics.Path.Direction.CW) + outline.setConvexPath(backgroundPath); + } + } + } + ShadowOutlineProvider = ShadowOutlineProviderImpl; } const classCache: { [id: string]: { class: any, fieldCache: { [id: string]: number } } } = {}; @@ -72,70 +108,12 @@ export class Shadow { ); } - private static isShadow(drawable: any): boolean { - return (drawable instanceof LayeredShadow || drawable instanceof PlainShadow); - } - private static applyOnAndroid(tnsView: any, data: AndroidData) { const nativeView = tnsView.android; - let shape; - let overrideBackground = true; - - - let currentBg = nativeView.getBackground(); - - if (currentBg instanceof android.graphics.drawable.RippleDrawable) { // play nice if a ripple is wrapping a shadow - let rippleBg = currentBg.getDrawable(0); - if (rippleBg instanceof android.graphics.drawable.InsetDrawable) { - overrideBackground = false; // this is a button with it's own shadow - } else if (Shadow.isShadow(rippleBg)) { // if the ripple is wrapping a shadow, strip it - currentBg = rippleBg; - } - } - if (overrideBackground) { - if (Shadow.isShadow(currentBg)) { // make sure to have the right background - currentBg = currentBg instanceof LayeredShadow ? // if layered, get the original background - currentBg.getDrawable(1) : null; - } - - const outerRadii = Array.create("float", 8); - if (data.cornerRadius === undefined) { - outerRadii[0] = outerRadii[1] = Length.toDevicePixels(tnsView.borderTopLeftRadius, 0); - outerRadii[2] = outerRadii[3] = Length.toDevicePixels(tnsView.borderTopRightRadius, 0); - outerRadii[4] = outerRadii[5] = Length.toDevicePixels(tnsView.borderBottomRightRadius, 0); - outerRadii[6] = outerRadii[7] = Length.toDevicePixels(tnsView.borderBottomLeftRadius, 0); - } else { - java.util.Arrays.fill(outerRadii, Shadow.androidDipToPx(nativeView, data.cornerRadius as number)); - } - - // use the user defined color or the default in case the color is TRANSPARENT - const bgColor = currentBg ? - (currentBg instanceof android.graphics.drawable.ColorDrawable && currentBg.getColor() ? - currentBg.getColor() : android.graphics.Color.parseColor(data.bgcolor || Shadow.DEFAULT_BGCOLOR)) : - android.graphics.Color.parseColor(data.bgcolor || Shadow.DEFAULT_BGCOLOR); - - let newBg; - - if (data.shape !== ShapeEnum.RECTANGLE || data.bgcolor || !currentBg) { // replace background - shape = new PlainShadow(); - shape.setShape( - android.graphics.drawable.GradientDrawable[data.shape], - ); - shape.setCornerRadii(outerRadii); - shape.setColor(bgColor); - newBg = shape; - } else { // add a layer - const r = new android.graphics.drawable.shapes.RoundRectShape(outerRadii, null, null); - shape = new android.graphics.drawable.ShapeDrawable(r); - shape.getPaint().setColor(bgColor); - var arr = Array.create(android.graphics.drawable.Drawable, 2); - arr[0] = shape; - arr[1] = currentBg; - const drawable = new LayeredShadow(arr); - newBg = drawable; - } - nativeView.setBackgroundDrawable(newBg); + initializeShadowOutlineProvider(); + if (nativeView.getOutlineProvider() === ViewOutlineProvider.BACKGROUND) { // override all background providers + nativeView.setOutlineProvider(new ShadowOutlineProvider(tnsView)); } nativeView.setElevation( diff --git a/lib/src/nativescript-ngx-shadow/ng-shadow.directive.ts b/lib/src/nativescript-ngx-shadow/ng-shadow.directive.ts index 81adc1d..f0a8629 100755 --- a/lib/src/nativescript-ngx-shadow/ng-shadow.directive.ts +++ b/lib/src/nativescript-ngx-shadow/ng-shadow.directive.ts @@ -1,7 +1,6 @@ import { Directive, ElementRef, - HostListener, Input, OnChanges, OnInit, @@ -42,16 +41,10 @@ export class NativeShadowDirective implements OnInit, OnChanges, AfterViewInit, private loaded = false; private initialized = false; - private originalNSFn: any; - private previousNSFn: any; private iosShadowRapper: View; private eventsBound = false; - constructor(private el: ElementRef, private render: Renderer2) { - if (isAndroid) { - this.originalNSFn = this.el.nativeElement._redrawNativeBackground; //always store the original method - } - } + constructor(private el: ElementRef, private render: Renderer2) { } ngOnInit() { // RadListView calls this multiple times for the same view if (!this.initialized) { @@ -117,10 +110,6 @@ export class NativeShadowDirective implements OnInit, OnChanges, AfterViewInit, this.ngOnInit(); } this.applyShadow(); - if (isAndroid) { - this.previousNSFn = this.el.nativeElement._redrawNativeBackground; // just to maintain compatibility with other patches - this.el.nativeElement._redrawNativeBackground = this.monkeyPatch; - } } private addIosWrapper() { @@ -141,10 +130,6 @@ export class NativeShadowDirective implements OnInit, OnChanges, AfterViewInit, onUnloaded() { this.loaded = false; - - if (isAndroid) { - this.el.nativeElement._redrawNativeBackground = this.originalNSFn; // always revert to the original method - } } ngAfterViewInit() { this.addIosWrapper(); @@ -189,11 +174,6 @@ export class NativeShadowDirective implements OnInit, OnChanges, AfterViewInit, } } - private monkeyPatch = val => { - this.previousNSFn.call(this.el.nativeElement, val); - this.applyShadow(); - }; - private applyShadow() { if ( this.shadow === null ||