list-view progress

This commit is contained in:
Hristo Hristov
2016-11-29 01:32:52 +02:00
committed by Hristo Hristov
parent d795ee94e4
commit 0f45a0df5e
5 changed files with 233 additions and 343 deletions

View File

@ -329,6 +329,11 @@ declare module "ui/core/view" {
cssClasses: Set<string>; cssClasses: Set<string>;
cssPseudoClasses: Set<string>; cssPseudoClasses: Set<string>;
/**
* Gets the parent view. This property is read-only.
*/
public parent: View;
/** /**
* Gets owner page. This is a read-only property. * Gets owner page. This is a read-only property.
*/ */
@ -776,7 +781,7 @@ declare module "ui/core/view" {
export const fontFamilyProperty: InheritedCssProperty<Style, string>; export const fontFamilyProperty: InheritedCssProperty<Style, string>;
export const fontStyleProperty: InheritedCssProperty<Style, string>; export const fontStyleProperty: InheritedCssProperty<Style, string>;
export const fontWeightProperty: InheritedCssProperty<Style, string>; export const fontWeightProperty: InheritedCssProperty<Style, string>;
export const backgroundInternalProperty: CssProperty<Style, Background>; export const backgroundInternalProperty: CssProperty<Style, Background>;
export const fontInternalProperty: InheritedCssProperty<Style, Font>; export const fontInternalProperty: InheritedCssProperty<Style, Font>;
} }

View File

