@nativescript/core
Version:
A JavaScript library providing an easy to use api for interacting with iOS and Android platform APIs.
1,005 lines • 43.3 kB
JavaScript
import { ListViewBase, separatorColorProperty, itemTemplatesProperty, stickyHeaderProperty, stickyHeaderTemplateProperty, sectionedProperty, showSearchProperty } from './list-view-common';
import { View } from '../core/view';
import { PercentLength } from '../styling/length-shared';
import { unsetValue } from '../core/properties/property-shared';
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';
import { Trace } from '../../trace';
import { Builder } from '../builder';
import { Label } from '../label';
export * from './list-view-common';
const ITEMLOADING = ListViewBase.itemLoadingEvent;
const LOADMOREITEMS = ListViewBase.loadMoreItemsEvent;
const ITEMTAP = ListViewBase.itemTapEvent;
const SEARCHCHANGE = ListViewBase.searchChangeEvent;
const STICKY_HEADER_Z_INDEX = 1000;
const SEARCH_VIEW_Z_INDEX = 2000;
// View type constants for sectioned lists
const ITEM_VIEW_TYPE = 0;
let ItemClickListener;
function initializeItemClickListener() {
if (ItemClickListener) {
return;
}
var ItemClickListenerImpl = (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,
});
};
ItemClickListenerImpl = __decorate([
Interfaces([android.widget.AdapterView.OnItemClickListener])
], 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();
this._stickyHeaderHeight = 0;
this._hiddenHeaderPositions = new Set(); // Track which headers to hide
}
get hasSearchView() {
return !!this._searchView;
}
_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);
// Don't setup search here - wait for onLoaded when context is properly available
}
disposeNativeView() {
const nativeView = this.nativeViewProtected;
nativeView.setAdapter(null);
if (nativeView.itemClickListener) {
nativeView.itemClickListener.owner = null;
}
if (nativeView.adapter) {
nativeView.adapter.owner = null;
}
// Cleanup search
this._cleanupSearchView();
// Cleanup sticky header
this._cleanupStickyHeader();
this.clearRealizedCells();
super.disposeNativeView();
}
_cleanupStickyHeader() {
// Remove scroll listener
if (this._scrollListener) {
this.nativeViewProtected.setOnScrollListener(null);
this._scrollListener = null;
}
// Remove sticky header from parent
if (this._stickyHeaderView && this._stickyHeaderView.parent) {
this._stickyHeaderView.parent._removeView(this._stickyHeaderView);
}
this._stickyHeaderView = null;
this._stickyHeaderHeight = 0;
// Clear hidden headers
this._hiddenHeaderPositions.clear();
}
onLoaded() {
super.onLoaded();
// Without this call itemClick won't be fired... :(
this.requestLayout();
// Setup sticky header if enabled
if (this.stickyHeader && this.sectioned && this.stickyHeaderTemplate) {
this._setupStickyHeader();
}
// Setup search if enabled and not already set up
if (this.showSearch && !this._searchView && this.nativeViewProtected && this.nativeViewProtected.getAdapter()) {
this._setupSearchView();
}
}
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;
}
});
// Safely refresh the adapter - no HeaderViewListAdapter issues since we don't use headers
const adapter = nativeView.getAdapter();
if (adapter instanceof android.widget.BaseAdapter) {
try {
adapter.notifyDataSetChanged();
}
catch (error) {
if (Trace.isEnabled()) {
Trace.error('Error refreshing adapter, recreating: ' + error);
}
nativeView.setAdapter(new ListViewAdapterClass(this));
}
}
}
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;
}
// Sticky header methods
_setupStickyHeader() {
if (!this.stickyHeader || !this.sectioned || !this.stickyHeaderTemplate) {
return;
}
// Create the sticky header view
this._createStickyHeaderView();
// Add it as an overlay to the parent
this._addStickyHeaderToParent();
// Add padding to ListView so content doesn't hide behind sticky header
this._addListViewPadding();
// Setup scroll listener to update header content
this._setupScrollListener();
}
_createStickyHeaderView() {
if (this._stickyHeaderView) {
return; // Already created
}
// Create header view using the same template as section headers
if (typeof this.stickyHeaderTemplate === 'string') {
try {
this._stickyHeaderView = Builder.parse(this.stickyHeaderTemplate, this);
}
catch (error) {
// Fallback to simple label
this._stickyHeaderView = new Label();
this._stickyHeaderView.text = 'Header Error';
}
}
if (!this._stickyHeaderView) {
// Default header
this._stickyHeaderView = new Label();
this._stickyHeaderView.text = 'Section 0';
}
// Set initial binding context (section 0)
this._updateStickyHeader(0);
}
_addStickyHeaderToParent() {
if (!this._stickyHeaderView || !this.parent) {
return;
}
// Remove from current parent if it has one (likely the ListView from Builder.parse)
if (this._stickyHeaderView.parent) {
this._stickyHeaderView.parent._removeView(this._stickyHeaderView);
}
// Set proper sizing - don't stretch to fill parent
this._stickyHeaderView.width = { unit: '%', value: 100 };
this._stickyHeaderView.height = 'auto'; // Let it size to content
this._stickyHeaderView.verticalAlignment = 'top';
this._stickyHeaderView.horizontalAlignment = 'stretch';
// Add sticky header to the parent layout
// If search view exists, position sticky header after it (index 1), otherwise at top (index 0)
const parentLayout = this.parent;
const hasSearchView = this.showSearch && this._searchView && this._searchView._wrapper;
if (parentLayout instanceof StackLayout) {
const insertIndex = hasSearchView ? 1 : 0;
parentLayout.insertChild(this._stickyHeaderView, insertIndex);
}
else {
parentLayout._addView(this._stickyHeaderView);
}
// When search is enabled, position sticky header below search view with proper top margin
if (this.showSearch && this._searchView) {
// Add top margin to push sticky header below search view
this._stickyHeaderView.marginTop = 0; // Reset any previous margin
// Position sticky header with proper offset using native positioning
if (this._stickyHeaderView.nativeViewProtected) {
this._stickyHeaderView.nativeViewProtected.setZ(STICKY_HEADER_Z_INDEX);
// Use a timeout to ensure search view is measured first
setTimeout(() => {
if (this._searchView && this._searchView._wrapper) {
const searchWrapper = this._searchView._wrapper;
if (searchWrapper.nativeViewProtected) {
const searchHeight = searchWrapper.nativeViewProtected.getMeasuredHeight() || 50;
// Position sticky header below search view using translation
if (this._stickyHeaderView.nativeViewProtected) {
this._stickyHeaderView.nativeViewProtected.setTranslationY(searchHeight);
}
}
}
}, 100);
}
}
else {
// No search view - position at top
if (this._stickyHeaderView.nativeViewProtected) {
this._stickyHeaderView.nativeViewProtected.setZ(STICKY_HEADER_Z_INDEX);
this._stickyHeaderView.nativeViewProtected.setTranslationY(0);
}
}
}
_addListViewPadding() {
if (!this._stickyHeaderView) {
return;
}
// Calculate total top padding: search view height + sticky header height
let searchViewHeight = 0;
if (this.showSearch && this._searchView && this._searchView._wrapper) {
const searchWrapper = this._searchView._wrapper;
if (searchWrapper.nativeViewProtected && searchWrapper.nativeViewProtected.getMeasuredHeight() > 0) {
searchViewHeight = searchWrapper.nativeViewProtected.getMeasuredHeight();
}
else {
searchViewHeight = 50; // Default search view height
}
}
// Apply immediate padding with defaults to prevent content hiding
const defaultHeaderHeight = 50; // Reasonable default height in dp
const totalPadding = searchViewHeight + defaultHeaderHeight;
this.nativeViewProtected.setPadding(0, totalPadding, 0, 0);
this._stickyHeaderHeight = defaultHeaderHeight;
// Request layout to ensure proper measurement
this._stickyHeaderView.requestLayout();
// Then measure and adjust padding if needed using a layout listener for determinism
const stickyHeaderNativeView = this._stickyHeaderView && this._stickyHeaderView.nativeViewProtected;
if (stickyHeaderNativeView) {
const layoutListener = new android.view.View.OnLayoutChangeListener({
onLayoutChange: (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) => {
if (v.getMeasuredHeight() > 0) {
const measuredHeaderHeight = v.getMeasuredHeight();
let finalSearchHeight = searchViewHeight;
// Re-measure search view if needed
if (this.showSearch && this._searchView && this._searchView._wrapper) {
const searchWrapper = this._searchView._wrapper;
if (searchWrapper.nativeViewProtected && searchWrapper.nativeViewProtected.getMeasuredHeight() > 0) {
finalSearchHeight = searchWrapper.nativeViewProtected.getMeasuredHeight();
}
}
// Calculate final padding: search height + sticky header height + small buffer
const totalPaddingHeight = finalSearchHeight + measuredHeaderHeight + 4;
this._stickyHeaderHeight = measuredHeaderHeight;
this.nativeViewProtected.setPadding(0, totalPaddingHeight, 0, 0);
this.scrollToIndex(0);
// Remove the listener after first valid layout
v.removeOnLayoutChangeListener(layoutListener);
}
},
});
stickyHeaderNativeView.addOnLayoutChangeListener(layoutListener);
}
}
_setupScrollListener() {
if (this._scrollListener) {
return; // Already setup
}
const owner = this;
this._scrollListener = new android.widget.AbsListView.OnScrollListener({
onScrollStateChanged(view, scrollState) {
// Not needed for sticky headers
},
onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount) {
if (owner.sectioned && owner._stickyHeaderView) {
const currentSection = owner._getCurrentSection(firstVisibleItem);
owner._updateStickyHeader(currentSection);
// Hide section headers when they would appear right below sticky header
owner._updateHiddenHeaders(firstVisibleItem);
}
},
});
this.nativeViewProtected.setOnScrollListener(this._scrollListener);
}
_getCurrentSection(firstVisibleItem) {
if (!this.sectioned) {
return 0;
}
// Convert the first visible list position to section number
let currentPosition = 0;
const sectionCount = this._getSectionCount();
for (let section = 0; section < sectionCount; section++) {
// Check if firstVisibleItem is in this section (header or items)
const sectionItems = this._getItemsInSection(section) || [];
const itemsInSection = sectionItems.length || 0;
const sectionEndPosition = currentPosition + 1 + itemsInSection; // +1 for header
if (firstVisibleItem < sectionEndPosition) {
return section;
}
currentPosition = sectionEndPosition;
}
return Math.max(0, sectionCount - 1); // Fallback to last section
}
_updateStickyHeader(section) {
if (!this._stickyHeaderView) {
return;
}
// Update binding context to match the current section
const sectionData = this._getSectionData(section);
if (sectionData) {
this._stickyHeaderView.bindingContext = sectionData;
}
else {
this._stickyHeaderView.bindingContext = { title: `Section ${section}`, section: section };
}
}
_updateHiddenHeaders(firstVisibleItem) {
const previousHiddenHeaders = new Set(this._hiddenHeaderPositions);
this._hiddenHeaderPositions.clear();
// If we're at the very top (first item is position 0, which is the first section header),
// hide that section header to avoid duplication with sticky header
if (firstVisibleItem === 0) {
this._hiddenHeaderPositions.add(0); // Hide the first section header position
}
// If hidden headers changed, refresh the adapter
const hiddenHeadersChanged = previousHiddenHeaders.size !== this._hiddenHeaderPositions.size || [...previousHiddenHeaders].some((pos) => !this._hiddenHeaderPositions.has(pos));
if (hiddenHeadersChanged) {
// Refresh adapter to update visibility
const adapter = this.nativeViewProtected.getAdapter();
if (adapter instanceof android.widget.BaseAdapter) {
adapter.notifyDataSetChanged();
}
}
}
[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);
}
if (this.nativeViewProtected) {
this.nativeViewProtected.setAdapter(new ListViewAdapterClass(this));
this.refresh();
}
}
// Sticky header property handlers
[stickyHeaderProperty.setNative](value) {
// Refresh adapter to handle sectioned vs non-sectioned display
if (this.nativeViewProtected && this.nativeViewProtected.getAdapter()) {
this.nativeViewProtected.setAdapter(new ListViewAdapterClass(this));
}
// Setup or cleanup sticky header
if (value && this.sectioned && this.stickyHeaderTemplate && this.isLoaded) {
this._setupStickyHeader();
}
else {
this._cleanupStickyHeader();
}
}
[stickyHeaderTemplateProperty.setNative](value) {
// Refresh adapter when template changes
if (this.nativeViewProtected && this.nativeViewProtected.getAdapter()) {
this.nativeViewProtected.setAdapter(new ListViewAdapterClass(this));
}
// Recreate sticky header with new template
this._cleanupStickyHeader();
if (value && this.stickyHeader && this.sectioned && this.isLoaded) {
this._setupStickyHeader();
}
}
[sectionedProperty.setNative](value) {
// Refresh adapter to handle sectioned vs non-sectioned data
if (this.nativeViewProtected && this.nativeViewProtected.getAdapter()) {
this.nativeViewProtected.setAdapter(new ListViewAdapterClass(this));
}
// Setup or cleanup sticky header based on sectioned state
if (value && this.stickyHeader && this.stickyHeaderTemplate && this.isLoaded) {
this._setupStickyHeader();
}
else {
this._cleanupStickyHeader();
}
}
// Search methods
_setupSearchView() {
if (this._searchView || !this.showSearch || !this.nativeViewProtected) {
return;
}
// Create SearchView using the ListView's context
this._searchView = new android.widget.SearchView(this.nativeViewProtected.getContext());
this._searchView.setQueryHint('Search...');
this._searchView.setIconifiedByDefault(false);
this._searchView.setSubmitButtonEnabled(false);
// Setup search listener
const owner = this;
this._searchListener = new android.widget.SearchView.OnQueryTextListener({
onQueryTextChange(newText) {
const args = {
eventName: SEARCHCHANGE,
object: owner,
text: newText,
android: owner._searchView,
};
owner.notify(args);
return true;
},
onQueryTextSubmit(query) {
const args = {
eventName: SEARCHCHANGE,
object: owner,
text: query,
android: owner._searchView,
};
owner.notify(args);
return true;
},
});
this._searchView.setOnQueryTextListener(this._searchListener);
// Add search view to the parent container above the ListView
this._addSearchToParent();
// Add padding to ListView if no sticky header (otherwise sticky header method handles it)
if (!this.stickyHeader || !this._stickyHeaderView) {
this._addSearchPadding();
}
}
_addSearchPadding() {
if (!this._searchView) {
return;
}
// Add basic padding for search view
const defaultSearchHeight = 50; // Default search view height
this.nativeViewProtected.setPadding(0, defaultSearchHeight, 0, 0);
// Measure and adjust if needed
setTimeout(() => {
if (this._searchView && this._searchView._wrapper) {
const searchWrapper = this._searchView._wrapper;
if (searchWrapper.nativeViewProtected && searchWrapper.nativeViewProtected.getMeasuredHeight() > 0) {
const measuredHeight = searchWrapper.nativeViewProtected.getMeasuredHeight();
this.nativeViewProtected.setPadding(0, measuredHeight + 4, 0, 0);
}
}
}, 100);
}
_addSearchToParent() {
if (!this._searchView || !this.parent) {
return;
}
// Get the parent layout
const parentLayout = this.parent;
// Create a simple NativeScript wrapper for the native SearchView
const searchView = this._searchView;
const searchViewWrapper = new (class extends View {
createNativeView() {
return searchView;
}
})();
// Set layout properties - ensure it's at the top
searchViewWrapper.height = 'auto';
searchViewWrapper.width = { unit: '%', value: 100 };
searchViewWrapper.verticalAlignment = 'top';
searchViewWrapper.horizontalAlignment = 'stretch';
// Always insert at position 0 (top) regardless of ListView position
if (parentLayout instanceof StackLayout) {
parentLayout.insertChild(searchViewWrapper, 0);
}
else {
// For other layout types, add as first child
parentLayout._addView(searchViewWrapper);
}
// Ensure search view appears above everything else
if (searchViewWrapper.nativeViewProtected) {
searchViewWrapper.nativeViewProtected.setZ(SEARCH_VIEW_Z_INDEX);
}
// Store reference for cleanup
this._searchView._wrapper = searchViewWrapper;
}
_cleanupSearchView() {
if (this._searchView) {
// Remove search view wrapper from parent
const wrapper = this._searchView._wrapper;
if (wrapper && wrapper.parent) {
wrapper.parent._removeView(wrapper);
}
// Clear listener
if (this._searchListener) {
this._searchView.setOnQueryTextListener(null);
this._searchListener = null;
}
this._searchView = null;
// Reset ListView padding if no sticky header
if (!this.stickyHeader || !this._stickyHeaderView) {
this.nativeViewProtected.setPadding(0, 0, 0, 0);
}
}
}
[showSearchProperty.setNative](value) {
if (value) {
if (this.isLoaded && this.nativeViewProtected && this.nativeViewProtected.getAdapter()) {
this._setupSearchView();
// Reposition sticky header if it exists
if (this._stickyHeaderView) {
this._repositionStickyHeader();
}
}
}
else {
this._cleanupSearchView();
// Reposition sticky header if it exists
if (this._stickyHeaderView) {
this._repositionStickyHeader();
}
}
}
_repositionStickyHeader() {
if (!this._stickyHeaderView || !this._stickyHeaderView.nativeViewProtected) {
return;
}
// Reset positioning
this._stickyHeaderView.nativeViewProtected.setTranslationY(0);
// If search is enabled, position below search view
if (this.showSearch && this._searchView && this._searchView._wrapper) {
setTimeout(() => {
const searchWrapper = this._searchView._wrapper;
if (searchWrapper.nativeViewProtected) {
const searchHeight = searchWrapper.nativeViewProtected.getMeasuredHeight() || 50;
this._stickyHeaderView.nativeViewProtected.setTranslationY(searchHeight);
}
}, 100);
}
// Update ListView padding
if (this.stickyHeader && this._stickyHeaderView) {
this._addListViewPadding();
}
}
}
__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 = (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 () {
if (!this.owner) {
return 0;
}
var count = 0;
if (this.owner.sectioned) {
var sectionCount = this.owner._getSectionCount();
if (!this.owner.items || sectionCount <= 0) {
return 0;
}
for (var i = 0; i < sectionCount; i++) {
var itemsInSection = this.owner._getItemsInSection(i) || [];
if (itemsInSection.length > 0) {
count += 1;
count += itemsInSection.length;
}
}
}
else {
var src = this.owner.items;
count = src && typeof src.length === "number" ? src.length : 0;
}
return Math.max(0, count);
};
ListViewAdapter.prototype.getItem = function (i) {
if (!this.owner || !this.owner.items) {
return null;
}
var totalCount = this.getCount();
if (i < 0 || i >= totalCount) {
return null;
}
if (this.owner.sectioned) {
var positionInfo = this._getPositionInfo(i);
if (positionInfo.isHeader) {
return this.owner._getSectionData(positionInfo.section);
}
else {
return this.owner._getDataItemInSection(positionInfo.section, positionInfo.itemIndex);
}
}
else {
var src = this.owner.items;
if (src && typeof src.length === "number" && i < src.length) {
var getItem = src.getItem;
return getItem ? getItem.call(src, i) : src[i];
}
}
return null;
};
ListViewAdapter.prototype._getPositionInfo = function (position) {
if (!this.owner.sectioned) {
return { isHeader: false, section: 0, itemIndex: position };
}
var currentPosition = 0;
var sectionCount = this.owner._getSectionCount();
for (var section = 0; section < sectionCount; section++) {
var itemsInSection = this.owner._getItemsInSection(section) || [];
if (itemsInSection.length === 0) {
continue;
}
if (currentPosition === position) {
return { isHeader: true, section: section, itemIndex: -1 };
}
currentPosition++;
if (position < currentPosition + itemsInSection.length) {
var itemIndex = position - currentPosition;
return { isHeader: false, section: section, itemIndex: itemIndex };
}
currentPosition += itemsInSection.length;
}
return { isHeader: false, section: 0, itemIndex: 0 };
};
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.isEnabled = function (position) {
var totalCount = this.getCount();
if (totalCount === 0 || position < 0 || position >= totalCount) {
return false;
}
if (this.owner.sectioned) {
var positionInfo = this._getPositionInfo(position);
return !positionInfo.isHeader;
}
return true;
};
ListViewAdapter.prototype.getViewTypeCount = function () {
var count = this.owner._itemTemplatesInternal.length;
if (this.owner.sectioned && this.owner.stickyHeaderTemplate) {
count += 1;
}
return count;
};
ListViewAdapter.prototype.getItemViewType = function (index) {
if (this.owner.sectioned) {
var positionInfo = this._getPositionInfo(index);
if (positionInfo.isHeader) {
return this.owner._itemTemplatesInternal.length;
}
else {
var template = this.owner._getItemTemplate(positionInfo.itemIndex);
return this.owner._itemTemplatesInternal.indexOf(template);
}
}
else {
var template = this.owner._getItemTemplate(index);
return this.owner._itemTemplatesInternal.indexOf(template);
}
};
ListViewAdapter.prototype.getView = function (index, convertView, parent) {
if (!this.owner) {
return null;
}
var totalCount = this.getCount();
if (index < 0 || index >= totalCount) {
var emptyView = new android.view.View(this.owner._context);
var layoutParams = new android.view.ViewGroup.LayoutParams(android.view.ViewGroup.LayoutParams.MATCH_PARENT, 0);
emptyView.setLayoutParams(layoutParams);
return emptyView;
}
if (index === totalCount - 1) {
this.owner.notify({
eventName: LOADMOREITEMS,
object: this.owner,
});
}
if (this.owner.sectioned) {
var positionInfo = this._getPositionInfo(index);
if (positionInfo.isHeader) {
return this._createHeaderView(positionInfo.section, convertView, parent, index);
}
else {
return this._createItemView(positionInfo.section, positionInfo.itemIndex, convertView, parent);
}
}
else {
return this._createItemView(0, index, convertView, parent);
}
};
ListViewAdapter.prototype._createHeaderView = function (section, convertView, parent, index) {
if (this.owner._hiddenHeaderPositions.has(index)) {
var emptyView = new android.view.View(this.owner._context);
var layoutParams = new android.view.ViewGroup.LayoutParams(android.view.ViewGroup.LayoutParams.MATCH_PARENT, 0);
emptyView.setLayoutParams(layoutParams);
return emptyView;
}
var headerView = null;
var headerViewType = this.owner._itemTemplatesInternal.length;
if (convertView) {
var existingData = this.owner._realizedItems.get(convertView);
if (existingData && existingData.templateKey === "header_".concat(headerViewType)) {
headerView = existingData.view;
}
}
if (!headerView) {
if (this.owner.stickyHeaderTemplate) {
if (typeof this.owner.stickyHeaderTemplate === "string") {
try {
headerView = Builder.parse(this.owner.stickyHeaderTemplate, this.owner);
}
catch (error) {
headerView = new Label();
headerView.text = "Header Error";
}
}
}
if (!headerView) {
headerView = new Label();
headerView.text = "Section ".concat(section);
}
if (!headerView.parent) {
if (headerView instanceof LayoutBase && !(headerView instanceof ProxyViewContainer)) {
this.owner._addView(headerView);
convertView = headerView.nativeViewProtected;
}
else {
var sp = new StackLayout();
sp.addChild(headerView);
this.owner._addView(sp);
convertView = sp.nativeViewProtected;
}
}
this.owner._realizedItems.set(convertView, {
view: headerView,
templateKey: "header_".concat(headerViewType),
});
}
var sectionData = this.owner._getSectionData(section);
if (sectionData) {
headerView.bindingContext = sectionData;
}
else {
headerView.bindingContext = { title: "Section ".concat(section), section: section };
}
return convertView;
};
ListViewAdapter.prototype._createItemView = function (section, itemIndex, convertView, parent) {
var template = this.owner._getItemTemplate(itemIndex);
var view;
if (convertView && this.owner._getKeyFromView(convertView) !== template.key) {
this.owner._markViewUnused(convertView);
convertView = this.owner._getAvailableView(template.key);
}
if (convertView) {
view = this.owner._realizedItems.get(convertView).view;
}
if (!view) {
view = template.createView();
}
var args = {
eventName: ITEMLOADING,
object: this.owner,
index: itemIndex,
view: view,
android: parent,
ios: undefined,
};
if (this.owner.sectioned) {
args.section = section;
}
this.owner.notify(args);
if (!args.view) {
args.view = this.owner._getDefaultItemContent(itemIndex);
}
if (args.view) {
if (this.owner._effectiveRowHeight > -1) {
args.view.height = this.owner.rowHeight;
}
else {
args.view.height = unsetValue;
}
if (this.owner.sectioned) {
args.view._listViewItemIndex = itemIndex;
args.view._listViewSectionIndex = section;
}
if (this.owner.sectioned) {
this.owner._prepareItemInSection(args.view, section, itemIndex);
}
else {
this.owner._prepareItem(args.view, itemIndex);
}
if (!args.view.parent) {
if (args.view instanceof LayoutBase && !(args.view instanceof ProxyViewContainer)) {
var mt = PercentLength.toDevicePixels(args.view.marginTop, 0, Number.NaN);
var mb = PercentLength.toDevicePixels(args.view.marginBottom, 0, Number.NaN);
var ml = PercentLength.toDevicePixels(args.view.marginLeft, 0, Number.NaN);
var mr = PercentLength.toDevicePixels(args.view.marginRight, 0, Number.NaN);
var hasMargins = mt > 0 || mb > 0 || ml > 0 || mr > 0;
if (hasMargins) {
var outer = new StackLayout();
outer.addChild(args.view);
this.owner._addView(outer);
convertView = outer.nativeViewProtected;
}
else {
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;
};
__decorate([
profile
], ListViewAdapter.prototype, "getView", null);
return ListViewAdapter;
}(android.widget.BaseAdapter));
ListViewAdapterClass = ListViewAdapter;
}
//# sourceMappingURL=index.android.js.map