diff --git a/packages/core/ui/core/view/index.android.ts b/packages/core/ui/core/view/index.android.ts index d4d52adba..07fe9346b 100644 --- a/packages/core/ui/core/view/index.android.ts +++ b/packages/core/ui/core/view/index.android.ts @@ -4,7 +4,7 @@ import type { GestureTypes, GestureEventData } from '../../gestures'; // Types. import { ViewCommon, isEnabledProperty, originXProperty, originYProperty, isUserInteractionEnabledProperty } from './view-common'; -import { paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty } from '../../styling/style-properties'; +import { paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty, Length } from '../../styling/style-properties'; import { layout } from '../../../utils'; import { Trace } from '../../../trace'; import { ShowModalOptions, hiddenProperty } from '../view-base'; @@ -14,7 +14,7 @@ import { perspectiveProperty, visibilityProperty, opacityProperty, horizontalAli import { CoreTypes } from '../../../core-types'; import { Background, ad as androidBackground } from '../../styling/background'; -import { refreshBorderDrawable } from '../../styling/background.android'; +import { BackgroundClearFlags, refreshBorderDrawable } from '../../styling/background.android'; import { profile } from '../../../profiling'; import { topmost } from '../../frame/frame-stack'; import { Screen } from '../../../platform'; @@ -24,6 +24,7 @@ import lazy from '../../../utils/lazy'; import { accessibilityEnabledProperty, accessibilityHiddenProperty, accessibilityHintProperty, accessibilityIdentifierProperty, accessibilityLabelProperty, accessibilityLanguageProperty, accessibilityLiveRegionProperty, accessibilityMediaSessionProperty, accessibilityRoleProperty, accessibilityStateProperty, accessibilityValueProperty } from '../../../accessibility/accessibility-properties'; import { AccessibilityLiveRegion, AccessibilityRole, AndroidAccessibilityEvent, setupAccessibleView, isAccessibilityServiceEnabled, sendAccessibilityEvent, updateAccessibilityProperties, updateContentDescription, AccessibilityState } from '../../../accessibility'; import * as Utils from '../../../utils'; +import { CSSShadow } from 'ui/styling/css-shadow'; export * from './view-common'; // helpers (these are okay re-exported here) @@ -49,6 +50,10 @@ const modalMap = new Map(); let TouchListener: TouchListener; let DialogFragment: DialogFragment; +interface AndroidView { + _cachedDrawable: android.graphics.drawable.Drawable.ConstantState | android.graphics.drawable.Drawable; +} + interface DialogOptions { owner: View; fullscreen: boolean; @@ -1114,6 +1119,19 @@ export class View extends ViewCommon { nativeView.setBackground(cachedDrawable); } } + + protected _drawBoxShadow(boxShadow: CSSShadow) { + const nativeView = this.nativeViewProtected; + const config = { + shadowColor: boxShadow.color.android, + cornerRadius: Length.toDevicePixels(this.borderRadius as CoreTypes.LengthType, 0.0), + spreadRadius: Length.toDevicePixels(boxShadow.spreadRadius, 0.0), + blurRadius: Length.toDevicePixels(boxShadow.blurRadius, 0.0), + offsetX: Length.toDevicePixels(boxShadow.offsetX, 0.0), + offsetY: Length.toDevicePixels(boxShadow.offsetY, 0.0), + }; + org.nativescript.widgets.Utils.drawBoxShadow(nativeView, JSON.stringify(config)); + } protected onBackgroundOrBorderPropertyChanged() { const nativeView = this.nativeViewProtected; if (!nativeView) { @@ -1121,11 +1139,36 @@ export class View extends ViewCommon { } const background = this.style.backgroundInternal; + + if (background.clearFlags & BackgroundClearFlags.CLEAR_BOX_SHADOW || background.clearFlags & BackgroundClearFlags.CLEAR_BACKGROUND_COLOR) { + // clear background if we're clearing the box shadow + // or the background has been removed + nativeView.setBackground(null); + } + const drawable = nativeView.getBackground(); + const androidView = (this) as AndroidView; + // use undefined as not set. getBackground will never return undefined only Drawable or null; + if (androidView._cachedDrawable === undefined && drawable) { + const constantState = drawable.getConstantState(); + androidView._cachedDrawable = constantState || drawable; + } const isBorderDrawable = drawable instanceof org.nativescript.widgets.BorderDrawable; - const onlyColor = !background.hasBorderWidth() && !background.hasBorderRadius() && !background.clipPath && !background.image && !!background.color; + + // prettier-ignore + const onlyColor = !background.hasBorderWidth() + && !background.hasBorderRadius() + && !background.hasBoxShadow() + && !background.clipPath + && !background.image + && !!background.color; + this._applyBackground(background, isBorderDrawable, onlyColor, drawable); + if (background.hasBoxShadow()) { + this._drawBoxShadow(background.getBoxShadow()); + } + // TODO: Can we move BorderWidths as separate native setter? // This way we could skip setPadding if borderWidth is not changed. const leftPadding = Math.ceil(this.effectiveBorderLeftWidth + this.effectivePaddingLeft); @@ -1137,6 +1180,8 @@ export class View extends ViewCommon { } else { nativeView.setPadding(leftPadding, topPadding, rightPadding, bottomPadding); } + // reset clear flags + background.clearFlags = BackgroundClearFlags.NONE; } _redrawNativeBackground(value: android.graphics.drawable.Drawable | Background): void { if (value instanceof Background) { diff --git a/packages/core/ui/styling/background.android.ts b/packages/core/ui/styling/background.android.ts index bee7ded34..bd6f9d106 100644 --- a/packages/core/ui/styling/background.android.ts +++ b/packages/core/ui/styling/background.android.ts @@ -1,139 +1,14 @@ import { View } from '../core/view'; import { LinearGradient } from './linear-gradient'; -import { CoreTypes } from '../../core-types'; -import { isDataURI, isFileOrResourcePath, layout, RESOURCE_PREFIX, FILE_PREFIX } from '../../utils'; +import { isDataURI, isFileOrResourcePath, RESOURCE_PREFIX, FILE_PREFIX } from '../../utils'; import { parse } from '../../css-value'; import { path, knownFolders } from '../../file-system'; import * as application from '../../application'; -import { profile } from '../../profiling'; -import { CSSShadow } from './css-shadow'; -import { Length } from './style-properties'; -import { BackgroundClearFlags } from './background-common'; export * from './background-common'; -interface AndroidView { - _cachedDrawable: android.graphics.drawable.Drawable.ConstantState | android.graphics.drawable.Drawable; -} - // TODO: Change this implementation to use // We are using "ad" here to avoid namespace collision with the global android object export namespace ad { - let SDK: number; - function getSDK() { - if (!SDK) { - SDK = android.os.Build.VERSION.SDK_INT; - } - - return SDK; - } - - function isSetColorFilterOnlyWidget(nativeView: android.view.View): boolean { - // prettier-ignore - return ( - nativeView instanceof android.widget.Button - || (nativeView instanceof androidx.appcompat.widget.Toolbar && getSDK() >= 21) - // There is an issue with the DrawableContainer which was fixed - // for API version 21 and above: https://code.google.com/p/android/issues/detail?id=60183 - ); - } - - export function onBackgroundOrBorderPropertyChanged(view: View) { - const nativeView = view.nativeViewProtected; - if (!nativeView) { - return; - } - - const background = view.style.backgroundInternal; - - if (background.clearFlags & BackgroundClearFlags.CLEAR_BOX_SHADOW || background.clearFlags & BackgroundClearFlags.CLEAR_BACKGROUND_COLOR) { - // clear background if we're clearing the box shadow - // or the background has been removed - nativeView.setBackground(null); - } - - let drawable = nativeView.getBackground(); - const androidView = (view) as AndroidView; - // use undefined as not set. getBackground will never return undefined only Drawable or null; - if (androidView._cachedDrawable === undefined && drawable) { - const constantState = drawable.getConstantState(); - androidView._cachedDrawable = constantState || drawable; - } - const isBorderDrawable = drawable instanceof org.nativescript.widgets.BorderDrawable; - - // prettier-ignore - const onlyColor = !background.hasBorderWidth() - && !background.hasBorderRadius() - && !background.hasBoxShadow() - && !background.clipPath - && !background.image - && !!background.color; - - if (!isBorderDrawable && drawable instanceof android.graphics.drawable.ColorDrawable && onlyColor) { - drawable.setColor(background.color.android); - drawable.invalidateSelf(); - } else if (isSetColorFilterOnlyWidget(nativeView) && drawable && onlyColor) { - if (isBorderDrawable && androidView._cachedDrawable) { - if (!(androidView._cachedDrawable instanceof android.graphics.drawable.Drawable.ConstantState)) { - return; - } - - drawable = androidView._cachedDrawable.newDrawable(nativeView.getResources()); - nativeView.setBackground(drawable); - } - - const backgroundColor = ((drawable).backgroundColor = background.color.android); - drawable.mutate(); - drawable.setColorFilter(backgroundColor, android.graphics.PorterDuff.Mode.SRC_IN); - drawable.invalidateSelf(); // Make sure the drawable is invalidated. Android forgets to invalidate it in some cases: toolbar - (drawable).backgroundColor = backgroundColor; - } else if (!isBorderDrawable && onlyColor) { - // this is the fastest way to change only background color - nativeView.setBackgroundColor(background.color.android); - } else if (!background.isEmpty()) { - let backgroundDrawable = drawable; - - if (drawable instanceof org.nativescript.widgets.BoxShadowDrawable) { - // if we have BoxShadow's we have to get the underlying drawable - backgroundDrawable = drawable.getWrappedDrawable(); - } - - if (backgroundDrawable instanceof org.nativescript.widgets.BorderDrawable) { - refreshBorderDrawable(view, backgroundDrawable); - } else { - backgroundDrawable = new org.nativescript.widgets.BorderDrawable(layout.getDisplayDensity(), view.toString()); - refreshBorderDrawable(view, backgroundDrawable); - nativeView.setBackground(backgroundDrawable); - } - } else { - const cachedDrawable = androidView._cachedDrawable; - let defaultDrawable: android.graphics.drawable.Drawable = null; - if (cachedDrawable) { - if (cachedDrawable instanceof android.graphics.drawable.Drawable.ConstantState) { - defaultDrawable = cachedDrawable.newDrawable(nativeView.getResources()); - } else if (cachedDrawable instanceof android.graphics.drawable.Drawable) { - defaultDrawable = cachedDrawable; - } - } - - nativeView.setBackground(defaultDrawable); - } - - if (background.hasBoxShadow()) { - drawBoxShadow(nativeView, view, background.getBoxShadow()); - } - - // TODO: Can we move BorderWidths as separate native setter? - // This way we could skip setPadding if borderWidth is not changed. - const leftPadding = Math.ceil(view.effectiveBorderLeftWidth + view.effectivePaddingLeft); - const topPadding = Math.ceil(view.effectiveBorderTopWidth + view.effectivePaddingTop); - const rightPadding = Math.ceil(view.effectiveBorderRightWidth + view.effectivePaddingRight); - const bottomPadding = Math.ceil(view.effectiveBorderBottomWidth + view.effectivePaddingBottom); - - nativeView.setPadding(leftPadding, topPadding, rightPadding, bottomPadding); - - // reset clear flags - background.clearFlags = BackgroundClearFlags.NONE; - } } function fromBase64(source: string): android.graphics.Bitmap { @@ -253,18 +128,6 @@ function createNativeCSSValueArray(css: string): androidNative.Array