@ -1,224 +1,72 @@
import observable = require("data/observable"); import { ListView as ListViewDefinition, ItemsSource } from "ui/list-view";
import view = require("ui/core/view"); import { EventData, Observable } from "data/observable";
import proxy = require("ui/core/proxy"); import { View, Template, KeyedTemplate } from "ui/core/view";
import definition = require("ui/list-view"); import { Property } from "ui/core/properties";
import dependencyObservable = require("ui/core/dependency-observable"); import { Color } from "color";
import color = require("color");
import * as builderModule from "ui/builder"; import { parse, parseMultipleTemplates } from "ui/builder";
import * as labelModule from "ui/label"; import { Label } from "ui/label";
import * as observableArrayModule from "data/observable-array"; import { ObservableArray } from "data/observable-array";
import * as weakEventsModule from "ui/core/weak-event-listener"; import { addWeakEventListener, removeWeakEventListener } from "ui/core/weak-event-listener";
import {isString, isFunction} from "utils/types";
import { Bindable } from "ui/core/bindable"; import { Bindable } from "ui/core/bindable";
var builder: typeof builderModule; // TODO: Think of a way to register these instead of relying on hardcoded values.
function ensureBuilder() {
if (!builder) {
builder = require("ui/builder");
}
}
var label: typeof labelModule;
function ensureLabel() {
if (!label) {
label = require("ui/label");
}
}
var observableArray: typeof observableArrayModule;
function ensureObservableArray() {
if (!observableArray) {
observableArray = require("data/observable-array");
}
}
var weakEvents: typeof weakEventsModule;
function ensureWeakEvents() {
if (!weakEvents) {
weakEvents = require("ui/core/weak-event-listener");
}
}
var ITEMS = "items";
var ITEMTEMPLATE = "itemTemplate";
var ITEMTEMPLATES = "itemTemplates";
var ISSCROLLING = "isScrolling";
var LISTVIEW = "ListView";
var SEPARATORCOLOR = "separatorColor";
var ROWHEIGHT = "rowHeight";
export module knownTemplates { export module knownTemplates {
export var itemTemplate = "itemTemplate"; export let itemTemplate = "itemTemplate";
} }
export module knownMultiTemplates { export module knownMultiTemplates {
export var itemTemplates = "itemTemplates"; export let itemTemplates = "itemTemplates";
} }
function onItemsPropertyChanged(data: dependencyObservable.PropertyChangeData) { export abstract class ListViewBase extends View implements ListViewDefinition {
var listView = <ListView>data.object;
listView._onItemsPropertyChanged(data);
}
function onItemTemplatePropertyChanged(data: dependencyObservable.PropertyChangeData) {
var listView = <definition.ListView>data.object;
listView.refresh();
}
function onItemTemplatesPropertyChanged(data: dependencyObservable.PropertyChangeData) {
var listView = <ListView>data.object;
listView._onItemTemplatesPropertyChanged(data);
}
function onRowHeightPropertyChanged(data: dependencyObservable.PropertyChangeData) {
var listView = <ListView>data.object;
listView._onRowHeightPropertyChanged(data);
}
export class ListView extends view.View implements definition.ListView {
public static itemLoadingEvent = "itemLoading"; public static itemLoadingEvent = "itemLoading";
public static itemTapEvent = "itemTap"; public static itemTapEvent = "itemTap";
public static loadMoreItemsEvent = "loadMoreItems"; public static loadMoreItemsEvent = "loadMoreItems";
// TODO: get rid of such hacks.
public static knownFunctions = ["itemTemplateSelector"]; //See component-builder.ts isKnownFunction public static knownFunctions = ["itemTemplateSelector"]; //See component-builder.ts isKnownFunction
private _itemTemplateSelector: (item: any, index: number, items: any) => string; private _itemTemplateSelector: (item: any, index: number, items: any) => string;
private _itemTemplateSelectorBindable = new Bindable(); private _itemTemplateSelectorBindable = new Bindable();
public _defaultTemplate: view.KeyedTemplate = { public _defaultTemplate: KeyedTemplate = {
key: "default", key: "default",
createView: () => { createView: () => {
if (this.itemTemplate) { if (this.itemTemplate) {
ensureBuilder(); return parse(this.itemTemplate, this);
return builder.parse(this.itemTemplate, this);
} }
return undefined; return undefined;
} }
} }
public _itemTemplatesInternal = new Array<view.KeyedTemplate>(this._defaultTemplate);
public static separatorColorProperty = new dependencyObservable.Property( public _itemTemplatesInternal = new Array<KeyedTemplate>(this._defaultTemplate);
SEPARATORCOLOR,
LISTVIEW,
new proxy.PropertyMetadata(undefined));
public static itemsProperty = new dependencyObservable.Property( public rowHeight: number;
ITEMS, public separatorColor: Color;
LISTVIEW, public items: any[] | ItemsSource;
new proxy.PropertyMetadata( public itemTemplate: string | Template;
undefined, public itemTemplates: string | Array<KeyedTemplate>;
dependencyObservable.PropertyMetadataSettings.AffectsLayout,
onItemsPropertyChanged
)
);
public static itemTemplateProperty = new dependencyObservable.Property(
ITEMTEMPLATE,
LISTVIEW,
new proxy.PropertyMetadata(
undefined,
dependencyObservable.PropertyMetadataSettings.AffectsLayout,
onItemTemplatePropertyChanged
)
);
public static itemTemplatesProperty = new dependencyObservable.Property(
ITEMTEMPLATES,
LISTVIEW,
new proxy.PropertyMetadata(
undefined,
dependencyObservable.PropertyMetadataSettings.AffectsLayout,
onItemTemplatesPropertyChanged
)
);
public static isScrollingProperty = new dependencyObservable.Property(
ISSCROLLING,
LISTVIEW,
new proxy.PropertyMetadata(
false,
dependencyObservable.PropertyMetadataSettings.None
)
);
public static rowHeightProperty = new dependencyObservable.Property(
ROWHEIGHT,
LISTVIEW,
new proxy.PropertyMetadata(
-1,
dependencyObservable.PropertyMetadataSettings.AffectsLayout,
onRowHeightPropertyChanged
)
);
get items(): any {
return this._getValue(ListView.itemsProperty);
}
set items(value: any) {
this._setValue(ListView.itemsProperty, value);
}
get itemTemplate(): string | view.Template {
return this._getValue(ListView.itemTemplateProperty);
}
set itemTemplate(value: string | view.Template) {
this._setValue(ListView.itemTemplateProperty, value);
}
get itemTemplates(): string | Array<view.KeyedTemplate> {
return this._getValue(ListView.itemTemplatesProperty);
}
set itemTemplates(value: string | Array<view.KeyedTemplate>) {
let newValue = value;
if (isString(newValue)){
ensureBuilder();
newValue = builder.parseMultipleTemplates(<string>newValue, this);
}
this._setValue(ListView.itemTemplatesProperty, newValue);
}
get itemTemplateSelector(): string | ((item: any, index: number, items: any) => string) { get itemTemplateSelector(): string | ((item: any, index: number, items: any) => string) {
return this._itemTemplateSelector; return this._itemTemplateSelector;
} }
set itemTemplateSelector(value: string | ((item: any, index: number, items: any) => string)) { set itemTemplateSelector(value: string | ((item: any, index: number, items: any) => string)) {
if (isString(value)){ if (typeof value === "string") {
this._itemTemplateSelectorBindable.bind({ this._itemTemplateSelectorBindable.bind({
sourceProperty: null, sourceProperty: null,
targetProperty: "templateKey", targetProperty: "templateKey",
expression: <string>value expression: value
}); });
this._itemTemplateSelector = (item: any, index: number, items: any) => { this._itemTemplateSelector = (item: any, index: number, items: any) => {
item["$index"] = index; item["$index"] = index;
this._itemTemplateSelectorBindable.bindingContext = item; this._itemTemplateSelectorBindable.bindingContext = item;
return this._itemTemplateSelectorBindable.get("templateKey"); return this._itemTemplateSelectorBindable.get("templateKey");
}; };
} }
else if (isFunction(value)) { else if (typeof value === "function") {
this._itemTemplateSelector = <((item: any, index: number, items: any) => string)>value; this._itemTemplateSelector = value;
} }
} }
get isScrolling(): boolean {
return false;
}
set isScrolling(value: boolean) {
// Do nothing.
}
get separatorColor(): color.Color {
return this.style._getValue(ListView.separatorColorProperty);
}
set separatorColor(value: color.Color) {
this.style._setValue(ListView.separatorColorProperty,
value instanceof color.Color ? value : new color.Color(<any>value));
}
get rowHeight(): number {
return this._getValue(ListView.rowHeightProperty);
}
set rowHeight(value: number) {
this._setValue(ListView.rowHeightProperty, value);
}
public refresh() { public refresh() {
// //
} }
@ -227,69 +75,107 @@ export class ListView extends view.View implements definition.ListView {
// //
} }
public _getItemTemplate(index: number): view.KeyedTemplate { public _getItemTemplate(index: number): KeyedTemplate {
let templateKey = "default"; let templateKey = "default";
if (this.itemTemplateSelector){ if (this.itemTemplateSelector) {
let dataItem = this._getDataItem(index); let dataItem = this._getDataItem(index);
templateKey = this._itemTemplateSelector(dataItem, index, this.items); templateKey = this._itemTemplateSelector(dataItem, index, this.items);
} }
for (let i = 0, length = this._itemTemplatesInternal.length; i < length; i++) { for (let i = 0, length = this._itemTemplatesInternal.length; i < length; i++) {
if (this._itemTemplatesInternal[i].key === templateKey){ if (this._itemTemplatesInternal[i].key === templateKey) {
return this._itemTemplatesInternal[i]; return this._itemTemplatesInternal[i];
} }
} }
// This is the default template // This is the default template
return this._itemTemplatesInternal[0]; return this._itemTemplatesInternal[0];
} }
public _prepareItem(item: view.View, index: number) { public _prepareItem(item: View, index: number) {
if (item) { if (item) {
item.bindingContext = this._getDataItem(index); item.bindingContext = this._getDataItem(index);
} }
} }
private _getDataItem(index: number): any { private _getDataItem(index: number): any {
let thisItems = this.items; let thisItems = <ItemsSource>this.items;
return thisItems.getItem ? thisItems.getItem(index) : thisItems[index]; return thisItems.getItem ? thisItems.getItem(index) : thisItems[index];
} }
public _getDefaultItemContent(index: number): view.View { public _getDefaultItemContent(index: number): View {
ensureLabel(); let lbl = new Label();
var lbl = new label.Label();
lbl.bind({ lbl.bind({
targetProperty: "text", targetProperty: "text",
sourceProperty: "$value" sourceProperty: "$value"
}); }, null);
return lbl; return lbl;
} }
public _onItemsPropertyChanged(data: dependencyObservable.PropertyChangeData) { public _onItemsChanged(args: EventData) {
ensureObservableArray(); this.refresh();
ensureWeakEvents(); }
if (data.oldValue instanceof observable.Observable) { public _onRowHeightPropertyChanged(oldValue: number, newValue: number) {
weakEvents.removeWeakEventListener(data.oldValue, observableArray.ObservableArray.changeEvent, this._onItemsChanged, this); this.refresh();
}
}
/**
* Represents the property backing the items property of each ListView instance.
*/
export const itemsProperty = new Property<ListViewBase, any[] | ItemsSource>({
name: "items", valueChanged: (target, oldValue, newValue) => {
if (oldValue instanceof Observable) {
removeWeakEventListener(oldValue, ObservableArray.changeEvent, target._onItemsChanged, target);
} }
if (data.newValue instanceof observable.Observable) { if (newValue instanceof Observable) {
weakEvents.addWeakEventListener(data.newValue, observableArray.ObservableArray.changeEvent, this._onItemsChanged, this); addWeakEventListener(newValue, ObservableArray.changeEvent, target._onItemsChanged, target);
} }
this.refresh(); target.refresh();
} }
});
private _onItemsChanged(args: observable.EventData) { /**
this.refresh(); * Represents the item template property of each ListView instance.
*/
export const itemTemplateProperty = new Property<ListViewBase, string | Template>({
name: "itemTemplate", valueChanged: (target) => {
target.refresh();
} }
});
public _onRowHeightPropertyChanged(data: dependencyObservable.PropertyChangeData) { /**
this.refresh(); * Represents the items template property of each ListView instance.
} */
export const itemTemplatesProperty = new Property<ListViewBase, string | Array<KeyedTemplate>>({
name: "itemTemplates", valueConverter: (value) => {
if (typeof value === "string") {
return parseMultipleTemplates(value);
}
public _onItemTemplatesPropertyChanged(data: dependencyObservable.PropertyChangeData) { return value;
//
} }
} })
/**
* Represents the separator color backing property.
*/
export const separatorColor = new Property<ListViewBase, Color>({
name: "separatorColor", valueConverter: (value) => {
if (typeof value === "string") {
return new Color(value);
}
return value;
}
})
/**
* Represents the observable property backing the rowHeight property of each ListView instance.
*/
export const rowHeightProperty = new Property<ListViewBase, number>({
name: "rowHeight", defaultValue: -1, valueChanged: (target, oldValue, newValue) => target._onRowHeightPropertyChanged(oldValue, newValue)
});

