@nativescript/core
Version:
A JavaScript library providing an easy to use api for interacting with iOS and Android platform APIs.
368 lines • 14.4 kB
JavaScript
import { ListViewBase, separatorColorProperty, itemTemplatesProperty } from './list-view-common';
import { unsetValue } from '../core/properties';
import { Color } from '../../color';
import { Observable } from '../../data/observable';
import { StackLayout } from '../layouts/stack-layout';
import { ProxyViewContainer } from '../proxy-view-container';
import { LayoutBase } from '../layouts/layout-base';
import { profile } from '../../profiling';
export * from './list-view-common';
const ITEMLOADING = ListViewBase.itemLoadingEvent;
const LOADMOREITEMS = ListViewBase.loadMoreItemsEvent;
const ITEMTAP = ListViewBase.itemTapEvent;
let ItemClickListener;
function initializeItemClickListener() {
if (ItemClickListener) {
return;
}
var ItemClickListenerImpl = /** @class */ (function (_super) {
__extends(ItemClickListenerImpl, _super);
function ItemClickListenerImpl(owner) {
var _this = _super.call(this) || this;
_this.owner = owner;
return global.__native(_this);
}
ItemClickListenerImpl.prototype.onItemClick = function (parent, convertView, index, id) {
var owner = this.owner;
var view = owner._realizedItems.get(convertView).view;
owner.notify({
eventName: ITEMTAP,
object: owner,
index: index,
view: view,
});
};
var _a;
ItemClickListenerImpl = __decorate([
Interfaces([android.widget.AdapterView.OnItemClickListener]),
__metadata("design:paramtypes", [typeof (_a = typeof ListView !== "undefined" && ListView) === "function" ? _a : Object])
], ItemClickListenerImpl);
return ItemClickListenerImpl;
}(java.lang.Object));
ItemClickListener = ItemClickListenerImpl;
}
export class ListView extends ListViewBase {
constructor() {
super(...arguments);
this._androidViewId = -1;
this._realizedItems = new Map();
this._availableViews = new Map();
this._realizedTemplates = new Map();
}
_ensureAvailableViews(templateKey) {
if (!this._availableViews.has(templateKey)) {
this._availableViews.set(templateKey, new Set());
}
}
_registerViewToTemplate(templateKey, nativeView, view) {
this._realizedItems.set(nativeView, {
view,
templateKey,
});
if (!this._realizedTemplates.has(templateKey)) {
this._realizedTemplates.set(templateKey, new Map());
}
this._realizedTemplates.get(templateKey).set(nativeView, view);
this._ensureAvailableViews(templateKey);
const availableViews = this._availableViews.get(templateKey);
availableViews.add(nativeView);
}
_markViewUsed(nativeView) {
const viewData = this._realizedItems.get(nativeView);
if (!viewData) {
throw new Error('View not registered');
}
this._ensureAvailableViews(viewData.templateKey);
this._availableViews.get(viewData.templateKey).delete(nativeView);
}
_markViewUnused(nativeView) {
const viewData = this._realizedItems.get(nativeView);
if (!viewData) {
throw new Error('View not registered');
}
this._ensureAvailableViews(viewData.templateKey);
this._availableViews.get(viewData.templateKey).add(nativeView);
}
_getKeyFromView(nativeView) {
return this._realizedItems.get(nativeView).templateKey;
}
_hasAvailableView(templateKey) {
this._ensureAvailableViews(templateKey);
return this._availableViews.get(templateKey).size > 0;
}
_getAvailableView(templateKey) {
this._ensureAvailableViews(templateKey);
if (!this._hasAvailableView(templateKey)) {
return null;
}
const view = this._availableViews.get(templateKey).values().next().value;
this._markViewUsed(view);
return view;
}
createNativeView() {
const listView = new android.widget.ListView(this._context);
listView.setDescendantFocusability(android.view.ViewGroup.FOCUS_AFTER_DESCENDANTS);
// Fixes issue with black random black items when scrolling
listView.setCacheColorHint(android.graphics.Color.TRANSPARENT);
return listView;
}
initNativeView() {
super.initNativeView();
this.updateEffectiveRowHeight();
const nativeView = this.nativeViewProtected;
initializeItemClickListener();
ensureListViewAdapterClass();
const adapter = new ListViewAdapterClass(this);
nativeView.setAdapter(adapter);
nativeView.adapter = adapter;
const itemClickListener = new ItemClickListener(this);
nativeView.setOnItemClickListener(itemClickListener);
nativeView.itemClickListener = itemClickListener;
if (this._androidViewId < 0) {
this._androidViewId = android.view.View.generateViewId();
}
nativeView.setId(this._androidViewId);
}
disposeNativeView() {
const nativeView = this.nativeViewProtected;
nativeView.setAdapter(null);
if (nativeView.itemClickListener) {
nativeView.itemClickListener.owner = null;
}
if (nativeView.adapter) {
nativeView.adapter.owner = null;
}
this.clearRealizedCells();
super.disposeNativeView();
}
onLoaded() {
super.onLoaded();
// Without this call itemClick won't be fired... :(
this.requestLayout();
}
refresh() {
const nativeView = this.nativeViewProtected;
if (!nativeView || !nativeView.getAdapter()) {
return;
}
// clear bindingContext when it is not observable because otherwise bindings to items won't reevaluate
this._realizedItems.forEach(({ view }, nativeView) => {
if (!(view.bindingContext instanceof Observable)) {
view.bindingContext = null;
}
});
nativeView.getAdapter().notifyDataSetChanged();
}
scrollToIndex(index) {
const nativeView = this.nativeViewProtected;
if (nativeView) {
nativeView.setSelection(index);
}
}
scrollToIndexAnimated(index) {
const nativeView = this.nativeViewProtected;
if (nativeView) {
nativeView.smoothScrollToPosition(index);
}
}
get _childrenCount() {
return this._realizedItems.size;
}
eachChildView(callback) {
this._realizedItems.forEach(({ view }, nativeView) => {
if (view.parent instanceof ListView) {
callback(view);
}
else {
// in some cases (like item is unloaded from another place (like angular) view.parent becomes undefined)
if (view.parent) {
callback(view.parent);
}
}
});
}
_dumpRealizedTemplates() {
console.log(`Realized Templates:`);
this._realizedTemplates.forEach((value, index) => {
console.log(`\t${index}:`);
value.forEach((value, index) => {
console.log(`\t\t${index.hashCode()}: ${value}`);
});
});
console.log(`Realized Items Size: ${this._realizedItems.size}`);
}
clearRealizedCells() {
// clear the cache
this._realizedItems.forEach(({ view }, nativeView) => {
if (view.parent) {
// This is to clear the StackLayout that is used to wrap non LayoutBase & ProxyViewContainer instances.
if (!(view.parent instanceof ListView)) {
this._removeView(view.parent);
}
view.parent._removeView(view);
}
});
this._realizedItems.clear();
this._availableViews.clear();
this._realizedTemplates.clear();
}
isItemAtIndexVisible(index) {
const nativeView = this.nativeViewProtected;
const start = nativeView.getFirstVisiblePosition();
const end = nativeView.getLastVisiblePosition();
return index >= start && index <= end;
}
[separatorColorProperty.getDefault]() {
const nativeView = this.nativeViewProtected;
return {
dividerHeight: nativeView.getDividerHeight(),
divider: nativeView.getDivider(),
};
}
[separatorColorProperty.setNative](value) {
const nativeView = this.nativeViewProtected;
if (value instanceof Color) {
nativeView.setDivider(new android.graphics.drawable.ColorDrawable(value.android));
nativeView.setDividerHeight(1);
}
else {
nativeView.setDivider(value.divider);
nativeView.setDividerHeight(value.dividerHeight);
}
}
[itemTemplatesProperty.getDefault]() {
return null;
}
[itemTemplatesProperty.setNative](value) {
this._itemTemplatesInternal = new Array(this._defaultTemplate);
if (value) {
this._itemTemplatesInternal = this._itemTemplatesInternal.concat(value);
}
this.nativeViewProtected.setAdapter(new ListViewAdapterClass(this));
this.refresh();
}
}
__decorate([
profile,
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", void 0)
], ListView.prototype, "createNativeView", null);
let ListViewAdapterClass;
function ensureListViewAdapterClass() {
if (ListViewAdapterClass) {
return;
}
var ListViewAdapter = /** @class */ (function (_super) {
__extends(ListViewAdapter, _super);
function ListViewAdapter(owner) {
var _this = _super.call(this) || this;
_this.owner = owner;
return global.__native(_this);
}
ListViewAdapter.prototype.getCount = function () {
return this.owner && this.owner.items && this.owner.items.length ? this.owner.items.length : 0;
};
ListViewAdapter.prototype.getItem = function (i) {
if (this.owner && this.owner.items && i < this.owner.items.length) {
var getItem = this.owner.items.getItem;
return getItem ? getItem.call(this.owner.items, i) : this.owner.items[i];
}
return null;
};
ListViewAdapter.prototype.getItemId = function (i) {
var item = this.getItem(i);
var id = i;
if (this.owner && item && this.owner.items) {
id = this.owner.itemIdGenerator(item, i, this.owner.items);
}
return long(id);
};
ListViewAdapter.prototype.hasStableIds = function () {
return true;
};
ListViewAdapter.prototype.getViewTypeCount = function () {
return this.owner._itemTemplatesInternal.length;
};
ListViewAdapter.prototype.getItemViewType = function (index) {
var template = this.owner._getItemTemplate(index);
var itemViewType = this.owner._itemTemplatesInternal.indexOf(template);
return itemViewType;
};
ListViewAdapter.prototype.getView = function (index, convertView, parent) {
//this.owner._dumpRealizedTemplates();
if (!this.owner) {
return null;
}
var totalItemCount = this.owner.items ? this.owner.items.length : 0;
if (index === totalItemCount - 1) {
this.owner.notify({
eventName: LOADMOREITEMS,
object: this.owner,
});
}
// Recycle an existing view or create a new one if needed.
var template = this.owner._getItemTemplate(index);
var view;
// convertView is of the wrong type
if (convertView && this.owner._getKeyFromView(convertView) !== template.key) {
this.owner._markViewUnused(convertView); // release this view
convertView = this.owner._getAvailableView(template.key); // get a view from the right type or null
}
if (convertView) {
view = this.owner._realizedItems.get(convertView).view;
}
if (!view) {
view = template.createView();
}
var args = {
eventName: ITEMLOADING,
object: this.owner,
index: index,
view: view,
android: parent,
ios: undefined,
};
this.owner.notify(args);
if (!args.view) {
args.view = this.owner._getDefaultItemContent(index);
}
if (args.view) {
if (this.owner._effectiveRowHeight > -1) {
args.view.height = this.owner.rowHeight;
}
else {
args.view.height = unsetValue;
}
this.owner._prepareItem(args.view, index);
if (!args.view.parent) {
// Proxy containers should not get treated as layouts.
// Wrap them in a real layout as well.
if (args.view instanceof LayoutBase && !(args.view instanceof ProxyViewContainer)) {
this.owner._addView(args.view);
convertView = args.view.nativeViewProtected;
}
else {
var sp = new StackLayout();
sp.addChild(args.view);
this.owner._addView(sp);
convertView = sp.nativeViewProtected;
}
}
this.owner._registerViewToTemplate(template.key, convertView, args.view);
this.owner._markViewUsed(convertView);
}
return convertView;
};
var _a, _b, _c, _d, _e, _f;
__decorate([
profile,
__metadata("design:type", Function),
__metadata("design:paramtypes", [Number, typeof (_b = typeof android !== "undefined" && (_a = android.view) !== void 0 && _a.View) === "function" ? _b : Object, typeof (_d = typeof android !== "undefined" && (_c = android.view) !== void 0 && _c.ViewGroup) === "function" ? _d : Object]),
__metadata("design:returntype", typeof (_f = typeof android !== "undefined" && (_e = android.view) !== void 0 && _e.View) === "function" ? _f : Object)
], ListViewAdapter.prototype, "getView", null);
return ListViewAdapter;
}(android.widget.BaseAdapter));
ListViewAdapterClass = ListViewAdapter;
}
//# sourceMappingURL=index.android.js.map