10000 fix: refactor background handling. by farfromrefug · Pull Request #9223 · NativeScript/NativeScript · GitHub
[go: up one dir, main page]

Skip to content

fix: refactor background handling. #9223

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion packages/core/ui/action-bar/index.android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,18 @@ import { colorProperty } from '../styling/style-properties';
import { ImageSource } from '../../image-source';
import * as application from '../../application';
import { isAccessibilityServiceEnabled, updateContentDescription } from '../../accessibility';
import type { Background } from '../styling/background';
import { Device } from '../../platform';
import lazy from '../../utils/lazy';

export * from './action-bar-common';

const R_ID_HOME = 0x0102002c;
const ACTION_ITEM_ID_OFFSET = 10000;
const DEFAULT_ELEVATION = 4;

const sdkVersion = lazy(() => parseInt(Device.sdkVersion));

let AppCompatTextView;
let actionItemIdGenerator = ACTION_ITEM_ID_OFFSET;
function generateItemId(): number {
Expand Down Expand Up @@ -59,7 +64,7 @@ function initializeMenuItemClickListener(): void {
return;
}

apiLevel = android.os.Build.VERSION.SDK_INT;
apiLevel = sdkVersion();

AppCompatTextView = androidx.appcompat.widget.AppCompatTextView;

Expand Down Expand Up @@ -216,6 +221,32 @@ export class ActionBar extends ActionBarBase {
this._updateNavigationButton();
}

public _applyBackground(background: Background, isBorderDrawable, onlyColor: boolean, backgroundDrawable: any) {
const nativeView = this.nativeViewProtected;
if (backgroundDrawable && onlyColor && sdkVersion() >= 21) {
if (isBorderDrawable && (<any>nativeView)._cachedDrawable) {
backgroundDrawable = (<any>nativeView)._cachedDrawable;
// we need to duplicate the drawable or we lose the "default" cached drawable
const constantState = backgroundDrawable.getConstantState();
if (constantState) {
try {
backgroundDrawable = constantState.newDrawable(nativeView.getResources());
// eslint-disable-next-line no-empty
} catch {}
}
nativeView.setBackground(backgroundDrawable);
}

const backgroundColor = ((<any>backgroundDrawable).backgroundColor = background.color.android);
backgroundDrawable.mutate();
backgroundDrawable.setColorFilter(backgroundColor, android.graphics.PorterDuff.Mode.SRC_IN);
backgroundDrawable.invalidateSelf(); // Make sure the drawable is invalidated. Android forgets to invalidate it in some cases: toolbar
(<any>backgroundDrawable).backgroundColor = backgroundColor;
} else {
super._applyBackground(background, isBorderDrawable, onlyColor, backgroundDrawable);
}
}

public _onAndroidItemSelected(itemId: number): boolean {
// Handle home button
if (this.navigationButton && itemId === R_ID_HOME) {
Expand Down
27 changes: 27 additions & 0 deletions packages/core/ui/button/index.android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { profile } from '../../profiling';
import { TouchGestureEventData, GestureTypes, TouchAction } from '../gestures';
import { Device } from '../../platform';
import lazy from '../../utils/lazy';
import type { Background } from 'ui/styling/background';

export * from './button-common';

Expand Down Expand Up @@ -58,6 +59,32 @@ export class Button extends ButtonBase {
private _stateListAnimator: any;
private _highlightedHandler: (args: TouchGestureEventData) => void;

public _applyBackground(background: Background, isBorderDrawable, onlyColor: boolean, backgroundDrawable: any) {
const nativeView = this.nativeViewProtected;
if (backgroundDrawable && onlyColor) {
if (isBorderDrawable && (<any>nativeView)._cachedDrawable) {
backgroundDrawable = (<any>nativeView)._cachedDrawable;
// we need to duplicate the drawable or we lose the "default" cached drawable
const constantState = backgroundDrawable.getConstantState();
if (constantState) {
try {
backgroundDrawable = constantState.newDrawable(nativeView.getResources());
// eslint-disable-next-line no-empty
} catch {}
}
nativeView.setBackground(backgroundDrawable);
}

const backgroundColor = ((<any>backgroundDrawable).backgroundColor = background.color.android);
backgroundDrawable.mutate();
backgroundDrawable.setColorFilter(backgroundColor, android.graphics.PorterDuff.Mode.SRC_IN);
backgroundDrawable.invalidateSelf(); // Make sure the drawable is invalidated. Android forgets to invalidate it in some cases: toolbar
(<any>backgroundDrawable).backgroundColor = backgroundColor;
} else {
super._applyBackground(background, isBorderDrawable, onlyColor, backgroundDrawable);
}
}

@profile
public createNativeView() {
if (!AndroidButton) {
Expand Down
113 changes: 101 additions & 12 deletions packages/core/ui/core/view/index.android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -14,6 +14,7 @@ import { perspectiveProperty, visibilityProperty, opacityProperty, horizontalAli
import { CoreTypes } from '../../../core-types';

import { Background, ad as androidBackground } from '../../styling/background';
import { BackgroundClearFlags, refreshBorderDrawable } from '../../styling/background.android';
import { profile } from '../../../profiling';
import { topmost } from '../../frame/frame-stack';
import { Screen } from '../../../platform';
Expand All @@ -23,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 '../../styling/css-shadow';

export * from './view-common';
// helpers (these are okay re-exported here)
Expand All @@ -45,6 +47,10 @@ const modalMap = new Map<number, DialogOptions>();
let TouchListener: TouchListener;
let DialogFragment: DialogFragment;

interface AndroidView {
_cachedDrawable: android.graphics.drawable.Drawable.ConstantState | android.graphics.drawable.Drawable;
}

interface DialogOptions {
owner: View;
fullscreen: boolean;
Expand Down Expand Up @@ -1045,21 +1051,19 @@ export class View extends ViewCommon {

[backgroundInternalProperty.getDefault](): android.graphics.drawable.Drawable {
const nativeView = this.nativeViewProtected;
const drawable = nativeView.getBackground();
let drawable = nativeView.getBackground();
if (drawable) {
const constantState = drawable.getConstantState();
if (constantState) {
try {
return constantState.newDrawable(nativeView.getResources());
} catch (e) {
return drawable;
}
} else {
return drawable;
drawable = constantState.newDrawable(nativeView.getResources());
// eslint-disable-next-line no-empty
} catch {}
}
}
(<any>nativeView)._cachedDrawable = drawable;

return null;
return drawable;
}
[backgroundInternalProperty.setNative](value: android.graphics.drawable.Drawable | Background) {
this._redrawNativeBackground(value);
Expand All @@ -1081,9 +1085,96 @@ export class View extends ViewCommon {
}
}

public _applyBackground(background: Background, isBorderDrawable: boolean, onlyColor: boolean, backgroundDrawable: any) {
const nativeView = this.nativeViewProtected;
if (!isBorderDrawable && onlyColor) {
if (backgroundDrawable && backgroundDrawable.setColor) {
backgroundDrawable.setColor(background.color.android);
backgroundDrawable.invalidateSelf();
} else {
nativeView.setBackgroundColor(background.color.android);
}
} else if (!background.isEmpty()) {
if (!isBorderDrawable) {
backgroundDrawable = new org.nativescript.widgets.BorderDrawable(layout.getDisplayDensity(), this.toString());
refreshBorderDrawable(this, backgroundDrawable);
nativeView.setBackground(backgroundDrawable);
} else {
refreshBorderDrawable(this, backgroundDrawable);
}
} else {
//empty background let s reset
const cachedDrawable = (<any>nativeView)._cachedDrawable;
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 = <android.view.View & { _cachedDrawable: android.graphics.drawable.Drawable.ConstantState | android.graphics.drawable.Drawable }>this.nativeViewProtected;
if (!nativeView) {
return;
}

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 = (<any>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;

// 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);
const topPadding = Math.ceil(this.effectiveBorderTopWidth + this.effectivePaddingTop);
const rightPadding = Math.ceil(this.effectiveBorderRightWidth + this.effectivePaddingRight);
const bottomPadding = Math.ceil(this.effectiveBorderBottomWidth + this.effectivePaddingBottom);
if (this._isPaddingRelative) {
nativeView.setPaddingRelative(leftPadding, topPadding, rightPadding, bottomPadding);
} 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) {
androidBackground.onBackgroundOrBorderPropertyChanged(this);
this.onBackgroundOrBorderPropertyChanged();
} else {
const nativeView = this.nativeViewProtected;
nativeView.setBackground(value);
Expand All @@ -1099,8 +1190,6 @@ export class View extends ViewCommon {
} else {
nativeView.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
}

(<any>nativeView).background = undefined;
}
}

Expand Down
6 changes: 6 additions & 0 deletions packages/core/ui/core/view/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,12 @@ export abstract class View extends ViewBase {
* @private
*/
_redrawNativeBackground(value: any): void;
/**
* @private
* method called on Android to apply the background. This allows custom handling
*/
_applyBackground(background: Background, isBorderDrawable: boolean, onlyColor: boolean, backgroundDrawable: any);

/**
* @private
*/
Expand Down
3 changes: 3 additions & 0 deletions packages/core/ui/core/view/view-common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1067,6 +1067,9 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
public _redrawNativeBackground(value: any): void {
//
}
public _applyBackground(background, isBorderDrawable: boolean, onlyColor: boolean, backgroundDrawable: any) {
//
}

_onAttachedToWindow(): void {
//
Expand Down
Loading
0