feat(view): introduce LayoutChanged event on every View component (#5825)

* feat(view): introduce LayoutChanged event

* test(view): add LayoutChanged event tests

* chore(view-android): attach to onLayoutChange only if listener attached

* feat(view-android): override on/off in order to attach and detach from OnLayoutChangeListener
This commit is contained in:
Alexander Djenkov
2018-05-21 17:22:40 +03:00
committed by GitHub
parent f671f778f3
commit 0fc1547a19
8 changed files with 305 additions and 18 deletions

View File

@@ -61,6 +61,7 @@ export function PseudoClassHandler(...pseudoClasses: string[]): MethodDecorator
export const _rootModalViews = new Array<ViewBase>();
export abstract class ViewCommon extends ViewBase implements ViewDefinition {
public static layoutChangedEvent = "layoutChanged";
public static shownModallyEvent = "shownModally";
public static showingModallyEvent = "showingModally";
@@ -277,6 +278,14 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
//
}
protected _raiseLayoutChangedEvent() {
const args: EventData = {
eventName: ViewCommon.layoutChangedEvent,
object: this
};
this.notify(args);
}
protected _raiseShownModallyEvent() {
const args: ShownModallyData = {
eventName: ViewCommon.shownModallyEvent,

View File

@@ -7,7 +7,7 @@ import {
ViewCommon, layout, isEnabledProperty, originXProperty, originYProperty, automationTextProperty, isUserInteractionEnabledProperty,
traceEnabled, traceWrite, traceCategories, traceNotifyEvent,
paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty,
Color
Color, EventData
} from "./view-common";
import {
@@ -229,6 +229,8 @@ export class View extends ViewCommon {
private _isClickable: boolean;
private touchListenerIsSet: boolean;
private touchListener: android.view.View.OnTouchListener;
private layoutChangeListenerIsSet: boolean;
private layoutChangeListener: android.view.View.OnLayoutChangeListener;
private _manager: android.app.FragmentManager;
nativeViewProtected: android.view.View;
@@ -241,6 +243,26 @@ export class View extends ViewCommon {
}
}
on(eventNames: string, callback: (data: EventData) => void, thisArg?: any) {
super.on(eventNames, callback, thisArg);
const isLayoutEvent = typeof eventNames === "string" ? eventNames.indexOf(ViewCommon.layoutChangedEvent) !== -1 : false;
if (this.isLoaded && !this.layoutChangeListenerIsSet && isLayoutEvent) {
this.setOnLayoutChangeListener();
}
}
off(eventNames: string, callback?: any, thisArg?: any) {
super.off(eventNames, callback, thisArg);
const isLayoutEvent = typeof eventNames === "string" ? eventNames.indexOf(ViewCommon.layoutChangedEvent) !== -1 : false;
// Remove native listener only if there are no more user listeners for LayoutChanged event
if (this.isLoaded && this.layoutChangeListenerIsSet && isLayoutEvent && !this.hasListeners(ViewCommon.layoutChangedEvent)) {
this.nativeViewProtected.removeOnLayoutChangeListener(this.layoutChangeListener);
this.layoutChangeListenerIsSet = false;
}
}
public _getFragmentManager(): android.app.FragmentManager {
let manager = this._manager;
if (!manager) {
@@ -305,6 +327,19 @@ export class View extends ViewCommon {
public initNativeView(): void {
super.initNativeView();
this._isClickable = this.nativeViewProtected.isClickable();
if (this.hasListeners(ViewCommon.layoutChangedEvent)) {
this.setOnLayoutChangeListener();
}
}
public disposeNativeView(): void {
super.disposeNativeView();
if (this.layoutChangeListenerIsSet) {
this.layoutChangeListenerIsSet = false;
this.nativeViewProtected.removeOnLayoutChangeListener(this.layoutChangeListener);
}
}
private setOnTouchListener() {
@@ -320,6 +355,25 @@ export class View extends ViewCommon {
}
}
private setOnLayoutChangeListener() {
if (this.nativeViewProtected) {
const owner = this;
this.layoutChangeListenerIsSet = true;
this.layoutChangeListener = this.layoutChangeListener || new android.view.View.OnLayoutChangeListener({
onLayoutChange(
v: android.view.View,
left: number, top: number, right: number, bottom: number,
oldLeft: number, oldTop: number, oldRight: number, oldBottom: number): void {
if (left !== oldLeft || top !== oldTop || right !== oldRight || bottom !== oldBottom) {
owner._raiseLayoutChangedEvent();
}
}
});
this.nativeViewProtected.addOnLayoutChangeListener(this.layoutChangeListener);
}
}
get isLayoutRequired(): boolean {
return !this.isLayoutValid;
}

View File

@@ -103,6 +103,10 @@ export interface ShownModallyData extends EventData {
* A View occupies a rectangular area on the screen and is responsible for drawing and layouting of all UI components within.
*/
export abstract class View extends ViewBase {
/**
* String value used when hooking to layoutChanged event.
*/
public static layoutChangedEvent: string;
/**
* String value used when hooking to showingModally event.
*/

View File

@@ -28,6 +28,7 @@ export class View extends ViewCommon {
nativeViewProtected: UIView;
viewController: UIViewController;
private _isLaidOut = false;
private _hasTransfrom = false;
private _privateFlags: number = PFLAG_LAYOUT_REQUIRED | PFLAG_FORCE_LAYOUT;
private _cachedFrame: CGRect;
@@ -160,6 +161,11 @@ export class View extends ViewCommon {
const boundsOrigin = nativeView.bounds.origin;
nativeView.bounds = CGRectMake(boundsOrigin.x, boundsOrigin.y, frame.size.width, frame.size.height);
this._raiseLayoutChangedEvent();
this._isLaidOut = true;
} else if (!this._isLaidOut) {
// Rects could be equal on the first layout and an event should be raised.
this._raiseLayoutChangedEvent();
}
}