Skip to content

Commit 96dd78f

Browse files
rigor789NathanWalker
authored andcommitted
feat: improved background handling (#9615)
1 parent a309ab2 commit 96dd78f

File tree

6 files changed

+71
-144
lines changed

6 files changed

+71
-144
lines changed

packages/core/ui/action-bar/index.android.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,18 @@ import { colorProperty } from '../styling/style-properties';
77
import { ImageSource } from '../../image-source';
88
import * as application from '../../application';
99
import { isAccessibilityServiceEnabled, updateContentDescription } from '../../accessibility';
10+
import type { Background } from '../styling/background';
11+
import { Device } from '../../platform';
12+
import lazy from '../../utils/lazy';
1013

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

1316
const R_ID_HOME = 0x0102002c;
1417
const ACTION_ITEM_ID_OFFSET = 10000;
1518
const DEFAULT_ELEVATION = 4;
1619

20+
const sdkVersion = lazy(() => parseInt(Device.sdkVersion));
21+
1722
let AppCompatTextView;
1823
let actionItemIdGenerator = ACTION_ITEM_ID_OFFSET;
1924
function generateItemId(): number {
@@ -59,7 +64,7 @@ function initializeMenuItemClickListener(): void {
5964
return;
6065
}
6166

62-
apiLevel = android.os.Build.VERSION.SDK_INT;
67+
apiLevel = sdkVersion();
6368

6469
AppCompatTextView = androidx.appcompat.widget.AppCompatTextView;
6570

@@ -216,6 +221,32 @@ export class ActionBar extends ActionBarBase {
216221
this._updateNavigationButton();
217222
}
218223

224+
public _applyBackground(background: Background, isBorderDrawable, onlyColor: boolean, backgroundDrawable: any) {
225+
const nativeView = this.nativeViewProtected;
226+
if (backgroundDrawable && onlyColor && sdkVersion() >= 21) {
227+
if (isBorderDrawable && (<any>nativeView)._cachedDrawable) {
228+
backgroundDrawable = (<any>nativeView)._cachedDrawable;
229+
// we need to duplicate the drawable or we lose the "default" cached drawable
230+
const constantState = backgroundDrawable.getConstantState();
231+
if (constantState) {
232+
try {
233+
backgroundDrawable = constantState.newDrawable(nativeView.getResources());
234+
// eslint-disable-next-line no-empty
235+
} catch {}
236+
}
237+
nativeView.setBackground(backgroundDrawable);
238+
}
239+
240+
const backgroundColor = ((<any>backgroundDrawable).backgroundColor = background.color.android);
241+
backgroundDrawable.mutate();
242+
backgroundDrawable.setColorFilter(backgroundColor, android.graphics.PorterDuff.Mode.SRC_IN);
243+
backgroundDrawable.invalidateSelf(); // Make sure the drawable is invalidated. Android forgets to invalidate it in some cases: toolbar
244+
(<any>backgroundDrawable).backgroundColor = backgroundColor;
245+
} else {
246+
super._applyBackground(background, isBorderDrawable, onlyColor, backgroundDrawable);
247+
}
248+
}
249+
219250
public _onAndroidItemSelected(itemId: number): boolean {
220251
// Handle home button
221252
if (this.navigationButton && itemId === R_ID_HOME) {

packages/core/ui/button/index.android.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { profile } from '../../profiling';
77
import { TouchGestureEventData, GestureTypes, TouchAction } from '../gestures';
88
import { Device } from '../../platform';
99
import lazy from '../../utils/lazy';
10+
import type { Background } from 'ui/styling/background';
1011

1112
export * from './button-common';
1213

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

62+
public _applyBackground(background: Background, isBorderDrawable, onlyColor: boolean, backgroundDrawable: any) {
63+
const nativeView = this.nativeViewProtected;
64+
if (backgroundDrawable && onlyColor) {
65+
if (isBorderDrawable && (<any>nativeView)._cachedDrawable) {
66+
backgroundDrawable = (<any>nativeView)._cachedDrawable;
67+
// we need to duplicate the drawable or we lose the "default" cached drawable
68+
const constantState = backgroundDrawable.getConstantState();
69+
if (constantState) {
70+
try {
71+
backgroundDrawable = constantState.newDrawable(nativeView.getResources());
72+
// eslint-disable-next-line no-empty
73+
} catch {}
74+
}
75+
nativeView.setBackground(backgroundDrawable);
76+
}
77+
78+
const backgroundColor = ((<any>backgroundDrawable).backgroundColor = background.color.android);
79+
backgroundDrawable.mutate();
80+
backgroundDrawable.setColorFilter(backgroundColor, android.graphics.PorterDuff.Mode.SRC_IN);
81+
backgroundDrawable.invalidateSelf(); // Make sure the drawable is invalidated. Android forgets to invalidate it in some cases: toolbar
82+
(<any>backgroundDrawable).backgroundColor = backgroundColor;
83+
} else {
84+
super._applyBackground(background, isBorderDrawable, onlyColor, backgroundDrawable);
85+
}
86+
}
87+
6188
@profile
6289
public createNativeView() {
6390
if (!AndroidButton) {

packages/core/ui/core/view/index.android.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1210,11 +1210,13 @@ export class View extends ViewCommon {
12101210
const topPadding = Math.ceil(this.effectiveBorderTopWidth + this.effectivePaddingTop);
12111211
const rightPadding = Math.ceil(this.effectiveBorderRightWidth + this.effectivePaddingRight);
12121212
const bottomPadding = Math.ceil(this.effectiveBorderBottomWidth + this.effectivePaddingBottom);
1213+
12131214
if (this._isPaddingRelative) {
12141215
nativeView.setPaddingRelative(leftPadding, topPadding, rightPadding, bottomPadding);
12151216
} else {
12161217
nativeView.setPadding(leftPadding, topPadding, rightPadding, bottomPadding);
12171218
}
1219+
12181220
// reset clear flags
12191221
background.clearFlags = BackgroundClearFlags.NONE;
12201222
}

packages/core/ui/core/view/index.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,12 @@ export abstract class View extends ViewCommon {
830830
* @private
831831
*/
832832
_redrawNativeBackground(value: any): void;
833+
/**
834+
* @private
835+
* method called on Android to apply the background. This allows custom handling
836+
*/
837+
_applyBackground(background: Background, isBorderDrawable: boolean, onlyColor: boolean, backgroundDrawable: any);
838+
833839
/**
834840
* @private
835841
*/

packages/core/ui/core/view/view-common.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,6 +1082,9 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
10821082
public _redrawNativeBackground(value: any): void {
10831083
//
10841084
}
1085+
public _applyBackground(background, isBorderDrawable: boolean, onlyColor: boolean, backgroundDrawable: any) {
1086+
//
1087+
}
10851088

10861089
_onAttachedToWindow(): void {
10871090
//

packages/core/ui/styling/background.android.ts

Lines changed: 1 addition & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -1,141 +1,11 @@
11
import { View } from '../core/view';
22
import { LinearGradient } from './linear-gradient';
3-
import { CoreTypes } from '../../core-types';
4-
import { isDataURI, isFileOrResourcePath, layout, RESOURCE_PREFIX, FILE_PREFIX } from '../../utils';
3+
import { isDataURI, isFileOrResourcePath, RESOURCE_PREFIX, FILE_PREFIX } from '../../utils';
54
import { parse } from '../../css-value';
65
import { path, knownFolders } from '../../file-system';
76
import * as application from '../../application';
8-
import { profile } from '../../profiling';
9-
import { CSSShadow } from './css-shadow';
10-
import { Length } from './style-properties';
11-
import { BackgroundClearFlags } from './background-common';
127
export * from './background-common';
138

14-
interface AndroidView {
15-
_cachedDrawable: android.graphics.drawable.Drawable.ConstantState | android.graphics.drawable.Drawable;
16-
}
17-
18-
// TODO: Change this implementation to use
19-
// We are using "ad" here to avoid namespace collision with the global android object
20-
export namespace ad {
21-
let SDK: number;
22-
function getSDK() {
23-
if (!SDK) {
24-
SDK = android.os.Build.VERSION.SDK_INT;
25-
}
26-
27-
return SDK;
28-
}
29-
30-
function isSetColorFilterOnlyWidget(nativeView: android.view.View): boolean {
31-
// prettier-ignore
32-
return (
33-
nativeView instanceof android.widget.Button
34-
|| (nativeView instanceof androidx.appcompat.widget.Toolbar && getSDK() >= 21)
35-
// There is an issue with the DrawableContainer which was fixed
36-
// for API version 21 and above: https://code.google.com/p/android/issues/detail?id=60183
37-
);
38-
}
39-
40-
export function onBackgroundOrBorderPropertyChanged(view: View) {
41-
const nativeView = <android.view.View>view.nativeViewProtected;
42-
if (!nativeView) {
43-
return;
44-
}
45-
46-
const background = view.style.backgroundInternal;
47-
48-
if (background.clearFlags & BackgroundClearFlags.CLEAR_BOX_SHADOW || background.clearFlags & BackgroundClearFlags.CLEAR_BACKGROUND_COLOR) {
49-
// clear background if we're clearing the box shadow
50-
// or the background has been removed
51-
nativeView.setBackground(null);
52-
}
53-
54-
let drawable = nativeView.getBackground();
55-
const androidView = (<any>view) as AndroidView;
56-
// use undefined as not set. getBackground will never return undefined only Drawable or null;
57-
if (androidView._cachedDrawable === undefined && drawable) {
58-
const constantState = drawable.getConstantState();
59-
androidView._cachedDrawable = constantState || drawable;
60-
}
61-
const isBorderDrawable = drawable instanceof org.nativescript.widgets.BorderDrawable;
62-
63-
// prettier-ignore
64-
const onlyColor = !background.hasBorderWidth()
65-
&& !background.hasBorderRadius()
66-
&& !background.hasBoxShadow()
67-
&& !background.clipPath
68-
&& !background.image
69-
&& !!background.color;
70-
71-
if (!isBorderDrawable && drawable instanceof android.graphics.drawable.ColorDrawable && onlyColor) {
72-
drawable.setColor(background.color.android);
73-
drawable.invalidateSelf();
74-
} else if (isSetColorFilterOnlyWidget(nativeView) && drawable && onlyColor) {
75-
if (isBorderDrawable && androidView._cachedDrawable) {
76-
if (!(androidView._cachedDrawable instanceof android.graphics.drawable.Drawable.ConstantState)) {
77-
return;
78-
}
79-
80-
drawable = androidView._cachedDrawable.newDrawable(nativeView.getResources());
81-
nativeView.setBackground(drawable);
82-
}
83-
84-
const backgroundColor = ((<any>drawable).backgroundColor = background.color.android);
85-
drawable.mutate();
86-
drawable.setColorFilter(backgroundColor, android.graphics.PorterDuff.Mode.SRC_IN);
87-
drawable.invalidateSelf(); // Make sure the drawable is invalidated. Android forgets to invalidate it in some cases: toolbar
88-
(<any>drawable).backgroundColor = backgroundColor;
89-
} else if (!isBorderDrawable && onlyColor) {
90-
// this is the fastest way to change only background color
91-
nativeView.setBackgroundColor(background.color.android);
92-
} else if (!background.isEmpty()) {
93-
let backgroundDrawable = drawable;
94-
95-
if (drawable instanceof org.nativescript.widgets.BoxShadowDrawable) {
96-
// if we have BoxShadow's we have to get the underlying drawable
97-
backgroundDrawable = drawable.getWrappedDrawable();
98-
}
99-
100-
if (backgroundDrawable instanceof org.nativescript.widgets.BorderDrawable) {
101-
refreshBorderDrawable(view, backgroundDrawable);
102-
} else {
103-
backgroundDrawable = new org.nativescript.widgets.BorderDrawable(layout.getDisplayDensity(), view.toString());
104-
refreshBorderDrawable(view, <org.nativescript.widgets.BorderDrawable>backgroundDrawable);
105-
nativeView.setBackground(backgroundDrawable);
106-
}
107-
} else {
108-
const cachedDrawable = androidView._cachedDrawable;
109-
let defaultDrawable: android.graphics.drawable.Drawable = null;
110-
if (cachedDrawable) {
111-
if (cachedDrawable instanceof android.graphics.drawable.Drawable.ConstantState) {
112-
defaultDrawable = cachedDrawable.newDrawable(nativeView.getResources());
113-
} else if (cachedDrawable instanceof android.graphics.drawable.Drawable) {
114-
defaultDrawable = cachedDrawable;
115-
}
116-
}
117-
118-
nativeView.setBackground(defaultDrawable);
119-
}
120-
121-
if (background.hasBoxShadow()) {
122-
drawBoxShadow(nativeView, view, background.getBoxShadow());
123-
}
124-
125-
// TODO: Can we move BorderWidths as separate native setter?
126-
// This way we could skip setPadding if borderWidth is not changed.
127-
const leftPadding = Math.ceil(view.effectiveBorderLeftWidth + view.effectivePaddingLeft);
128-
const topPadding = Math.ceil(view.effectiveBorderTopWidth + view.effectivePaddingTop);
129-
const rightPadding = Math.ceil(view.effectiveBorderRightWidth + view.effectivePaddingRight);
130-
const bottomPadding = Math.ceil(view.effectiveBorderBottomWidth + view.effectivePaddingBottom);
131-
132-
nativeView.setPadding(leftPadding, topPadding, rightPadding, bottomPadding);
133-
134-
// reset clear flags
135-
background.clearFlags = BackgroundClearFlags.NONE;
136-
}
137-
}
138-
1399
function fromBase64(source: string): android.graphics.Bitmap {
14010
const bytes = android.util.Base64.decode(source, android.util.Base64.DEFAULT);
14111

@@ -253,18 +123,6 @@ function createNativeCSSValueArray(css: string): androidNative.Array<org.natives
253123
return nativeArray;
254124
}
255125

256-
function drawBoxShadow(nativeView: android.view.View, view: View, boxShadow: CSSShadow) {
257-
const config = {
258-
shadowColor: boxShadow.color.android,
259-
cornerRadius: Length.toDevicePixels(view.borderRadius as CoreTypes.LengthType, 0.0),
260-
spreadRadius: Length.toDevicePixels(boxShadow.spreadRadius, 0.0),
261-
blurRadius: Length.toDevicePixels(boxShadow.blurRadius, 0.0),
262-
offsetX: Length.toDevicePixels(boxShadow.offsetX, 0.0),
263-
offsetY: Length.toDevicePixels(boxShadow.offsetY, 0.0),
264-
};
265-
org.nativescript.widgets.Utils.drawBoxShadow(nativeView, JSON.stringify(config));
266-
}
267-
268126
export enum CacheMode {
269127
none,
270128
memory,

0 commit comments

Comments
 (0)