Avoid excessive requestLayout calls (#2270)

Added View.isLayoutRequired - to check if Layout call is needed after a measure call. Use in list-view.
Measure pass now get all its properties from nativeLayoutParams property instead of using instance dependency properties (e.g. perf improvement).
List-view now layouts cells only if there is need to.
List-view now measure rows with the specified rowHeight.
This commit is contained in:
Hristo Hristov
2016-06-08 15:41:30 +03:00
parent ec85cf3860
commit 22608011f2
6 changed files with 33 additions and 34 deletions

View File

@ -15,18 +15,10 @@ import {PropertyMetadataSettings, PropertyChangeData, Property, ValueSource, Pro
import {registerSpecialProperty} from "ui/builder/special-properties";
import {CommonLayoutParams, nativeLayoutParamsProperty} from "ui/styling/style";
import * as visualStateConstants from "ui/styling/visual-state-constants";
import * as bindableModule from "ui/core/bindable";
import * as visualStateModule from "../styling/visual-state";
import * as animModule from "ui/animation";
import { Source } from "utils/debug";
var bindable: typeof bindableModule;
function ensureBindable() {
if (!bindable) {
bindable = require("ui/core/bindable");
}
}
var visualState: typeof visualStateModule;
function ensureVisualState() {
if (!visualState) {
@ -175,21 +167,18 @@ export class View extends ProxyObject implements definition.View {
private _isLoaded: boolean;
private _isLayoutValid: boolean = false;
private _updatingInheritedProperties: boolean;
private _registeredAnimations: Array<keyframeAnimationModule.KeyframeAnimation>;
public _domId: number;
public _isAddedToNativeVisualTree = false;
public _cssClasses: Array<string> = [];
public _gestureObservers = {};
public getGestureObservers(type: gestures.GestureTypes): Array<gestures.GesturesObserver> {
return this._gestureObservers[type];
}
private _updatingInheritedProperties: boolean;
constructor() {
super({});
@ -208,7 +197,6 @@ export class View extends ProxyObject implements definition.View {
public addEventListener(arg: string | gestures.GestureTypes, callback: (data: observable.EventData) => void, thisArg?: any) {
if (types.isString(arg)) {
arg = getEventOrGestureName(<string>arg);
var gesture = gestures.fromString(<string>arg);
@ -521,6 +509,10 @@ export class View extends ProxyObject implements definition.View {
throw new Error("View.style property is read-only.");
}
get isLayoutRequired(): boolean {
return true;
}
get isLayoutValid(): boolean {
return this._isLayoutValid;
}
@ -687,7 +679,6 @@ export class View extends ProxyObject implements definition.View {
}
public static layoutChild(parent: View, child: View, left: number, top: number, right: number, bottom: number): void {
if (!child || !child._isVisible) {
return;
}
@ -702,11 +693,11 @@ export class View extends ProxyObject implements definition.View {
var childHeight = child.getMeasuredHeight();
var vAlignment: string;
if (lp.height >= 0 && child.verticalAlignment === enums.VerticalAlignment.stretch) {
if (lp.height >= 0 && lp.verticalAlignment === enums.VerticalAlignment.stretch) {
vAlignment = enums.VerticalAlignment.center;
}
else {
vAlignment = child.verticalAlignment;
vAlignment = lp.verticalAlignment;
}
let marginTop = lp.topMargin;
@ -736,11 +727,11 @@ export class View extends ProxyObject implements definition.View {
}
var hAlignment: string;
if (lp.width >= 0 && child.horizontalAlignment === enums.HorizontalAlignment.stretch) {
if (lp.width >= 0 && lp.horizontalAlignment === enums.HorizontalAlignment.stretch) {
hAlignment = enums.HorizontalAlignment.center;
}
else {
hAlignment = child.horizontalAlignment;
hAlignment = lp.horizontalAlignment;
}
switch (hAlignment) {
@ -771,6 +762,7 @@ export class View extends ProxyObject implements definition.View {
if (trace.enabled) {
trace.write(child.parent + " :layoutChild: " + child + " " + childLeft + ", " + childTop + ", " + childRight + ", " + childBottom, trace.categories.Layout);
}
child.layout(childLeft, childTop, childRight, childBottom);
}
@ -779,7 +771,6 @@ export class View extends ProxyObject implements definition.View {
var measureHeight = 0;
if (child && child._isVisible) {
var width = utils.layout.getMeasureSpecSize(widthMeasureSpec);
var widthMode = utils.layout.getMeasureSpecMode(widthMeasureSpec);
@ -840,7 +831,7 @@ export class View extends ProxyObject implements definition.View {
// Parent has imposed an exact size on us
case utils.layout.EXACTLY:
resultSize = measureLength;
var stretched = horizontal ? view.horizontalAlignment === enums.HorizontalAlignment.stretch : view.verticalAlignment === enums.VerticalAlignment.stretch;
var stretched = horizontal ? lp.horizontalAlignment === enums.HorizontalAlignment.stretch : lp.verticalAlignment === enums.VerticalAlignment.stretch;
// if stretched - nativeView wants to be our size. So be it.
// else - nativeView wants to determine its own size. It can't be bigger than us.
@ -1044,9 +1035,7 @@ export class View extends ProxyObject implements definition.View {
view.onUnloaded();
}
ensureBindable();
view._setValue(bindable.Bindable.bindingContextProperty, undefined, ValueSource.Inherited);
view._setValue(ProxyObject.bindingContextProperty, undefined, ValueSource.Inherited);
view._eachSetProperty((property) => {
if (!(property instanceof styling.Property) && property.inheritable) {
view._resetValue(property, ValueSource.Inherited);

View File

@ -280,6 +280,10 @@ export class View extends viewCommon.View {
return this.android;
}
get isLayoutRequired(): boolean {
return !this.isLayoutValid;
}
get isLayoutValid(): boolean {
if (this._nativeView) {
return !this._nativeView.isLayoutRequested();

View File

@ -484,8 +484,8 @@ declare module "ui/core/view" {
isLoaded: boolean;
_addView(view: View, atIndex?: number);
_propagateInheritableProperties(view: View)
_inheritProperties(parentView: View)
_propagateInheritableProperties(view: View);
_inheritProperties(parentView: View);
_removeView(view: View);
_context: any /* android.content.Context */;
@ -499,8 +499,8 @@ declare module "ui/core/view" {
public _applyXmlAttribute(attribute: string, value: any): boolean;
// TODO: Implement logic for stripping these lines out
//@private
isLayoutRequired: boolean;
_parentChanged(oldParent: View): void;
_gestureObservers: any;
_isInheritedChange(): boolean;
@ -610,5 +610,4 @@ declare module "ui/core/view" {
*/
_applyXmlAttribute(attributeName: string, attrValue: any): boolean;
}
}

View File

@ -94,6 +94,10 @@ export class View extends viewCommon.View {
return this.ios;
}
get isLayoutRequired(): boolean {
return (this._privateFlags & PFLAG_LAYOUT_REQUIRED) === PFLAG_LAYOUT_REQUIRED;
}
get isLayoutRequested(): boolean {
return (this._privateFlags & PFLAG_FORCE_LAYOUT) === PFLAG_FORCE_LAYOUT;
}
@ -109,7 +113,6 @@ export class View extends viewCommon.View {
}
public measure(widthMeasureSpec: number, heightMeasureSpec: number): void {
var measureSpecsChanged = this._setCurrentMeasureSpecs(widthMeasureSpec, heightMeasureSpec);
var forceLayout = (this._privateFlags & PFLAG_FORCE_LAYOUT) === PFLAG_FORCE_LAYOUT;
if (forceLayout || measureSpecsChanged) {
@ -136,9 +139,11 @@ export class View extends viewCommon.View {
this.onLayout(left, top, right, bottom);
this._privateFlags &= ~PFLAG_LAYOUT_REQUIRED;
}
if (sizeChanged) {
this._onSizeChanged();
}
this._privateFlags &= ~PFLAG_FORCE_LAYOUT;
}

View File

@ -174,7 +174,8 @@ export class ListView extends view.View implements definition.ListView {
}
private _getDataItem(index: number): any {
return this.items.getItem ? this.items.getItem(index) : this.items[index];
let thisItems = this.items;
return thisItems.getItem ? thisItems.getItem(index) : thisItems[index];
}
public _getDefaultItemContent(index: number): view.View {

View File

@ -74,7 +74,7 @@ class DataSource extends NSObject implements UITableViewDataSource {
owner._prepareCell(cell, indexPath);
let cellView: view.View = cell.view;
if (cellView) {
if (cellView && cellView.isLayoutRequired) {
// Arrange cell views. We do it here instead of _layoutCell because _layoutCell is called
// from 'tableViewHeightForRowAtIndexPath' method too (in iOS 7.1) and we don't want to arrange the fake cell.
let width = utils.layout.getMeasureSpecSize(owner.widthMeasureSpec);
@ -180,7 +180,7 @@ class UITableViewRowHeightDelegateImpl extends NSObject implements UITableViewDe
return DEFAULT_HEIGHT;
}
return owner.rowHeight;
return owner._rowHeight;
}
}
@ -208,11 +208,11 @@ export class ListView extends common.ListView {
private _preparingCell: boolean = false;
private _isDataDirty: boolean = false;
private _map: Map<ListViewCell, view.View>;
public _rowHeight: number = -1;
widthMeasureSpec: number = 0;
constructor() {
super();
this._ios = new UITableView();
this._ios.registerClassForCellReuseIdentifier(ListViewCell.class(), CELLIDENTIFIER);
this._ios.autoresizingMask = UIViewAutoresizing.UIViewAutoresizingNone;
@ -284,6 +284,7 @@ export class ListView extends common.ListView {
}
public _onRowHeightPropertyChanged(data: dependencyObservable.PropertyChangeData) {
this._rowHeight = data.newValue;
if (data.newValue < 0) {
this._nativeView.rowHeight = UITableViewAutomaticDimension;
this._nativeView.estimatedRowHeight = DEFAULT_HEIGHT;
@ -320,7 +321,7 @@ export class ListView extends common.ListView {
private _layoutCell(cellView: view.View, indexPath: NSIndexPath): number {
if (cellView) {
let measuredSize = view.View.measureChild(this, cellView, this.widthMeasureSpec, infinity);
let measuredSize = view.View.measureChild(this, cellView, this.widthMeasureSpec, this._rowHeight < 0 ? infinity : this._rowHeight);
let height = measuredSize.measuredHeight;
this.setHeight(indexPath.row, height);
return height;