mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-18 22:01:42 +08:00
fix: refactor background handling.
The idea is for views to handle sepecial case themselves. I realised that android.widget.Button was materialized even if you were not using it because there was a test for instanceof in the background handling. Now the special background handling is done in Button and ActionBar
This commit is contained in:
@ -6,6 +6,9 @@ import { layout, RESOURCE_PREFIX, isFontIconURI } from '../../utils';
|
|||||||
import { colorProperty } from '../styling/style-properties';
|
import { colorProperty } from '../styling/style-properties';
|
||||||
import { ImageSource } from '../../image-source';
|
import { ImageSource } from '../../image-source';
|
||||||
import * as application from '../../application';
|
import * as application from '../../application';
|
||||||
|
import type { Background } from 'ui/styling/background';
|
||||||
|
import { Device } from '../../platform';
|
||||||
|
import lazy from '../../utils/lazy';
|
||||||
|
|
||||||
export * from './action-bar-common';
|
export * from './action-bar-common';
|
||||||
|
|
||||||
@ -13,6 +16,9 @@ const R_ID_HOME = 0x0102002c;
|
|||||||
const ACTION_ITEM_ID_OFFSET = 10000;
|
const ACTION_ITEM_ID_OFFSET = 10000;
|
||||||
const DEFAULT_ELEVATION = 4;
|
const DEFAULT_ELEVATION = 4;
|
||||||
|
|
||||||
|
const sdkVersion = lazy(() => parseInt(Device.sdkVersion));
|
||||||
|
|
||||||
|
|
||||||
let AppCompatTextView;
|
let AppCompatTextView;
|
||||||
let actionItemIdGenerator = ACTION_ITEM_ID_OFFSET;
|
let actionItemIdGenerator = ACTION_ITEM_ID_OFFSET;
|
||||||
function generateItemId(): number {
|
function generateItemId(): number {
|
||||||
@ -58,7 +64,7 @@ function initializeMenuItemClickListener(): void {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
apiLevel = android.os.Build.VERSION.SDK_INT;
|
apiLevel = sdkVersion();
|
||||||
|
|
||||||
AppCompatTextView = androidx.appcompat.widget.AppCompatTextView;
|
AppCompatTextView = androidx.appcompat.widget.AppCompatTextView;
|
||||||
|
|
||||||
@ -215,6 +221,24 @@ export class ActionBar extends ActionBarBase {
|
|||||||
this._updateNavigationButton();
|
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.newDrawable(nativeView.getResources());
|
||||||
|
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 {
|
public _onAndroidItemSelected(itemId: number): boolean {
|
||||||
// Handle home button
|
// Handle home button
|
||||||
if (this.navigationButton && itemId === R_ID_HOME) {
|
if (this.navigationButton && itemId === R_ID_HOME) {
|
||||||
|
@ -7,6 +7,7 @@ import { profile } from '../../profiling';
|
|||||||
import { TouchGestureEventData, GestureTypes, TouchAction } from '../gestures';
|
import { TouchGestureEventData, GestureTypes, TouchAction } from '../gestures';
|
||||||
import { Device } from '../../platform';
|
import { Device } from '../../platform';
|
||||||
import lazy from '../../utils/lazy';
|
import lazy from '../../utils/lazy';
|
||||||
|
import type { Background } from 'ui/styling/background';
|
||||||
|
|
||||||
export * from './button-common';
|
export * from './button-common';
|
||||||
|
|
||||||
@ -58,6 +59,26 @@ export class Button extends ButtonBase {
|
|||||||
private _stateListAnimator: any;
|
private _stateListAnimator: any;
|
||||||
private _highlightedHandler: (args: TouchGestureEventData) => void;
|
private _highlightedHandler: (args: TouchGestureEventData) => void;
|
||||||
|
|
||||||
|
|
||||||
|
public _applyBackground(background: Background, isBorderDrawable, onlyColor: boolean, backgroundDrawable: any) {
|
||||||
|
const nativeView = this.nativeViewProtected;
|
||||||
|
console.log('_applyBackground', nativeView, backgroundDrawable, onlyColor, isBorderDrawable);
|
||||||
|
if (backgroundDrawable && onlyColor) {
|
||||||
|
if (isBorderDrawable && (<any>nativeView)._cachedDrawable) {
|
||||||
|
backgroundDrawable = (<any>nativeView)._cachedDrawable.newDrawable(nativeView.getResources());
|
||||||
|
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
|
@profile
|
||||||
public createNativeView() {
|
public createNativeView() {
|
||||||
if (!AndroidButton) {
|
if (!AndroidButton) {
|
||||||
|
@ -42,6 +42,7 @@ import {
|
|||||||
} from '../../styling/style-properties';
|
} from '../../styling/style-properties';
|
||||||
|
|
||||||
import { Background, ad as androidBackground } from '../../styling/background';
|
import { Background, ad as androidBackground } from '../../styling/background';
|
||||||
|
import { refreshBorderDrawable } from '../../styling/background.android';
|
||||||
import { profile } from '../../../profiling';
|
import { profile } from '../../../profiling';
|
||||||
import { topmost } from '../../frame/frame-stack';
|
import { topmost } from '../../frame/frame-stack';
|
||||||
import { Screen } from '../../../platform';
|
import { Screen } from '../../../platform';
|
||||||
@ -989,21 +990,19 @@ export class View extends ViewCommon {
|
|||||||
|
|
||||||
[backgroundInternalProperty.getDefault](): android.graphics.drawable.Drawable {
|
[backgroundInternalProperty.getDefault](): android.graphics.drawable.Drawable {
|
||||||
const nativeView = this.nativeViewProtected;
|
const nativeView = this.nativeViewProtected;
|
||||||
const drawable = nativeView.getBackground();
|
let drawable = nativeView.getBackground();
|
||||||
if (drawable) {
|
if (drawable) {
|
||||||
const constantState = drawable.getConstantState();
|
const constantState = drawable.getConstantState();
|
||||||
if (constantState) {
|
if (constantState) {
|
||||||
try {
|
try {
|
||||||
return constantState.newDrawable(nativeView.getResources());
|
drawable = constantState.newDrawable(nativeView.getResources());
|
||||||
} catch (e) {
|
// eslint-disable-next-line no-empty
|
||||||
return drawable;
|
} catch {}
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return drawable;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
(<any>nativeView)._cachedDrawable = drawable;
|
||||||
|
|
||||||
return null;
|
return drawable;
|
||||||
}
|
}
|
||||||
[backgroundInternalProperty.setNative](value: android.graphics.drawable.Drawable | Background) {
|
[backgroundInternalProperty.setNative](value: android.graphics.drawable.Drawable | Background) {
|
||||||
this._redrawNativeBackground(value);
|
this._redrawNativeBackground(value);
|
||||||
@ -1025,9 +1024,56 @@ 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 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;
|
||||||
|
const drawable = nativeView.getBackground();
|
||||||
|
const isBorderDrawable = drawable instanceof org.nativescript.widgets.BorderDrawable;
|
||||||
|
const onlyColor = !background.hasBorderWidth() && !background.hasBorderRadius() && !background.clipPath && !background.image && !!background.color;
|
||||||
|
this._applyBackground(background, isBorderDrawable, onlyColor, drawable);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
_redrawNativeBackground(value: android.graphics.drawable.Drawable | Background): void {
|
_redrawNativeBackground(value: android.graphics.drawable.Drawable | Background): void {
|
||||||
if (value instanceof Background) {
|
if (value instanceof Background) {
|
||||||
androidBackground.onBackgroundOrBorderPropertyChanged(this);
|
this.onBackgroundOrBorderPropertyChanged();
|
||||||
} else {
|
} else {
|
||||||
const nativeView = this.nativeViewProtected;
|
const nativeView = this.nativeViewProtected;
|
||||||
nativeView.setBackground(value);
|
nativeView.setBackground(value);
|
||||||
@ -1043,8 +1089,6 @@ export class View extends ViewCommon {
|
|||||||
} else {
|
} else {
|
||||||
nativeView.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
|
nativeView.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
(<any>nativeView).background = undefined;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
6
packages/core/ui/core/view/index.d.ts
vendored
6
packages/core/ui/core/view/index.d.ts
vendored
@ -742,6 +742,12 @@ export abstract class View extends ViewBase {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_redrawNativeBackground(value: any): void;
|
_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
|
* @private
|
||||||
*/
|
*/
|
||||||
|
@ -985,6 +985,9 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
|
|||||||
public _redrawNativeBackground(value: any): void {
|
public _redrawNativeBackground(value: any): void {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
public _applyBackground(background, isBorderDrawable: boolean, onlyColor: boolean, backgroundDrawable: any) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
_onAttachedToWindow(): void {
|
_onAttachedToWindow(): void {
|
||||||
//
|
//
|
||||||
|
@ -15,90 +15,7 @@ interface AndroidView {
|
|||||||
// TODO: Change this implementation to use
|
// TODO: Change this implementation to use
|
||||||
// We are using "ad" here to avoid namespace collision with the global android object
|
// We are using "ad" here to avoid namespace collision with the global android object
|
||||||
export namespace ad {
|
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 {
|
|
||||||
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 = <android.view.View>view.nativeViewProtected;
|
|
||||||
if (!nativeView) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const background = view.style.backgroundInternal;
|
|
||||||
let drawable = nativeView.getBackground();
|
|
||||||
const androidView = (<any>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;
|
|
||||||
const onlyColor = !background.hasBorderWidth() && !background.hasBorderRadius() && !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 = ((<any>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
|
|
||||||
(<any>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 as org.nativescript.widgets.BorderDrawable;
|
|
||||||
if (!isBorderDrawable) {
|
|
||||||
backgroundDrawable = new org.nativescript.widgets.BorderDrawable(layout.getDisplayDensity(), view.toString());
|
|
||||||
refreshBorderDrawable(view, backgroundDrawable);
|
|
||||||
nativeView.setBackground(backgroundDrawable);
|
|
||||||
} else {
|
|
||||||
refreshBorderDrawable(view, 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function fromBase64(source: string): android.graphics.Bitmap {
|
function fromBase64(source: string): android.graphics.Bitmap {
|
||||||
@ -129,7 +46,7 @@ function fromGradient(gradient: LinearGradient): org.nativescript.widgets.Linear
|
|||||||
}
|
}
|
||||||
|
|
||||||
const pattern = /url\(('|")(.*?)\1\)/;
|
const pattern = /url\(('|")(.*?)\1\)/;
|
||||||
function refreshBorderDrawable(this: void, view: View, borderDrawable: org.nativescript.widgets.BorderDrawable) {
|
export function refreshBorderDrawable(this: void, view: View, borderDrawable: org.nativescript.widgets.BorderDrawable) {
|
||||||
const nativeView = <android.view.View>view.nativeViewProtected;
|
const nativeView = <android.view.View>view.nativeViewProtected;
|
||||||
const context = nativeView.getContext();
|
const context = nativeView.getContext();
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user