View File

@ -1,50 +1,41 @@
import observable = require("data/observable"); import { ItemEventData, ItemsSource } from "ui/list-view";
import common = require("./list-view-common"); import { ListViewBase, separatorColor, itemTemplatesProperty } from "./list-view-common";
import viewModule = require("ui/core/view"); import { View, KeyedTemplate } from "ui/core/view";
import stackLayout = require("ui/layouts/stack-layout"); import { Property } from "ui/core/properties";
import proxy = require("ui/core/proxy"); import { unsetValue } from "ui/core/dependency-observable";
import dependencyObservable = require("ui/core/dependency-observable"); import { Observable } from "data/observable";
import definition = require("ui/list-view"); import { StackLayout } from "ui/layouts/stack-layout";
import {ProxyViewContainer} from "ui/proxy-view-container"; import { ProxyViewContainer } from "ui/proxy-view-container";
import * as layoutBase from "ui/layouts/layout-base"; import { LayoutBase } from "ui/layouts/layout-base";
import * as colorModule from "color"; import { Color } from "color";
import { separatorColorProperty, registerHandler, Styler, StylePropertyChangedHandler } from "ui/styling/style";
let color: typeof colorModule; export * from "./list-view-common";
function ensureColor() {
if (!color) { let ITEMLOADING = ListViewBase.itemLoadingEvent;
color = require("color"); let LOADMOREITEMS = ListViewBase.loadMoreItemsEvent;
let ITEMTAP = ListViewBase.itemTapEvent;
@Interfaces([])
class ItemClickListener implements android.widget.AdapterView.OnItemClickListener {
constructor(private owner: WeakRef<ListView>) {
return global.__native(this);
}
onItemClick<T extends android.widget.Adapter>(parent: android.widget.AdapterView<T>, convertView: android.view.View, index: number, id: number) {
let owner = this.owner.get();
if (owner) {
let view = owner._realizedTemplates.get(owner._getItemTemplate(index).key).get(convertView);
owner.notify({ eventName: ITEMTAP, object: owner, index: index, view: view });
}
} }
} }
let ITEMLOADING = common.ListView.itemLoadingEvent; export class ListView extends ListViewBase {
let LOADMOREITEMS = common.ListView.loadMoreItemsEvent;
let ITEMTAP = common.ListView.itemTapEvent;
global.moduleMerge(common, exports);
function onSeparatorColorPropertyChanged(data: dependencyObservable.PropertyChangeData) {
let listView = <ListView>data.object;
if (!listView.android) {
return;
}
ensureColor();
if (data.newValue instanceof color.Color) {
listView.android.setDivider(new android.graphics.drawable.ColorDrawable(data.newValue.android));
listView.android.setDividerHeight(1);
}
}
// register the setNativeValue callbacks
(<proxy.PropertyMetadata>common.ListView.separatorColorProperty.metadata).onSetNativeValue = onSeparatorColorPropertyChanged;
export class ListView extends common.ListView {
private _androidViewId: number = -1; private _androidViewId: number = -1;
private _android: android.widget.ListView; private _android: android.widget.ListView;
public _realizedItems = new Map<android.view.View, viewModule.View>(); private _itemClickListener: android.widget.AdapterView.OnItemClickListener;
public _realizedTemplates = new Map<string, Map<android.view.View, viewModule.View>>(); public _realizedItems = new Map<android.view.View, View>();
public _realizedTemplates = new Map<string, Map<android.view.View, View>>();
public _createUI() { public _createUI() {
this._android = new android.widget.ListView(this._context); this._android = new android.widget.ListView(this._context);
@ -61,15 +52,8 @@ export class ListView extends common.ListView {
this.android.setAdapter(new ListViewAdapterClass(this)); this.android.setAdapter(new ListViewAdapterClass(this));
let that = new WeakRef(this); let that = new WeakRef(this);
this.android.setOnItemClickListener(new android.widget.AdapterView.OnItemClickListener({ this._itemClickListener = this._itemClickListener || new ItemClickListener(new WeakRef(this));
onItemClick: function (parent: any, convertView: android.view.View, index: number, id: number) { this.android.setOnItemClickListener(this._itemClickListener);
let owner = that.get();
if (owner) {
let view = owner._realizedTemplates.get(owner._getItemTemplate(index).key).get(convertView);
owner.notify({ eventName: ITEMTAP, object: owner, index: index, view: view });
}
}
}));
} }
get android(): android.widget.ListView { get android(): android.widget.ListView {
@ -82,12 +66,12 @@ export class ListView extends common.ListView {
} }
// clear bindingContext when it is not observable because otherwise bindings to items won't reevaluate // clear bindingContext when it is not observable because otherwise bindings to items won't reevaluate
this._realizedItems.forEach((view, nativeView, map) => { this._realizedItems.forEach((view, nativeView, map) => {
if (!(view.bindingContext instanceof observable.Observable)) { if (!(view.bindingContext instanceof Observable)) {
view.bindingContext = null; view.bindingContext = null;
} }
}); });
(<android.widget.BaseAdapter>this.android.getAdapter()).notifyDataSetChanged(); (<android.widget.BaseAdapter>this.android.getAdapter()).notifyDataSetChanged();
} }
@ -106,7 +90,7 @@ export class ListView extends common.ListView {
return this._realizedItems.size; return this._realizedItems.size;
} }
public _eachChildView(callback: (child: viewModule.View) => boolean): void { public _eachChildView(callback: (child: View) => boolean): void {
this._realizedItems.forEach((view, nativeView, map) => { this._realizedItems.forEach((view, nativeView, map) => {
if (view.parent instanceof ListView) { if (view.parent instanceof ListView) {
callback(view); callback(view);
@ -120,16 +104,16 @@ export class ListView extends common.ListView {
}); });
} }
public _dumpRealizedTemplates(){ public _dumpRealizedTemplates() {
console.log(`Realized Templates:`); console.log(`Realized Templates:`);
this._realizedTemplates.forEach((value, index, map) => { this._realizedTemplates.forEach((value, index, map) => {
console.log(`\t${index}:`); console.log(`\t${index}:`);
value.forEach((value, index, map) => { value.forEach((value, index, map) => {
console.log(`\t\t${index.hashCode()}: ${value}`); console.log(`\t\t${index.hashCode()}: ${value}`);
}); });
}); });
console.log(`Realized Items Size: ${this._realizedItems.size}`); console.log(`Realized Items Size: ${this._realizedItems.size}`);
} }
private clearRealizedCells(): void { private clearRealizedCells(): void {
// clear the cache // clear the cache
@ -147,17 +131,30 @@ export class ListView extends common.ListView {
this._realizedTemplates.clear(); this._realizedTemplates.clear();
} }
public _onItemTemplatesPropertyChanged(data: dependencyObservable.PropertyChangeData) { get [separatorColor.native](): number {
this._itemTemplatesInternal = new Array<viewModule.KeyedTemplate>(this._defaultTemplate); return null;
if (data.newValue){ }
this._itemTemplatesInternal = this._itemTemplatesInternal.concat(data.newValue); set [separatorColor.native](value: Color) {
let nativeView = this._android;
if (value) {
nativeView.setDivider(new android.graphics.drawable.ColorDrawable(value.android));
nativeView.setDividerHeight(1);
} else {
nativeView.setDivider(null);
nativeView.setDividerHeight(0);
} }
}
if (this.android){
ensureListViewAdapterClass(); get [itemTemplatesProperty.native](): KeyedTemplate[] {
this.android.setAdapter(new ListViewAdapterClass(this)); return null;
}
set [itemTemplatesProperty.native](value: KeyedTemplate[]) {
this._itemTemplatesInternal = new Array<KeyedTemplate>(this._defaultTemplate);
if (value) {
this._itemTemplatesInternal = this._itemTemplatesInternal.concat(value);
} }
this.android.setAdapter(new ListViewAdapterClass(this));
this.refresh(); this.refresh();
} }
} }
@ -173,9 +170,7 @@ function ensureListViewAdapterClass() {
constructor(listView: ListView) { constructor(listView: ListView) {
super(); super();
this._listView = listView; this._listView = listView;
return global.__native(this); return global.__native(this);
} }
@ -185,7 +180,8 @@ function ensureListViewAdapterClass() {
public getItem(i: number) { public getItem(i: number) {
if (this._listView && this._listView.items && i < this._listView.items.length) { if (this._listView && this._listView.items && i < this._listView.items.length) {
return this._listView.items.getItem ? this._listView.items.getItem(i) : this._listView.items[i]; let getItem = (<ItemsSource>this._listView.items).getItem
return getItem ? getItem(i) : this._listView.items[i];
} }
return null; return null;
@ -200,7 +196,7 @@ function ensureListViewAdapterClass() {
} }
public getViewTypeCount() { public getViewTypeCount() {
return this._listView._itemTemplatesInternal.length; return this._listView._itemTemplatesInternal.length;
} }
public getItemViewType(index: number) { public getItemViewType(index: number) {
@ -208,10 +204,10 @@ function ensureListViewAdapterClass() {
let itemViewType = this._listView._itemTemplatesInternal.indexOf(template); let itemViewType = this._listView._itemTemplatesInternal.indexOf(template);
return itemViewType; return itemViewType;
} }
public getView(index: number, convertView: android.view.View, parent: android.view.ViewGroup): android.view.View { public getView(index: number, convertView: android.view.View, parent: android.view.ViewGroup): android.view.View {
//this._listView._dumpRealizedTemplates(); //this._listView._dumpRealizedTemplates();
if (!this._listView) { if (!this._listView) {
return null; return null;
} }
@ -223,10 +219,10 @@ function ensureListViewAdapterClass() {
// Recycle an existing view or create a new one if needed. // Recycle an existing view or create a new one if needed.
let template = this._listView._getItemTemplate(index); let template = this._listView._getItemTemplate(index);
let view: viewModule.View; let view: View;
if (convertView){ if (convertView) {
view = this._listView._realizedTemplates.get(template.key).get(convertView); view = this._listView._realizedTemplates.get(template.key).get(convertView);
if (!view){ if (!view) {
throw new Error(`There is no entry with key '${convertView}' in the realized views cache for template with key'${template.key}'.`); throw new Error(`There is no entry with key '${convertView}' in the realized views cache for template with key'${template.key}'.`);
} }
} }
@ -234,7 +230,7 @@ function ensureListViewAdapterClass() {
view = template.createView(); view = template.createView();
} }
let args: definition.ItemEventData = { let args: ItemEventData = {
eventName: ITEMLOADING, object: this._listView, index: index, view: view, eventName: ITEMLOADING, object: this._listView, index: index, view: view,
android: parent, android: parent,
ios: undefined ios: undefined
@ -251,19 +247,19 @@ function ensureListViewAdapterClass() {
args.view.height = this._listView.rowHeight; args.view.height = this._listView.rowHeight;
} }
else { else {
args.view.height = Number.NaN; args.view.height = unsetValue;
} }
this._listView._prepareItem(args.view, index); this._listView._prepareItem(args.view, index);
if (!args.view.parent) { if (!args.view.parent) {
// Proxy containers should not get treated as layouts. // Proxy containers should not get treated as layouts.
// Wrap them in a real layout as well. // Wrap them in a real layout as well.
if (args.view instanceof layoutBase.LayoutBase && if (args.view instanceof LayoutBase &&
!(args.view instanceof ProxyViewContainer)) { !(args.view instanceof ProxyViewContainer)) {
this._listView._addView(args.view); this._listView._addView(args.view);
convertView = args.view.android; convertView = args.view.android;
} else { } else {
let sp = new stackLayout.StackLayout(); let sp = new StackLayout();
sp.addChild(args.view); sp.addChild(args.view);
this._listView._addView(sp); this._listView._addView(sp);
@ -273,10 +269,10 @@ function ensureListViewAdapterClass() {
// Cache the view for recycling // Cache the view for recycling
let realizedItemsForTemplateKey = this._listView._realizedTemplates.get(template.key); let realizedItemsForTemplateKey = this._listView._realizedTemplates.get(template.key);
if (!realizedItemsForTemplateKey){ if (!realizedItemsForTemplateKey) {
realizedItemsForTemplateKey = new Map<android.view.View, viewModule.View>(); realizedItemsForTemplateKey = new Map<android.view.View, View>();
this._listView._realizedTemplates.set(template.key, realizedItemsForTemplateKey); this._listView._realizedTemplates.set(template.key, realizedItemsForTemplateKey);
} }
realizedItemsForTemplateKey.set(convertView, args.view); realizedItemsForTemplateKey.set(convertView, args.view);
this._listView._realizedItems.set(convertView, args.view); this._listView._realizedItems.set(convertView, args.view);
} }
@ -309,4 +305,4 @@ export class ListViewStyler implements Styler {
} }
} }
ListViewStyler.registerHandlers(); ListViewStyler.registerHandlers();

View File

@ -2,10 +2,10 @@
* Contains the ListView class, which represents a standard list view widget. * Contains the ListView class, which represents a standard list view widget.
*/ */
declare module "ui/list-view" { declare module "ui/list-view" {
import observable = require("data/observable"); import { EventData } from "data/observable";
import dependencyObservable = require("ui/core/dependency-observable"); import { View, Template, KeyedTemplate } from "ui/core/view";
import view = require("ui/core/view"); import { Property } from "ui/core/properties";
import color = require("color"); import { Color } from "color";
/** /**
* Known template names. * Known template names.
@ -20,7 +20,7 @@ declare module "ui/list-view" {
/** /**
* Represents a view that shows items in a vertically scrolling list. * Represents a view that shows items in a vertically scrolling list.
*/ */
export class ListView extends view.View { export class ListView extends View {
/** /**
* String value used when hooking to itemLoading event. * String value used when hooking to itemLoading event.
*/ */
@ -34,27 +34,6 @@ declare module "ui/list-view" {
*/ */
public static loadMoreItemsEvent: string; public static loadMoreItemsEvent: string;
/**
* Represents the observable property backing the items property of each ListView instance.
*/
public static itemsProperty: dependencyObservable.Property;
/**
* Represents the item template property of each ListView instance.
*/
public static itemTemplateProperty: dependencyObservable.Property;
/**
* Represents the observable property backing the isScrolling property of each ListView instance.
*/
@Deprecated // in 2.1
public static isScrollingProperty: dependencyObservable.Property;
/**
* Represents the observable property backing the rowHeight property of each ListView instance.
*/
public static rowHeightProperty: dependencyObservable.Property;
/** /**
* Gets the native [android widget](http://developer.android.com/reference/android/widget/ListView.html) that represents the user interface for this component. Valid only when running on Android OS. * Gets the native [android widget](http://developer.android.com/reference/android/widget/ListView.html) that represents the user interface for this component. Valid only when running on Android OS.
*/ */
@ -65,27 +44,21 @@ declare module "ui/list-view" {
*/ */
ios: any /* UITableView */; ios: any /* UITableView */;
/**
* Gets a value indicating whether the ListView is currently scrolling.
*/
@Deprecated // in 2.1
isScrolling: boolean;
/** /**
* Gets or set the items collection of the ListView. * Gets or set the items collection of the ListView.
* The items property can be set to an array or an object defining length and getItem(index) method. * The items property can be set to an array or an object defining length and getItem(index) method.
*/ */
items: any; items: any[] | ItemsSource;
/** /**
* Gets or set the item template of the ListView. * Gets or set the item template of the ListView.
*/ */
itemTemplate: string | view.Template; itemTemplate: string | Template;
/** /**
* Gets or set the list of item templates for the item template selector * Gets or set the list of item templates for the item template selector
*/ */
itemTemplates: string | Array<view.KeyedTemplate>; itemTemplates: string | Array<KeyedTemplate>;
/** /**
* A function that returns the appropriate ket template based on the data item. * A function that returns the appropriate ket template based on the data item.
@ -95,7 +68,7 @@ declare module "ui/list-view" {
/** /**
* Gets or set the items separator line color of the ListView. * Gets or set the items separator line color of the ListView.
*/ */
separatorColor: color.Color; separatorColor: Color;
/** /**
* Gets or set row height of the ListView. * Gets or set row height of the ListView.
@ -121,7 +94,7 @@ declare module "ui/list-view" {
* @param callback - Callback function which will be executed when event is raised. * @param callback - Callback function which will be executed when event is raised.
* @param thisArg - An optional parameter which will be used as `this` context for callback execution. * @param thisArg - An optional parameter which will be used as `this` context for callback execution.
*/ */
on(eventNames: string, callback: (data: observable.EventData) => void, thisArg?: any); on(eventNames: string, callback: (data: EventData) => void, thisArg?: any);
/** /**
* Raised when a View for the data at the specified index should be created. * Raised when a View for the data at the specified index should be created.
@ -139,13 +112,13 @@ declare module "ui/list-view" {
/** /**
* Raised when the ListView is scrolled so that its last item is visible. * Raised when the ListView is scrolled so that its last item is visible.
*/ */
on(event: "loadMoreItems", callback: (args: observable.EventData) => void, thisArg?: any); on(event: "loadMoreItems", callback: (args: EventData) => void, thisArg?: any);
} }
/** /**
* Event data containing information for the index and the view associated to a list view item. * Event data containing information for the index and the view associated to a list view item.
*/ */
export interface ItemEventData extends observable.EventData { export interface ItemEventData extends EventData {
/** /**
* The index of the item, for which the event is raised. * The index of the item, for which the event is raised.
*/ */
@ -154,7 +127,7 @@ declare module "ui/list-view" {
/** /**
* The view that is associated to the item, for which the event is raised. * The view that is associated to the item, for which the event is raised.
*/ */
view: view.View; view: View;
/** /**
* Gets the native [iOS view](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableViewCell_Class/) that represents the user interface where the view is hosted. Valid only when running on iOS. * Gets the native [iOS view](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableViewCell_Class/) that represents the user interface where the view is hosted. Valid only when running on iOS.
@ -166,4 +139,34 @@ declare module "ui/list-view" {
*/ */
android: any /* android.view.ViewGroup */; android: any /* android.view.ViewGroup */;
} }
export interface ItemsSource {
length: number;
getItem(index: number): any;
}
/**
* Represents the property backing the items property of each ListView instance.
*/
export const itemsProperty: Property<ListView, any[] | ItemsSource>;
/**
* Represents the item template property of each ListView instance.
*/
export const itemTemplateProperty: Property<ListView, string | Template>;
/**
* Represents the items template property of each ListView instance.
*/
export const itemTemplatesProperty: Property<ListView, string | Array<KeyedTemplate>>;
/**
* Represents the separator color backing property.
*/
export const separatorColor: Property<ListView, Color>;
/**
* Represents the observable property backing the rowHeight property of each ListView instance.
*/
export const rowHeightProperty: Property<ListView, number>;
} }

View File

@ -214,8 +214,8 @@ export class ListView extends common.ListView {
private _dataSource; private _dataSource;
private _delegate; private _delegate;
private _heights: Array<number>; private _heights: Array<number>;
private _preparingCell: boolean = false; private _preparingCell: boolean;
private _isDataDirty: boolean = false; private _isDataDirty: boolean;
private _map: Map<ListViewCell, view.View>; private _map: Map<ListViewCell, view.View>;
public _rowHeight: number = -1; public _rowHeight: number = -1;
widthMeasureSpec: number = 0; widthMeasureSpec: number = 0;
@ -233,7 +233,7 @@ export class ListView extends common.ListView {
this._map = new Map<ListViewCell, view.View>(); this._map = new Map<ListViewCell, view.View>();
} }
public _onItemTemplatesPropertyChanged(data: dependencyObservable.PropertyChangeData) { protected _onItemTemplatesPropertyChanged(data: dependencyObservable.PropertyChangeData) {
this._itemTemplatesInternal = new Array<view.KeyedTemplate>(this._defaultTemplate); this._itemTemplatesInternal = new Array<view.KeyedTemplate>(this._defaultTemplate);
if (data.newValue) { if (data.newValue) {
for(let i = 0, length = data.newValue.length; i < length; i++){ for(let i = 0, length = data.newValue.length; i < length; i++){
@ -304,16 +304,16 @@ export class ListView extends common.ListView {
this._heights[index] = value; this._heights[index] = value;
} }
public _onRowHeightPropertyChanged(data: dependencyObservable.PropertyChangeData) { public _onRowHeightPropertyChanged(oldValue: number, newValue: number) {
this._rowHeight = data.newValue; this._rowHeight = newValue;
if (data.newValue < 0) { if (newValue < 0) {
this._nativeView.rowHeight = UITableViewAutomaticDimension; this._nativeView.rowHeight = UITableViewAutomaticDimension;
this._nativeView.estimatedRowHeight = DEFAULT_HEIGHT; this._nativeView.estimatedRowHeight = DEFAULT_HEIGHT;
this._delegate = UITableViewDelegateImpl.initWithOwner(new WeakRef(this)); this._delegate = UITableViewDelegateImpl.initWithOwner(new WeakRef(this));
} }
else { else {
this._nativeView.rowHeight = data.newValue; this._nativeView.rowHeight = newValue;
this._nativeView.estimatedRowHeight = data.newValue; this._nativeView.estimatedRowHeight = newValue;
this._delegate = UITableViewRowHeightDelegateImpl.initWithOwner(new WeakRef(this)); this._delegate = UITableViewRowHeightDelegateImpl.initWithOwner(new WeakRef(this));
} }
@ -321,7 +321,7 @@ export class ListView extends common.ListView {
this._nativeView.delegate = this._delegate; this._nativeView.delegate = this._delegate;
} }
super._onRowHeightPropertyChanged(data); super._onRowHeightPropertyChanged(oldValue, newValue);
} }
public requestLayout(): void { public requestLayout(): void {