mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
chore: addimg tabbable component android
This commit is contained in:
@@ -6,15 +6,10 @@ import { TextTransform } from "../text-base";
|
||||
|
||||
// Requires
|
||||
import * as application from "../../application";
|
||||
import { ImageSource } from "../../image-source";
|
||||
import { ad, isFontIconURI, layout, RESOURCE_PREFIX } from "../../utils/utils";
|
||||
import { ad, layout } from "../../utils/utils";
|
||||
import { Color, CSSType } from "../core/view";
|
||||
import { Frame, View } from "../frame";
|
||||
import { Font } from "../styling/font";
|
||||
import {
|
||||
getIconSpecSize, itemsProperty, selectedIndexProperty, TabNavigationBase, tabStripProperty
|
||||
} from "../tab-navigation-base/tab-navigation-base";
|
||||
import { getTransformedText } from "../text-base";
|
||||
import { View } from "../frame";
|
||||
import { TabbableComponent } from "../tabbable-component/tabbable-component.android";
|
||||
|
||||
// TODO: Impl trace
|
||||
// import { isEnabled as traceEnabled, write as traceWrite } from "../../../trace";
|
||||
@@ -36,11 +31,6 @@ let BottomNavigationBar: any;
|
||||
let AttachStateChangeListener: any;
|
||||
let appResources: android.content.res.Resources;
|
||||
|
||||
class IconInfo {
|
||||
drawable: android.graphics.drawable.BitmapDrawable;
|
||||
height: number;
|
||||
}
|
||||
|
||||
function makeFragmentName(viewId: number, id: number): string {
|
||||
return "android:bottomnavigation:" + viewId + ":" + id;
|
||||
}
|
||||
@@ -239,29 +229,16 @@ function setElevation(bottomNavigationBar: org.nativescript.widgets.BottomNaviga
|
||||
|
||||
export const tabs = new Array<WeakRef<BottomNavigation>>();
|
||||
|
||||
function iterateIndexRange(index: number, eps: number, lastIndex: number, callback: (i) => void) {
|
||||
const rangeStart = Math.max(0, index - eps);
|
||||
const rangeEnd = Math.min(index + eps, lastIndex);
|
||||
for (let i = rangeStart; i <= rangeEnd; i++) {
|
||||
callback(i);
|
||||
}
|
||||
}
|
||||
|
||||
@CSSType("BottomNavigation")
|
||||
export class BottomNavigation extends TabNavigationBase {
|
||||
export class BottomNavigation extends TabbableComponent {
|
||||
private _contentView: org.nativescript.widgets.ContentLayout;
|
||||
private _contentViewId: number = -1;
|
||||
private _bottomNavigationBar: org.nativescript.widgets.BottomNavigationBar;
|
||||
private _currentFragment: androidx.fragment.app.Fragment;
|
||||
private _currentTransaction: androidx.fragment.app.FragmentTransaction;
|
||||
private _attachedToWindow = false;
|
||||
public _originalBackground: any;
|
||||
private _textTransform: TextTransform = "none";
|
||||
private _selectedItemColor: Color;
|
||||
private _unSelectedItemColor: Color;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._textTransform = "none";
|
||||
tabs.push(new WeakRef(this));
|
||||
}
|
||||
|
||||
@@ -269,24 +246,16 @@ export class BottomNavigation extends TabNavigationBase {
|
||||
return true;
|
||||
}
|
||||
|
||||
public onItemsChanged(oldItems: TabContentItem[], newItems: TabContentItem[]): void {
|
||||
super.onItemsChanged(oldItems, newItems);
|
||||
public getBottomNavigationBar(): org.nativescript.widgets.BottomNavigationBar {
|
||||
return <org.nativescript.widgets.BottomNavigationBar>this.nativeBar;
|
||||
}
|
||||
|
||||
if (oldItems) {
|
||||
oldItems.forEach((item: TabContentItem, i, arr) => {
|
||||
(<any>item).index = 0;
|
||||
(<any>item).tabItemSpec = null;
|
||||
item.setNativeView(null);
|
||||
});
|
||||
}
|
||||
protected setNativeItems(items: Array<org.nativescript.widgets.TabItemSpec>) {
|
||||
this.getBottomNavigationBar().setItems(items);
|
||||
}
|
||||
|
||||
public createNativeView() {
|
||||
initializeNativeClasses();
|
||||
// if (traceEnabled()) {
|
||||
// traceWrite("BottomNavigation._createUI(" + this + ");", traceCategory);
|
||||
// }
|
||||
|
||||
const context: android.content.Context = this._context;
|
||||
const nativeView = new org.nativescript.widgets.GridLayout(context);
|
||||
|
||||
@@ -322,83 +291,40 @@ export class BottomNavigation extends TabNavigationBase {
|
||||
public initNativeView(): void {
|
||||
super.initNativeView();
|
||||
|
||||
if (this._contentViewId < 0) {
|
||||
this._contentViewId = android.view.View.generateViewId();
|
||||
}
|
||||
this.nativeViewProtected.addOnAttachStateChangeListener(AttachStateChangeListener);
|
||||
this.nativeViewProtected[ownerSymbol] = this;
|
||||
|
||||
const nativeView: any = this.nativeViewProtected;
|
||||
|
||||
nativeView.addOnAttachStateChangeListener(AttachStateChangeListener);
|
||||
nativeView[ownerSymbol] = this;
|
||||
|
||||
this._contentView = (<any>nativeView).contentView;
|
||||
this._contentView = (<any>this.nativeViewProtected).contentView;
|
||||
this._contentView.setId(this._contentViewId);
|
||||
|
||||
this._bottomNavigationBar = (<any>nativeView).bottomNavigationBar;
|
||||
(<any>this._bottomNavigationBar).owner = this;
|
||||
|
||||
if (this.tabStrip) {
|
||||
this.tabStrip.setNativeView(this._bottomNavigationBar);
|
||||
this.tabStrip.setNativeView(this.nativeBar);
|
||||
}
|
||||
}
|
||||
|
||||
public _loadUnloadTabItems(newIndex: number) {
|
||||
const items = this.items;
|
||||
const lastIndex = this.items.length - 1;
|
||||
const offsideItems = 0;
|
||||
|
||||
let toUnload = [];
|
||||
let toLoad = [];
|
||||
|
||||
iterateIndexRange(newIndex, offsideItems, lastIndex, (i) => toLoad.push(i));
|
||||
|
||||
items.forEach((item, i) => {
|
||||
const indexOfI = toLoad.indexOf(i);
|
||||
if (indexOfI < 0) {
|
||||
toUnload.push(i);
|
||||
}
|
||||
});
|
||||
|
||||
toUnload.forEach(index => {
|
||||
const item = items[index];
|
||||
if (items[index]) {
|
||||
item.unloadView(item.content);
|
||||
}
|
||||
});
|
||||
|
||||
const newItem = items[newIndex];
|
||||
const selectedView = newItem && newItem.content;
|
||||
if (selectedView instanceof Frame) {
|
||||
selectedView._pushInFrameStackRecursive();
|
||||
}
|
||||
|
||||
toLoad.forEach(index => {
|
||||
const item = items[index];
|
||||
if (this.isLoaded && items[index]) {
|
||||
item.loadView(item.content);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public onLoaded(): void {
|
||||
super.onLoaded();
|
||||
|
||||
if (this._originalBackground) {
|
||||
this.backgroundColor = null;
|
||||
this.backgroundColor = this._originalBackground;
|
||||
this._originalBackground = null;
|
||||
}
|
||||
|
||||
if (this.tabStrip) {
|
||||
this.setTabStripItems(this.tabStrip.items);
|
||||
} else {
|
||||
// manually set the visibility, so that the grid layout remeasures
|
||||
this._bottomNavigationBar.setVisibility(android.view.View.GONE);
|
||||
this.nativeBar.setVisibility(android.view.View.GONE);
|
||||
}
|
||||
|
||||
this.changeTab(this.selectedIndex);
|
||||
}
|
||||
|
||||
public onUnloaded(): void {
|
||||
super.onUnloaded();
|
||||
|
||||
const fragmentToDetach = this._currentFragment;
|
||||
if (fragmentToDetach) {
|
||||
this.destroyItem((<any>fragmentToDetach).index, fragmentToDetach);
|
||||
this.commitCurrentTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
_onAttachedToWindow(): void {
|
||||
super._onAttachedToWindow();
|
||||
|
||||
@@ -418,23 +344,9 @@ export class BottomNavigation extends TabNavigationBase {
|
||||
this._attachedToWindow = false;
|
||||
}
|
||||
|
||||
public onUnloaded(): void {
|
||||
super.onUnloaded();
|
||||
|
||||
if (this.tabStrip) {
|
||||
this.setTabStripItems(null);
|
||||
}
|
||||
|
||||
const fragmentToDetach = this._currentFragment;
|
||||
if (fragmentToDetach) {
|
||||
this.destroyItem((<any>fragmentToDetach).index, fragmentToDetach);
|
||||
this.commitCurrentTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
public disposeNativeView() {
|
||||
this._bottomNavigationBar.setItems(null);
|
||||
this._bottomNavigationBar = null;
|
||||
this.setNativeItems(null);
|
||||
this.nativeBar = null;
|
||||
|
||||
this.nativeViewProtected.removeOnAttachStateChangeListener(AttachStateChangeListener);
|
||||
this.nativeViewProtected[ownerSymbol] = null;
|
||||
@@ -442,26 +354,6 @@ export class BottomNavigation extends TabNavigationBase {
|
||||
super.disposeNativeView();
|
||||
}
|
||||
|
||||
public _onRootViewReset(): void {
|
||||
super._onRootViewReset();
|
||||
|
||||
// call this AFTER the super call to ensure descendants apply their rootview-reset logic first
|
||||
// i.e. in a scenario with tab frames let the frames cleanup their fragments first, and then
|
||||
// cleanup the tab fragments to avoid
|
||||
// android.content.res.Resources$NotFoundException: Unable to find resource ID #0xfffffff6
|
||||
this.disposeTabFragments();
|
||||
}
|
||||
|
||||
private disposeTabFragments(): void {
|
||||
const fragmentManager = this._getFragmentManager();
|
||||
const transaction = fragmentManager.beginTransaction();
|
||||
for (let fragment of (<Array<any>>fragmentManager.getFragments().toArray())) {
|
||||
transaction.remove(fragment);
|
||||
}
|
||||
|
||||
transaction.commitNowAllowingStateLoss();
|
||||
}
|
||||
|
||||
private get currentTransaction(): androidx.fragment.app.FragmentTransaction {
|
||||
if (!this._currentTransaction) {
|
||||
const fragmentManager = this._getFragmentManager();
|
||||
@@ -555,344 +447,11 @@ export class BottomNavigation extends TabNavigationBase {
|
||||
}
|
||||
}
|
||||
|
||||
private setTabStripItems(items: Array<TabStripItem>) {
|
||||
if (!this.tabStrip || !items) {
|
||||
this._bottomNavigationBar.setItems(null);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const tabItems = new Array<org.nativescript.widgets.TabItemSpec>();
|
||||
items.forEach((tabStripItem, i, arr) => {
|
||||
tabStripItem._index = i;
|
||||
if (items[i]) {
|
||||
const tabItemSpec = this.createTabItemSpec(items[i]);
|
||||
tabItems.push(tabItemSpec);
|
||||
}
|
||||
});
|
||||
|
||||
this._bottomNavigationBar.setItems(tabItems);
|
||||
|
||||
items.forEach((item, i, arr) => {
|
||||
const textView = this._bottomNavigationBar.getTextViewForItemAt(i);
|
||||
item.setNativeView(textView);
|
||||
this._setItemColor(item);
|
||||
});
|
||||
}
|
||||
|
||||
private getItemLabelTextTransform(tabStripItem: TabStripItem): TextTransform {
|
||||
const nestedLabel = tabStripItem.label;
|
||||
let textTransform: TextTransform = null;
|
||||
if (nestedLabel && nestedLabel.style.textTransform !== "initial") {
|
||||
textTransform = nestedLabel.style.textTransform;
|
||||
} else if (tabStripItem.style.textTransform !== "initial") {
|
||||
textTransform = tabStripItem.style.textTransform;
|
||||
}
|
||||
|
||||
return textTransform || this._textTransform;
|
||||
}
|
||||
|
||||
private createTabItemSpec(tabStripItem: TabStripItem): org.nativescript.widgets.TabItemSpec {
|
||||
const tabItemSpec = new org.nativescript.widgets.TabItemSpec();
|
||||
|
||||
if (tabStripItem.isLoaded) {
|
||||
const titleLabel = tabStripItem.label;
|
||||
let title = titleLabel.text;
|
||||
|
||||
// TEXT-TRANSFORM
|
||||
const textTransform = this.getItemLabelTextTransform(tabStripItem);
|
||||
title = getTransformedText(title, textTransform);
|
||||
tabItemSpec.title = title;
|
||||
|
||||
// BACKGROUND-COLOR
|
||||
const backgroundColor = tabStripItem.style.backgroundColor;
|
||||
tabItemSpec.backgroundColor = backgroundColor ? backgroundColor.android : this.getTabBarBackgroundArgbColor();
|
||||
|
||||
// COLOR
|
||||
let itemColor = this.selectedIndex === tabStripItem._index ? this._selectedItemColor : this._unSelectedItemColor;
|
||||
const color = itemColor || titleLabel.style.color;
|
||||
tabItemSpec.color = color && color.android;
|
||||
|
||||
// FONT
|
||||
const fontInternal = titleLabel.style.fontInternal;
|
||||
if (fontInternal) {
|
||||
tabItemSpec.fontSize = fontInternal.fontSize;
|
||||
tabItemSpec.typeFace = fontInternal.getAndroidTypeface();
|
||||
}
|
||||
|
||||
// ICON
|
||||
const iconSource = tabStripItem.image && tabStripItem.image.src;
|
||||
if (iconSource) {
|
||||
const iconInfo = this.getIconInfo(tabStripItem, itemColor);
|
||||
|
||||
if (iconInfo) {
|
||||
// TODO: Make this native call that accepts string so that we don't load Bitmap in JS.
|
||||
// tslint:disable-next-line:deprecation
|
||||
tabItemSpec.iconDrawable = iconInfo.drawable;
|
||||
tabItemSpec.imageHeight = iconInfo.height;
|
||||
} else {
|
||||
// TODO:
|
||||
// traceMissingIcon(iconSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tabItemSpec;
|
||||
}
|
||||
|
||||
private getOriginalIcon(tabStripItem: TabStripItem, color?: Color): android.graphics.Bitmap {
|
||||
const iconSource = tabStripItem.image && tabStripItem.image.src;
|
||||
if (!iconSource) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let is: ImageSource;
|
||||
if (isFontIconURI(iconSource)) {
|
||||
const fontIconCode = iconSource.split("//")[1];
|
||||
const target = tabStripItem.image ? tabStripItem.image : tabStripItem;
|
||||
const font = target.style.fontInternal;
|
||||
if (!color) {
|
||||
color = target.style.color;
|
||||
}
|
||||
is = ImageSource.fromFontIconCodeSync(fontIconCode, font, color);
|
||||
} else {
|
||||
is = ImageSource.fromFileOrResourceSync(iconSource);
|
||||
}
|
||||
|
||||
return is && is.android;
|
||||
}
|
||||
|
||||
private getDrawableInfo(image: android.graphics.Bitmap): IconInfo {
|
||||
if (image) {
|
||||
if (this.tabStrip && this.tabStrip.isIconSizeFixed) {
|
||||
image = this.getFixedSizeIcon(image);
|
||||
}
|
||||
|
||||
let imageDrawable = new android.graphics.drawable.BitmapDrawable(application.android.context.getResources(), image);
|
||||
|
||||
return {
|
||||
drawable: imageDrawable,
|
||||
height: image.getHeight()
|
||||
};
|
||||
}
|
||||
|
||||
return new IconInfo();
|
||||
}
|
||||
|
||||
private getIconInfo(tabStripItem: TabStripItem, color?: Color): IconInfo {
|
||||
let originalIcon = this.getOriginalIcon(tabStripItem, color);
|
||||
|
||||
return this.getDrawableInfo(originalIcon);
|
||||
}
|
||||
|
||||
private getFixedSizeIcon(image: android.graphics.Bitmap): android.graphics.Bitmap {
|
||||
const inWidth = image.getWidth();
|
||||
const inHeight = image.getHeight();
|
||||
|
||||
const iconSpecSize = getIconSpecSize({ width: inWidth, height: inHeight });
|
||||
|
||||
const widthPixels = iconSpecSize.width * layout.getDisplayDensity();
|
||||
const heightPixels = iconSpecSize.height * layout.getDisplayDensity();
|
||||
|
||||
const scaledImage = android.graphics.Bitmap.createScaledBitmap(image, widthPixels, heightPixels, true);
|
||||
|
||||
return scaledImage;
|
||||
}
|
||||
|
||||
public updateAndroidItemAt(index: number, spec: org.nativescript.widgets.TabItemSpec) {
|
||||
this._bottomNavigationBar.updateItemAt(index, spec);
|
||||
}
|
||||
|
||||
public getTabBarBackgroundColor(): android.graphics.drawable.Drawable {
|
||||
return this._bottomNavigationBar.getBackground();
|
||||
}
|
||||
|
||||
public setTabBarBackgroundColor(value: android.graphics.drawable.Drawable | Color): void {
|
||||
if (value instanceof Color) {
|
||||
this._bottomNavigationBar.setBackgroundColor(value.android);
|
||||
} else {
|
||||
this._bottomNavigationBar.setBackground(tryCloneDrawable(value, this.nativeViewProtected.getResources()));
|
||||
}
|
||||
|
||||
this.updateTabStripItems();
|
||||
}
|
||||
|
||||
private updateTabStripItems(): void {
|
||||
this.tabStrip.items.forEach((tabStripItem: TabStripItem) => {
|
||||
if (tabStripItem.nativeView) {
|
||||
const tabItemSpec = this.createTabItemSpec(tabStripItem);
|
||||
this.updateAndroidItemAt(tabStripItem._index, tabItemSpec);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public _setItemsColors(items: Array<TabStripItem>): void {
|
||||
items.forEach((item) => {
|
||||
if (item.nativeView) {
|
||||
this._setItemColor(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public getTabBarSelectedItemColor(): Color {
|
||||
return this._selectedItemColor;
|
||||
}
|
||||
|
||||
public setTabBarSelectedItemColor(value: Color) {
|
||||
this._selectedItemColor = value;
|
||||
this._setItemsColors(this.tabStrip.items);
|
||||
}
|
||||
|
||||
public getTabBarUnSelectedItemColor(): Color {
|
||||
return this._unSelectedItemColor;
|
||||
}
|
||||
|
||||
public setTabBarUnSelectedItemColor(value: Color) {
|
||||
this._unSelectedItemColor = value;
|
||||
this._setItemsColors(this.tabStrip.items);
|
||||
}
|
||||
|
||||
private updateItem(tabStripItem: TabStripItem): void {
|
||||
// TODO: Should figure out a way to do it directly with the the nativeView
|
||||
const tabStripItemIndex = this.tabStrip.items.indexOf(tabStripItem);
|
||||
const tabItemSpec = this.createTabItemSpec(tabStripItem);
|
||||
this.updateAndroidItemAt(tabStripItemIndex, tabItemSpec);
|
||||
}
|
||||
|
||||
public setTabBarItemTitle(tabStripItem: TabStripItem, value: string): void {
|
||||
this.updateItem(tabStripItem);
|
||||
}
|
||||
|
||||
public setTabBarItemBackgroundColor(tabStripItem: TabStripItem, value: android.graphics.drawable.Drawable | Color): void {
|
||||
this.updateItem(tabStripItem);
|
||||
}
|
||||
|
||||
public _setItemColor(tabStripItem: TabStripItem) {
|
||||
const itemColor = (tabStripItem._index === this.selectedIndex) ? this._selectedItemColor : this._unSelectedItemColor;
|
||||
if (!itemColor) {
|
||||
return;
|
||||
}
|
||||
|
||||
// set label color
|
||||
tabStripItem.nativeViewProtected.setTextColor(itemColor.android);
|
||||
|
||||
// set icon color
|
||||
this.setIconColor(tabStripItem, itemColor);
|
||||
}
|
||||
|
||||
private setIconColor(tabStripItem: TabStripItem, color?: Color) {
|
||||
const tabBarItem = this._bottomNavigationBar.getViewForItemAt(tabStripItem._index);
|
||||
|
||||
const drawableInfo = this.getIconInfo(tabStripItem, color);
|
||||
const imgView = <android.widget.ImageView>tabBarItem.getChildAt(0);
|
||||
imgView.setImageDrawable(drawableInfo.drawable);
|
||||
if (color) {
|
||||
imgView.setColorFilter(color.android);
|
||||
}
|
||||
}
|
||||
|
||||
public setTabBarItemColor(tabStripItem: TabStripItem, value: number | Color): void {
|
||||
const itemColor = (tabStripItem._index === this.selectedIndex) ? this._selectedItemColor : this._unSelectedItemColor;
|
||||
if (itemColor) {
|
||||
// the itemColor is set through the selectedItemColor and unSelectedItemColor properties
|
||||
// so it does not respect the css color
|
||||
return;
|
||||
}
|
||||
|
||||
const androidColor = value instanceof Color ? value.android : value;
|
||||
tabStripItem.nativeViewProtected.setTextColor(androidColor);
|
||||
}
|
||||
|
||||
public setTabBarIconColor(tabStripItem: TabStripItem, value: number | Color): void {
|
||||
const itemColor = (tabStripItem._index === this.selectedIndex) ? this._selectedItemColor : this._unSelectedItemColor;
|
||||
if (itemColor) {
|
||||
// the itemColor is set through the selectedItemColor and unSelectedItemColor properties
|
||||
// so it does not respect the css color
|
||||
return;
|
||||
}
|
||||
|
||||
this.setIconColor(tabStripItem);
|
||||
}
|
||||
|
||||
public setTabBarIconSource(tabStripItem: TabStripItem, value: number | Color): void {
|
||||
this.updateItem(tabStripItem);
|
||||
}
|
||||
|
||||
public setTabBarItemFontInternal(tabStripItem: TabStripItem, value: Font): void {
|
||||
if (value.fontSize) {
|
||||
tabStripItem.nativeViewProtected.setTextSize(value.fontSize);
|
||||
}
|
||||
|
||||
tabStripItem.nativeViewProtected.setTypeface(value.getAndroidTypeface());
|
||||
}
|
||||
|
||||
public setTabBarItemTextTransform(tabStripItem: TabStripItem, value: TextTransform): void {
|
||||
const titleLabel = tabStripItem.label;
|
||||
const title = getTransformedText(titleLabel.text, value);
|
||||
tabStripItem.nativeViewProtected.setText(title);
|
||||
}
|
||||
|
||||
public getTabBarTextTransform(): TextTransform {
|
||||
return this._textTransform;
|
||||
}
|
||||
|
||||
public setTabBarTextTransform(value: TextTransform): void {
|
||||
let items = this.tabStrip && this.tabStrip.items;
|
||||
if (items) {
|
||||
items.forEach((tabStripItem) => {
|
||||
if (tabStripItem.label && tabStripItem.nativeViewProtected) {
|
||||
const nestedLabel = tabStripItem.label;
|
||||
const title = getTransformedText(nestedLabel.text, value);
|
||||
tabStripItem.nativeViewProtected.setText(title);
|
||||
}
|
||||
});
|
||||
}
|
||||
this._textTransform = value;
|
||||
}
|
||||
|
||||
[selectedIndexProperty.setNative](value: number) {
|
||||
// const smoothScroll = false;
|
||||
|
||||
// if (traceEnabled()) {
|
||||
// traceWrite("TabView this._viewPager.setCurrentItem(" + value + ", " + smoothScroll + ");", traceCategory);
|
||||
// }
|
||||
|
||||
protected setSelectedItem(value: number) {
|
||||
if (this.tabStrip) {
|
||||
this._bottomNavigationBar.setSelectedPosition(value);
|
||||
this.getBottomNavigationBar().setSelectedPosition(value);
|
||||
} else {
|
||||
this.changeTab(value);
|
||||
}
|
||||
}
|
||||
|
||||
[itemsProperty.getDefault](): TabContentItem[] {
|
||||
return null;
|
||||
}
|
||||
[itemsProperty.setNative](value: TabContentItem[]) {
|
||||
if (value) {
|
||||
value.forEach((item: TabContentItem, i) => {
|
||||
(<any>item).index = i;
|
||||
});
|
||||
}
|
||||
|
||||
selectedIndexProperty.coerce(this);
|
||||
}
|
||||
|
||||
[tabStripProperty.getDefault](): TabStrip {
|
||||
return null;
|
||||
}
|
||||
[tabStripProperty.setNative](value: TabStrip) {
|
||||
const items = this.tabStrip ? this.tabStrip.items : null;
|
||||
this.setTabStripItems(items);
|
||||
}
|
||||
}
|
||||
|
||||
function tryCloneDrawable(value: android.graphics.drawable.Drawable, resources: android.content.res.Resources): android.graphics.drawable.Drawable {
|
||||
if (value) {
|
||||
const constantState = value.getConstantState();
|
||||
if (constantState) {
|
||||
return constantState.newDrawable(resources);
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @module "ui/tab-navigation/bottom-navigation"
|
||||
*/ /** */
|
||||
|
||||
import { TabNavigationBase } from "../tab-navigation-base/tab-navigation-base";
|
||||
import { TabbableComponent } from "../tabbable-component";
|
||||
import { TabContentItem } from "../tab-navigation-base/tab-content-item";
|
||||
import { TabStrip } from "../tab-navigation-base/tab-strip";
|
||||
import { Property, CoercibleProperty, EventData } from "../core/view";
|
||||
@@ -31,7 +31,7 @@ export interface SelectedIndexChangedEventData extends EventData {
|
||||
/**
|
||||
* Represents a tab navigation widget with static tabs at the bottom.
|
||||
*/
|
||||
export class BottomNavigation extends TabNavigationBase {
|
||||
export class BottomNavigation extends TabbableComponent {
|
||||
/**
|
||||
* Gets or sets the items of the BottomNavigation.
|
||||
*/
|
||||
|
||||
1
nativescript-core/ui/index.d.ts
vendored
1
nativescript-core/ui/index.d.ts
vendored
@@ -29,6 +29,7 @@ export { SegmentedBar, SegmentedBarItem } from "./segmented-bar";
|
||||
export { Slider } from "./slider";
|
||||
export { Style, CommonLayoutParams } from "./styling/style";
|
||||
export { Switch } from "./switch";
|
||||
export { TabbableComponent } from "./tabbable-component";
|
||||
export { TabContentItem } from "./tab-navigation-base/tab-content-item";
|
||||
export { TabNavigationBase } from "./tab-navigation-base/tab-navigation-base";
|
||||
export { TabStrip, TabStripItemEventData } from "./tab-navigation-base/tab-strip";
|
||||
|
||||
@@ -56,6 +56,7 @@ export { Style, CommonLayoutParams } from "./styling/style";
|
||||
export { Switch } from "./switch";
|
||||
export { TabContentItem } from "./tab-navigation-base/tab-content-item";
|
||||
export { TabNavigationBase } from "./tab-navigation-base/tab-navigation-base";
|
||||
export { TabbableComponent } from "./tabbable-component";
|
||||
export { TabStrip, TabStripItemEventData } from "./tab-navigation-base/tab-strip";
|
||||
export { TabStripItem } from "./tab-navigation-base/tab-strip-item";
|
||||
export { TabView, TabViewItem } from "./tab-view";
|
||||
|
||||
5
nativescript-core/ui/tabbable-component/package.json
Normal file
5
nativescript-core/ui/tabbable-component/package.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "tabbable-component",
|
||||
"main": "tabbable-component",
|
||||
"types": "tabbable-component.d.ts"
|
||||
}
|
||||
@@ -0,0 +1,486 @@
|
||||
// Types
|
||||
import { TabNavigationBase, getIconSpecSize } from "../tab-navigation-base/tab-navigation-base";
|
||||
import { TextTransform, getTransformedText } from "../text-base/text-base";
|
||||
import { Color } from "../core/view/view";
|
||||
import { Frame } from "../frame";
|
||||
import { Font } from "../styling/font";
|
||||
import { ImageSource } from "../../image-source/image-source";
|
||||
import { isFontIconURI, layout } from "../../utils/utils";
|
||||
import * as application from "../../application/application";
|
||||
import { TabContentItem } from "../tab-navigation-base/tab-content-item";
|
||||
import { TabStrip } from "../tab-navigation-base/tab-strip";
|
||||
import { TabStripItem } from "../tab-navigation-base/tab-strip-item";
|
||||
import { itemsProperty, selectedIndexProperty, tabStripProperty } from "../tab-navigation-base/tab-navigation-base";
|
||||
|
||||
class IconInfo {
|
||||
drawable: android.graphics.drawable.BitmapDrawable;
|
||||
height: number;
|
||||
}
|
||||
|
||||
export abstract class TabbableComponent extends TabNavigationBase {
|
||||
protected nativeBar: org.nativescript.widgets.TabbableBar;
|
||||
public _originalBackground: any;
|
||||
protected _textTransform: TextTransform = "uppercase";
|
||||
protected _selectedItemColor: Color;
|
||||
protected _unSelectedItemColor: Color;
|
||||
protected _offscreenTabLimit: number;
|
||||
protected _contentViewId: number = -1;
|
||||
|
||||
protected abstract setNativeItems(items: Array<org.nativescript.widgets.TabItemSpec>);
|
||||
protected abstract setSelectedItem(value: number);
|
||||
|
||||
public onLoaded(): void {
|
||||
super.onLoaded();
|
||||
|
||||
if (this._originalBackground) {
|
||||
this.backgroundColor = null;
|
||||
this.backgroundColor = this._originalBackground;
|
||||
this._originalBackground = null;
|
||||
}
|
||||
}
|
||||
|
||||
public onUnloaded(): void {
|
||||
super.onUnloaded();
|
||||
|
||||
this.setItems(null);
|
||||
if (this.tabStrip) {
|
||||
this.setTabStripItems(null);
|
||||
}
|
||||
}
|
||||
|
||||
public initNativeView(): void {
|
||||
super.initNativeView();
|
||||
if (this._contentViewId < 0) {
|
||||
this._contentViewId = android.view.View.generateViewId();
|
||||
}
|
||||
|
||||
const nativeView: any = this.nativeViewProtected;
|
||||
this.nativeBar = (<any>nativeView).bottomNavigationBar;
|
||||
|
||||
(<any>this.nativeBar).owner = this;
|
||||
}
|
||||
|
||||
protected getOffscreenTabLimit(): number {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public _loadUnloadTabItems(newIndex: number) {
|
||||
const items = this.items;
|
||||
if (!items) {
|
||||
return;
|
||||
}
|
||||
|
||||
const lastIndex = items.length - 1;
|
||||
const offsideItems = this.getOffscreenTabLimit();
|
||||
|
||||
let toUnload = [];
|
||||
let toLoad = [];
|
||||
|
||||
iterateIndexRange(newIndex, offsideItems, lastIndex, (i) => toLoad.push(i));
|
||||
|
||||
items.forEach((item, i) => {
|
||||
const indexOfI = toLoad.indexOf(i);
|
||||
if (indexOfI < 0) {
|
||||
toUnload.push(i);
|
||||
}
|
||||
});
|
||||
|
||||
toUnload.forEach(index => {
|
||||
const item = items[index];
|
||||
if (items[index]) {
|
||||
item.unloadView(item.content);
|
||||
}
|
||||
});
|
||||
|
||||
const newItem = items[newIndex];
|
||||
const selectedView = newItem && newItem.content;
|
||||
if (selectedView instanceof Frame) {
|
||||
(<Frame>selectedView)._pushInFrameStackRecursive();
|
||||
}
|
||||
|
||||
toLoad.forEach(index => {
|
||||
const item = items[index];
|
||||
if (this.isLoaded && items[index]) {
|
||||
item.loadView(item.content);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public _onRootViewReset(): void {
|
||||
super._onRootViewReset();
|
||||
|
||||
// call this AFTER the super call to ensure descendants apply their rootview-reset logic first
|
||||
// i.e. in a scenario with tab frames let the frames cleanup their fragments first, and then
|
||||
// cleanup the tab fragments to avoid
|
||||
// android.content.res.Resources$NotFoundException: Unable to find resource ID #0xfffffff6
|
||||
this.disposeCurrentFragments();
|
||||
}
|
||||
|
||||
private disposeCurrentFragments(): void {
|
||||
const fragmentManager = this._getFragmentManager();
|
||||
const transaction = fragmentManager.beginTransaction();
|
||||
for (let fragment of (<Array<any>>fragmentManager.getFragments().toArray())) {
|
||||
transaction.remove(fragment);
|
||||
}
|
||||
transaction.commitNowAllowingStateLoss();
|
||||
}
|
||||
|
||||
public onItemsChanged(oldItems: TabContentItem[], newItems: TabContentItem[]): void {
|
||||
super.onItemsChanged(oldItems, newItems);
|
||||
|
||||
if (oldItems) {
|
||||
oldItems.forEach((item: TabContentItem, i, arr) => {
|
||||
(<any>item).index = 0;
|
||||
(<any>item).tabItemSpec = null;
|
||||
item.setNativeView(null);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected setItems(items: Array<TabContentItem>) {
|
||||
//overriden
|
||||
}
|
||||
|
||||
protected setTabStripItems(items: Array<TabStripItem>) {
|
||||
if (!this.tabStrip || !items) {
|
||||
this.setNativeItems(null);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const tabItems = new Array<org.nativescript.widgets.TabItemSpec>();
|
||||
items.forEach((tabStripItem, i) => {
|
||||
tabStripItem._index = i;
|
||||
const tabItemSpec = this.createTabItemSpec(tabStripItem);
|
||||
(<any>tabStripItem).tabItemSpec = tabItemSpec;
|
||||
tabItems.push(tabItemSpec);
|
||||
});
|
||||
|
||||
this.setNativeItems(tabItems);
|
||||
this.tabStrip.setNativeView(this.nativeBar);
|
||||
items.forEach((item, i, arr) => {
|
||||
const textView = this.nativeBar.getTextViewForItemAt(i);
|
||||
item.setNativeView(textView);
|
||||
this._setItemColor(item);
|
||||
});
|
||||
}
|
||||
|
||||
public _setItemsColors(items: Array<TabStripItem>): void {
|
||||
items.forEach((item) => {
|
||||
if (item.nativeView) {
|
||||
this._setItemColor(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public setTabBarItemColor(tabStripItem: TabStripItem, value: number | Color): void {
|
||||
const itemColor = (tabStripItem._index === this.selectedIndex) ? this._selectedItemColor : this._unSelectedItemColor;
|
||||
if (itemColor) {
|
||||
// the itemColor is set through the selectedItemColor and unSelectedItemColor properties
|
||||
// so it does not respect the css color
|
||||
return;
|
||||
}
|
||||
|
||||
const androidColor = value instanceof Color ? value.android : value;
|
||||
tabStripItem.nativeViewProtected.setTextColor(androidColor);
|
||||
}
|
||||
|
||||
public setTabBarIconColor(tabStripItem: TabStripItem, value: number | Color): void {
|
||||
const itemColor = (tabStripItem._index === this.selectedIndex) ? this._selectedItemColor : this._unSelectedItemColor;
|
||||
if (itemColor) {
|
||||
// the itemColor is set through the selectedItemColor and unSelectedItemColor properties
|
||||
// so it does not respect the css color
|
||||
return;
|
||||
}
|
||||
|
||||
this.setIconColor(tabStripItem);
|
||||
}
|
||||
|
||||
public setTabBarItemFontInternal(tabStripItem: TabStripItem, value: Font): void {
|
||||
if (value.fontSize) {
|
||||
tabStripItem.nativeViewProtected.setTextSize(value.fontSize);
|
||||
}
|
||||
tabStripItem.nativeViewProtected.setTypeface(value.getAndroidTypeface());
|
||||
}
|
||||
|
||||
public setTabBarItemTextTransform(tabStripItem: TabStripItem, value: TextTransform): void {
|
||||
const nestedLabel = tabStripItem.label;
|
||||
const title = getTransformedText(nestedLabel.text, value);
|
||||
tabStripItem.nativeViewProtected.setText(title);
|
||||
}
|
||||
|
||||
public getTabBarTextTransform(): TextTransform {
|
||||
return this._textTransform;
|
||||
}
|
||||
|
||||
public setTabBarTextTransform(value: TextTransform): void {
|
||||
let items = this.tabStrip && this.tabStrip.items;
|
||||
if (items) {
|
||||
items.forEach((tabStripItem) => {
|
||||
if (tabStripItem.label && tabStripItem.nativeViewProtected) {
|
||||
const nestedLabel = tabStripItem.label;
|
||||
const title = getTransformedText(nestedLabel.text, value);
|
||||
tabStripItem.nativeViewProtected.setText(title);
|
||||
}
|
||||
});
|
||||
}
|
||||
this._textTransform = value;
|
||||
}
|
||||
|
||||
public getTabBarSelectedItemColor(): Color {
|
||||
return this._selectedItemColor;
|
||||
}
|
||||
|
||||
public setTabBarSelectedItemColor(value: Color) {
|
||||
this._selectedItemColor = value;
|
||||
this._setItemsColors(this.tabStrip.items);
|
||||
}
|
||||
|
||||
public getTabBarUnSelectedItemColor(): Color {
|
||||
return this._unSelectedItemColor;
|
||||
}
|
||||
|
||||
public setTabBarUnSelectedItemColor(value: Color) {
|
||||
this._unSelectedItemColor = value;
|
||||
this._setItemsColors(this.tabStrip.items);
|
||||
}
|
||||
|
||||
protected updateItem(tabStripItem: TabStripItem): void {
|
||||
// TODO: Should figure out a way to do it directly with the the nativeView
|
||||
const tabStripItemIndex = this.tabStrip.items.indexOf(tabStripItem);
|
||||
const tabItemSpec = this.createTabItemSpec(tabStripItem);
|
||||
this.updateAndroidItemAt(tabStripItemIndex, tabItemSpec);
|
||||
}
|
||||
|
||||
public setTabBarItemTitle(tabStripItem: TabStripItem, value: string): void {
|
||||
this.updateItem(tabStripItem);
|
||||
}
|
||||
|
||||
public setTabBarItemBackgroundColor(tabStripItem: TabStripItem, value: android.graphics.drawable.Drawable | Color): void {
|
||||
this.updateItem(tabStripItem);
|
||||
}
|
||||
|
||||
public setTabBarIconSource(tabStripItem: TabStripItem, value: number | Color): void {
|
||||
this.updateItem(tabStripItem);
|
||||
}
|
||||
|
||||
public updateAndroidItemAt(index: number, spec: org.nativescript.widgets.TabItemSpec) {
|
||||
this.nativeBar.updateItemAt(index, spec);
|
||||
}
|
||||
|
||||
public getTabBarBackgroundColor(): android.graphics.drawable.Drawable {
|
||||
return this.nativeBar.getBackground();
|
||||
}
|
||||
|
||||
public setTabBarBackgroundColor(value: android.graphics.drawable.Drawable | Color): void {
|
||||
if (value instanceof Color) {
|
||||
this.nativeBar.setBackgroundColor(value.android);
|
||||
} else {
|
||||
this.nativeBar.setBackground(tryCloneDrawable(value, this.nativeViewProtected.getResources()));
|
||||
}
|
||||
|
||||
this.updateTabStripItems();
|
||||
}
|
||||
|
||||
private updateTabStripItems(): void {
|
||||
this.tabStrip.items.forEach((tabStripItem: TabStripItem) => {
|
||||
if (tabStripItem.nativeView) {
|
||||
const tabItemSpec = this.createTabItemSpec(tabStripItem);
|
||||
this.updateAndroidItemAt(tabStripItem._index, tabItemSpec);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public _setItemColor(tabStripItem: TabStripItem) {
|
||||
const itemColor = (tabStripItem._index === this.selectedIndex) ? this._selectedItemColor : this._unSelectedItemColor;
|
||||
if (!itemColor) {
|
||||
return;
|
||||
}
|
||||
|
||||
// set label color
|
||||
tabStripItem.nativeViewProtected.setTextColor(itemColor.android);
|
||||
|
||||
// set icon color
|
||||
this.setIconColor(tabStripItem, itemColor);
|
||||
}
|
||||
|
||||
protected setIconColor(tabStripItem: TabStripItem, color?: Color) {
|
||||
const tabBarItem = this.nativeBar.getViewForItemAt(tabStripItem._index);
|
||||
|
||||
const drawableInfo = this.getIconInfo(tabStripItem, color);
|
||||
const imgView = <android.widget.ImageView>tabBarItem.getChildAt(0);
|
||||
imgView.setImageDrawable(drawableInfo.drawable);
|
||||
if (color) {
|
||||
imgView.setColorFilter(color.android);
|
||||
}
|
||||
}
|
||||
|
||||
protected createTabItemSpec(tabStripItem: TabStripItem): org.nativescript.widgets.TabItemSpec {
|
||||
const tabItemSpec = new org.nativescript.widgets.TabItemSpec();
|
||||
|
||||
if (tabStripItem.isLoaded) {
|
||||
const titleLabel = tabStripItem.label;
|
||||
let title = titleLabel.text;
|
||||
|
||||
// TEXT-TRANSFORM
|
||||
const textTransform = this.getItemLabelTextTransform(tabStripItem);
|
||||
title = getTransformedText(title, textTransform);
|
||||
tabItemSpec.title = title;
|
||||
|
||||
// BACKGROUND-COLOR
|
||||
const backgroundColor = tabStripItem.style.backgroundColor;
|
||||
tabItemSpec.backgroundColor = backgroundColor ? backgroundColor.android : this.getTabBarBackgroundArgbColor();
|
||||
|
||||
// COLOR
|
||||
let itemColor = this.selectedIndex === tabStripItem._index ? this._selectedItemColor : this._unSelectedItemColor;
|
||||
const color = itemColor || titleLabel.style.color;
|
||||
tabItemSpec.color = color && color.android;
|
||||
|
||||
// FONT
|
||||
const fontInternal = titleLabel.style.fontInternal;
|
||||
if (fontInternal) {
|
||||
tabItemSpec.fontSize = fontInternal.fontSize;
|
||||
tabItemSpec.typeFace = fontInternal.getAndroidTypeface();
|
||||
}
|
||||
|
||||
// ICON
|
||||
const iconSource = tabStripItem.image && tabStripItem.image.src;
|
||||
if (iconSource) {
|
||||
const iconInfo = this.getIconInfo(tabStripItem, itemColor);
|
||||
|
||||
if (iconInfo) {
|
||||
// TODO: Make this native call that accepts string so that we don't load Bitmap in JS.
|
||||
// tslint:disable-next-line:deprecation
|
||||
tabItemSpec.iconDrawable = iconInfo.drawable;
|
||||
tabItemSpec.imageHeight = iconInfo.height;
|
||||
} else {
|
||||
// TODO:
|
||||
// traceMissingIcon(iconSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tabItemSpec;
|
||||
}
|
||||
|
||||
public getTabBarItemTextTransform(tabStripItem: TabStripItem): TextTransform {
|
||||
return this.getItemLabelTextTransform(tabStripItem);
|
||||
}
|
||||
|
||||
private getItemLabelTextTransform(tabStripItem: TabStripItem): TextTransform {
|
||||
const nestedLabel = tabStripItem.label;
|
||||
let textTransform: TextTransform = null;
|
||||
if (nestedLabel && nestedLabel.style.textTransform !== "initial") {
|
||||
textTransform = nestedLabel.style.textTransform;
|
||||
} else if (tabStripItem.style.textTransform !== "initial") {
|
||||
textTransform = tabStripItem.style.textTransform;
|
||||
}
|
||||
|
||||
return textTransform || this._textTransform;
|
||||
}
|
||||
|
||||
private getOriginalIcon(tabStripItem: TabStripItem, color?: Color): android.graphics.Bitmap {
|
||||
const iconSource = tabStripItem.image && tabStripItem.image.src;
|
||||
if (!iconSource) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let is: ImageSource;
|
||||
if (isFontIconURI(iconSource)) {
|
||||
const fontIconCode = iconSource.split("//")[1];
|
||||
const target = tabStripItem.image ? tabStripItem.image : tabStripItem;
|
||||
const font = target.style.fontInternal;
|
||||
if (!color) {
|
||||
color = target.style.color;
|
||||
}
|
||||
is = ImageSource.fromFontIconCodeSync(fontIconCode, font, color);
|
||||
} else {
|
||||
is = ImageSource.fromFileOrResourceSync(iconSource);
|
||||
}
|
||||
|
||||
return is && is.android;
|
||||
}
|
||||
|
||||
private getFixedSizeIcon(image: android.graphics.Bitmap): android.graphics.Bitmap {
|
||||
const inWidth = image.getWidth();
|
||||
const inHeight = image.getHeight();
|
||||
|
||||
const iconSpecSize = getIconSpecSize({ width: inWidth, height: inHeight });
|
||||
|
||||
const widthPixels = iconSpecSize.width * layout.getDisplayDensity();
|
||||
const heightPixels = iconSpecSize.height * layout.getDisplayDensity();
|
||||
|
||||
const scaledImage = android.graphics.Bitmap.createScaledBitmap(image, widthPixels, heightPixels, true);
|
||||
|
||||
return scaledImage;
|
||||
}
|
||||
|
||||
protected getIconInfo(tabStripItem: TabStripItem, color?: Color): IconInfo {
|
||||
let originalIcon = this.getOriginalIcon(tabStripItem, color);
|
||||
|
||||
return this.getDrawableInfo(originalIcon);
|
||||
}
|
||||
|
||||
private getDrawableInfo(image: android.graphics.Bitmap): IconInfo {
|
||||
if (image) {
|
||||
if (this.tabStrip && this.tabStrip.isIconSizeFixed) {
|
||||
image = this.getFixedSizeIcon(image);
|
||||
}
|
||||
|
||||
let imageDrawable = new android.graphics.drawable.BitmapDrawable(application.android.context.getResources(), image);
|
||||
|
||||
return {
|
||||
drawable: imageDrawable,
|
||||
height: image.getHeight()
|
||||
};
|
||||
}
|
||||
|
||||
return new IconInfo();
|
||||
}
|
||||
|
||||
[itemsProperty.getDefault](): TabContentItem[] {
|
||||
return null;
|
||||
}
|
||||
|
||||
[itemsProperty.setNative](value: TabContentItem[]) {
|
||||
if (value) {
|
||||
value.forEach((item: TabContentItem, i) => {
|
||||
(<any>item).index = i;
|
||||
});
|
||||
}
|
||||
|
||||
selectedIndexProperty.coerce(this);
|
||||
}
|
||||
|
||||
[tabStripProperty.getDefault](): TabStrip {
|
||||
return null;
|
||||
}
|
||||
|
||||
[tabStripProperty.setNative](value: TabStrip) {
|
||||
const items = this.tabStrip ? this.tabStrip.items : null;
|
||||
this.setTabStripItems(items);
|
||||
}
|
||||
|
||||
[selectedIndexProperty.setNative](value: number) {
|
||||
this.setSelectedItem(value);
|
||||
}
|
||||
}
|
||||
|
||||
function tryCloneDrawable(value: android.graphics.drawable.Drawable, resources: android.content.res.Resources): android.graphics.drawable.Drawable {
|
||||
if (value) {
|
||||
const constantState = value.getConstantState();
|
||||
if (constantState) {
|
||||
return constantState.newDrawable(resources);
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function iterateIndexRange(index: number, eps: number, lastIndex: number, callback: (i) => void) {
|
||||
const rangeStart = Math.max(0, index - eps);
|
||||
const rangeEnd = Math.min(index + eps, lastIndex);
|
||||
for (let i = rangeStart; i <= rangeEnd; i++) {
|
||||
callback(i);
|
||||
}
|
||||
}
|
||||
58
nativescript-core/ui/tabbable-component/tabbable-component.d.ts
vendored
Normal file
58
nativescript-core/ui/tabbable-component/tabbable-component.d.ts
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Contains the TabNavigationBase class, which serves as the base class for tab navigation.
|
||||
* @module "ui/tabbable-component/tabbable-component"
|
||||
*/ /** */
|
||||
import { SelectedIndexChangedEventData, TabNavigationBase } from "../tab-navigation-base/tab-navigation-base";
|
||||
|
||||
|
||||
import { TabContentItem } from "../tab-navigation-base/tab-content-item/tab-content-item";
|
||||
import { TabStrip } from "../tab-navigation-base/tab-strip";
|
||||
import { Property, CoercibleProperty, EventData } from "../core/view/view";
|
||||
|
||||
/**
|
||||
* Represents a tab navigation widget with static tabs at the bottom.
|
||||
*/
|
||||
export abstract class TabbableComponent extends TabNavigationBase {
|
||||
/**
|
||||
* Gets or sets the items of the tab navigation.
|
||||
*/
|
||||
items: Array<TabContentItem>;
|
||||
|
||||
/**
|
||||
* Gets or sets the tab strip of the BottomNavigation.
|
||||
*/
|
||||
tabStrip: TabStrip;
|
||||
|
||||
/**
|
||||
* Gets or sets the selectedIndex of the BottomNavigation.
|
||||
*/
|
||||
selectedIndex: number;
|
||||
|
||||
/**
|
||||
* Gets the native [android widget](http://developer.android.com/reference/android/support/v4/view/ViewPager.html) that represents the user interface for this component. Valid only when running on Android OS.
|
||||
*/
|
||||
android: any /* android.view.View */; //android.support.v4.view.ViewPager;
|
||||
|
||||
/**
|
||||
* Gets the native iOS [UITabBarController](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITabBarController_Class/) that represents the user interface for this component. Valid only when running on iOS.
|
||||
*/
|
||||
ios: any /* UITabBarController */;
|
||||
|
||||
/**
|
||||
* String value used when hooking to the selectedIndexChanged event.
|
||||
*/
|
||||
public static selectedIndexChangedEvent: string;
|
||||
|
||||
/**
|
||||
* A basic method signature to hook an event listener (shortcut alias to the addEventListener method).
|
||||
* @param eventNames - String corresponding to events (e.g. "propertyChange"). Optionally could be used more events separated by `,` (e.g. "propertyChange", "change").
|
||||
* @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.
|
||||
*/
|
||||
on(eventNames: string, callback: (data: EventData) => void, thisArg?: any);
|
||||
|
||||
/**
|
||||
* Raised when the selected index changes.
|
||||
*/
|
||||
on(event: "selectedIndexChanged", callback: (args: SelectedIndexChangedEventData) => void, thisArg?: any);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// Types
|
||||
import { TabNavigationBase } from "../tab-navigation-base/tab-navigation-base";
|
||||
|
||||
export abstract class TabbableComponent extends TabNavigationBase {
|
||||
|
||||
}
|
||||
41
nativescript-core/ui/tabs/tabs-common.android.ts
Normal file
41
nativescript-core/ui/tabs/tabs-common.android.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
// Types
|
||||
import { Tabs as TabsDefinition } from ".";
|
||||
|
||||
// Requires
|
||||
import { CSSType, booleanConverter } from "../core/view";
|
||||
import { Property } from "../core/properties";
|
||||
import { TabbableComponent } from "../tabbable-component/tabbable-component.android";
|
||||
|
||||
export const traceCategory = "TabView";
|
||||
|
||||
export module knownCollections {
|
||||
export const items = "items";
|
||||
}
|
||||
|
||||
@CSSType("Tabs")
|
||||
export abstract class TabsBase extends TabbableComponent implements TabsDefinition {
|
||||
public swipeEnabled: boolean;
|
||||
public offscreenTabLimit: number;
|
||||
public tabsPosition: "top" | "bottom";
|
||||
public iOSTabBarItemsAlignment: IOSTabBarItemsAlignment;
|
||||
}
|
||||
|
||||
// TODO: Add Unit tests
|
||||
export const swipeEnabledProperty = new Property<TabsBase, boolean>({
|
||||
name: "swipeEnabled", defaultValue: true, valueConverter: booleanConverter
|
||||
});
|
||||
swipeEnabledProperty.register(TabsBase);
|
||||
|
||||
// TODO: Add Unit tests
|
||||
// TODO: Coerce to max number of items?
|
||||
export const offscreenTabLimitProperty = new Property<TabsBase, number>({
|
||||
name: "offscreenTabLimit", defaultValue: 1, valueConverter: (v) => parseInt(v)
|
||||
});
|
||||
offscreenTabLimitProperty.register(TabsBase);
|
||||
|
||||
export const tabsPositionProperty = new Property<TabsBase, "top" | "bottom">({ name: "tabsPosition", defaultValue: "top" });
|
||||
tabsPositionProperty.register(TabsBase);
|
||||
|
||||
export type IOSTabBarItemsAlignment = "leading" | "justified" | "center" | "centerSelected";
|
||||
export const iOSTabBarItemsAlignmentProperty = new Property<TabsBase, IOSTabBarItemsAlignment>({ name: "iOSTabBarItemsAlignment", defaultValue: "justified" });
|
||||
iOSTabBarItemsAlignmentProperty.register(TabsBase);
|
||||
33
nativescript-core/ui/tabs/tabs-common.d.ts
vendored
Normal file
33
nativescript-core/ui/tabs/tabs-common.d.ts
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
// Types
|
||||
import { Tabs as TabsDefinition } from ".";
|
||||
|
||||
// Requires
|
||||
import { CSSType, booleanConverter } from "../core/view";
|
||||
import { Property } from "../core/properties";
|
||||
import { TabbableComponent } from "../tabbable-component";
|
||||
|
||||
export * from "../tab-navigation-base/tab-content-item";
|
||||
export * from "../tab-navigation-base/tab-navigation-base";
|
||||
export * from "../tab-navigation-base/tab-strip";
|
||||
export * from "../tab-navigation-base/tab-strip-item";
|
||||
|
||||
export const traceCategory = "TabView";
|
||||
|
||||
export module knownCollections {
|
||||
export const items = "items";
|
||||
}
|
||||
|
||||
@CSSType("Tabs")
|
||||
export abstract class TabsBase extends TabbableComponent implements TabsDefinition {
|
||||
public swipeEnabled: boolean;
|
||||
public offscreenTabLimit: number;
|
||||
public tabsPosition: "top" | "bottom";
|
||||
public iOSTabBarItemsAlignment: IOSTabBarItemsAlignment;
|
||||
}
|
||||
|
||||
export const swipeEnabledProperty: Property<TabsBase, boolean>;
|
||||
export const offscreenTabLimitProperty: Property<TabsBase, number>;
|
||||
export const tabsPositionProperty: Property<TabsBase, "top" | "bottom">;
|
||||
|
||||
export type IOSTabBarItemsAlignment = "leading" | "justified" | "center" | "centerSelected";
|
||||
export const iOSTabBarItemsAlignmentProperty: Property<TabsBase, IOSTabBarItemsAlignment>;
|
||||
@@ -2,9 +2,9 @@
|
||||
import { Tabs as TabsDefinition } from ".";
|
||||
|
||||
// Requires
|
||||
import { TabNavigationBase } from "../tab-navigation-base/tab-navigation-base";
|
||||
import { CSSType, booleanConverter } from "../core/view";
|
||||
import { Property } from "../core/properties";
|
||||
import { TabbableComponent } from "../tabbable-component";
|
||||
|
||||
export * from "../tab-navigation-base/tab-content-item";
|
||||
export * from "../tab-navigation-base/tab-navigation-base";
|
||||
@@ -18,7 +18,7 @@ export module knownCollections {
|
||||
}
|
||||
|
||||
@CSSType("Tabs")
|
||||
export class TabsBase extends TabNavigationBase implements TabsDefinition {
|
||||
export abstract class TabsBase extends TabbableComponent implements TabsDefinition {
|
||||
public swipeEnabled: boolean;
|
||||
public offscreenTabLimit: number;
|
||||
public tabsPosition: "top" | "bottom";
|
||||
@@ -2,21 +2,12 @@
|
||||
import { TabContentItem } from "../tab-navigation-base/tab-content-item";
|
||||
import { TabStrip } from "../tab-navigation-base/tab-strip";
|
||||
import { TabStripItem } from "../tab-navigation-base/tab-strip-item";
|
||||
import { TextTransform } from "../text-base";
|
||||
|
||||
// Requires
|
||||
import * as application from "../../application";
|
||||
import { ImageSource } from "../../image-source";
|
||||
import { ad, isFontIconURI, layout, RESOURCE_PREFIX } from "../../utils/utils";
|
||||
import { ad, layout } from "../../utils/utils";
|
||||
import { Color } from "../core/view";
|
||||
import { Frame } from "../frame";
|
||||
import { Font } from "../styling/font";
|
||||
import {
|
||||
getIconSpecSize, itemsProperty, selectedIndexProperty, tabStripProperty
|
||||
} from "../tab-navigation-base/tab-navigation-base";
|
||||
import { getTransformedText } from "../text-base";
|
||||
import { offscreenTabLimitProperty, swipeEnabledProperty, TabsBase } from "./tabs-common";
|
||||
|
||||
import { offscreenTabLimitProperty, swipeEnabledProperty, TabsBase } from "./tabs-common.android";
|
||||
export * from "./tabs-common";
|
||||
|
||||
const ACCENT_COLOR = "colorAccent";
|
||||
@@ -361,23 +352,9 @@ function setElevation(grid: org.nativescript.widgets.GridLayout, tabsBar: org.na
|
||||
|
||||
export const tabs = new Array<WeakRef<Tabs>>();
|
||||
|
||||
function iterateIndexRange(index: number, eps: number, lastIndex: number, callback: (i) => void) {
|
||||
const rangeStart = Math.max(0, index - eps);
|
||||
const rangeEnd = Math.min(index + eps, lastIndex);
|
||||
for (let i = rangeStart; i <= rangeEnd; i++) {
|
||||
callback(i);
|
||||
}
|
||||
}
|
||||
|
||||
export class Tabs extends TabsBase {
|
||||
private _tabsBar: org.nativescript.widgets.TabsBar;
|
||||
private _viewPager: androidx.viewpager.widget.ViewPager;
|
||||
private _pagerAdapter: androidx.viewpager.widget.PagerAdapter;
|
||||
private _androidViewId: number = -1;
|
||||
public _originalBackground: any;
|
||||
private _textTransform: TextTransform = "uppercase";
|
||||
private _selectedItemColor: Color;
|
||||
private _unSelectedItemColor: Color;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@@ -388,16 +365,12 @@ export class Tabs extends TabsBase {
|
||||
return true;
|
||||
}
|
||||
|
||||
public onItemsChanged(oldItems: TabContentItem[], newItems: TabContentItem[]): void {
|
||||
super.onItemsChanged(oldItems, newItems);
|
||||
public getTabsBar(): org.nativescript.widgets.TabsBar {
|
||||
return <org.nativescript.widgets.TabsBar>(<any>this).nativeBar;
|
||||
}
|
||||
|
||||
if (oldItems) {
|
||||
oldItems.forEach((item: TabContentItem, i, arr) => {
|
||||
(<any>item).index = 0;
|
||||
(<any>item).tabItemSpec = null;
|
||||
item.setNativeView(null);
|
||||
});
|
||||
}
|
||||
protected setNativeItems(items: Array<org.nativescript.widgets.TabItemSpec>) {
|
||||
this.getTabsBar().setItems(items, items ? this._viewPager : null);
|
||||
}
|
||||
|
||||
public createNativeView() {
|
||||
@@ -454,71 +427,21 @@ export class Tabs extends TabsBase {
|
||||
|
||||
public initNativeView(): void {
|
||||
super.initNativeView();
|
||||
if (this._androidViewId < 0) {
|
||||
this._androidViewId = android.view.View.generateViewId();
|
||||
}
|
||||
|
||||
const nativeView: any = this.nativeViewProtected;
|
||||
this._tabsBar = (<any>nativeView).tabsBar;
|
||||
|
||||
const viewPager = (<any>nativeView).viewPager;
|
||||
viewPager.setId(this._androidViewId);
|
||||
const viewPager = (<any>this.nativeViewProtected).viewPager;
|
||||
viewPager.setId(this._contentViewId);
|
||||
this._viewPager = viewPager;
|
||||
this._pagerAdapter = (<any>viewPager).adapter;
|
||||
(<any>this._pagerAdapter).owner = this;
|
||||
}
|
||||
|
||||
public _loadUnloadTabItems(newIndex: number) {
|
||||
const items = this.items;
|
||||
if (!items) {
|
||||
return;
|
||||
}
|
||||
|
||||
const lastIndex = items.length - 1;
|
||||
const offsideItems = this.offscreenTabLimit;
|
||||
|
||||
let toUnload = [];
|
||||
let toLoad = [];
|
||||
|
||||
iterateIndexRange(newIndex, offsideItems, lastIndex, (i) => toLoad.push(i));
|
||||
|
||||
items.forEach((item, i) => {
|
||||
const indexOfI = toLoad.indexOf(i);
|
||||
if (indexOfI < 0) {
|
||||
toUnload.push(i);
|
||||
}
|
||||
});
|
||||
|
||||
toUnload.forEach(index => {
|
||||
const item = items[index];
|
||||
if (items[index]) {
|
||||
item.unloadView(item.content);
|
||||
}
|
||||
});
|
||||
|
||||
const newItem = items[newIndex];
|
||||
const selectedView = newItem && newItem.content;
|
||||
if (selectedView instanceof Frame) {
|
||||
(<Frame>selectedView)._pushInFrameStackRecursive();
|
||||
}
|
||||
|
||||
toLoad.forEach(index => {
|
||||
const item = items[index];
|
||||
if (this.isLoaded && items[index]) {
|
||||
item.loadView(item.content);
|
||||
}
|
||||
});
|
||||
protected getOffscreenTabLimit(): number {
|
||||
return this.offscreenTabLimit;
|
||||
}
|
||||
|
||||
public onLoaded(): void {
|
||||
super.onLoaded();
|
||||
|
||||
if (this._originalBackground) {
|
||||
this.backgroundColor = null;
|
||||
this.backgroundColor = this._originalBackground;
|
||||
this._originalBackground = null;
|
||||
}
|
||||
|
||||
this.setItems((<any>this.items));
|
||||
|
||||
if (this.tabStrip) {
|
||||
@@ -528,44 +451,16 @@ export class Tabs extends TabsBase {
|
||||
// this.setAdapterItems(this.items);
|
||||
}
|
||||
|
||||
public onUnloaded(): void {
|
||||
super.onUnloaded();
|
||||
|
||||
this.setItems(null);
|
||||
this.setTabStripItems(null);
|
||||
|
||||
// this.setAdapterItems(null);
|
||||
}
|
||||
|
||||
public disposeNativeView() {
|
||||
this._tabsBar.setItems(null, null);
|
||||
this.setNativeItems(null);
|
||||
(<any>this._pagerAdapter).owner = null;
|
||||
this._pagerAdapter = null;
|
||||
|
||||
this._tabsBar = null;
|
||||
this.nativeBar = null;
|
||||
this._viewPager = null;
|
||||
super.disposeNativeView();
|
||||
}
|
||||
|
||||
public _onRootViewReset(): void {
|
||||
super._onRootViewReset();
|
||||
|
||||
// call this AFTER the super call to ensure descendants apply their rootview-reset logic first
|
||||
// i.e. in a scenario with tab frames let the frames cleanup their fragments first, and then
|
||||
// cleanup the tab fragments to avoid
|
||||
// android.content.res.Resources$NotFoundException: Unable to find resource ID #0xfffffff6
|
||||
this.disposeCurrentFragments();
|
||||
}
|
||||
|
||||
private disposeCurrentFragments(): void {
|
||||
const fragmentManager = this._getFragmentManager();
|
||||
const transaction = fragmentManager.beginTransaction();
|
||||
for (let fragment of (<Array<any>>fragmentManager.getFragments().toArray())) {
|
||||
transaction.remove(fragment);
|
||||
}
|
||||
transaction.commitNowAllowingStateLoss();
|
||||
}
|
||||
|
||||
private shouldUpdateAdapter(items: Array<TabContentItem>) {
|
||||
if (!this._pagerAdapter) {
|
||||
return false;
|
||||
@@ -603,7 +498,7 @@ export class Tabs extends TabsBase {
|
||||
return false;
|
||||
}
|
||||
|
||||
private setItems(items: Array<TabContentItem>) {
|
||||
protected setItems(items: Array<TabContentItem>) {
|
||||
if (this.shouldUpdateAdapter(items)) {
|
||||
(<any>this._pagerAdapter).items = items;
|
||||
|
||||
@@ -617,335 +512,16 @@ export class Tabs extends TabsBase {
|
||||
}
|
||||
}
|
||||
|
||||
private setTabStripItems(items: Array<TabStripItem>) {
|
||||
const length = items ? items.length : 0;
|
||||
if (length === 0) {
|
||||
this._tabsBar.setItems(null, null);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const tabItems = new Array<org.nativescript.widgets.TabItemSpec>();
|
||||
items.forEach((tabStripItem: TabStripItem, i, arr) => {
|
||||
tabStripItem._index = i;
|
||||
const tabItemSpec = this.createTabItemSpec(tabStripItem);
|
||||
(<any>tabStripItem).tabItemSpec = tabItemSpec;
|
||||
tabItems.push(tabItemSpec);
|
||||
});
|
||||
|
||||
const tabsBar = this._tabsBar;
|
||||
tabsBar.setItems(tabItems, this._viewPager);
|
||||
this.tabStrip.setNativeView(tabsBar);
|
||||
items.forEach((item, i, arr) => {
|
||||
const tv = tabsBar.getTextViewForItemAt(i);
|
||||
item.setNativeView(tv);
|
||||
this._setItemColor(item);
|
||||
});
|
||||
}
|
||||
|
||||
private getItemLabelTextTransform(tabStripItem: TabStripItem): TextTransform {
|
||||
const nestedLabel = tabStripItem.label;
|
||||
let textTransform: TextTransform = null;
|
||||
if (nestedLabel && nestedLabel.style.textTransform !== "initial") {
|
||||
textTransform = nestedLabel.style.textTransform;
|
||||
} else if (tabStripItem.style.textTransform !== "initial") {
|
||||
textTransform = tabStripItem.style.textTransform;
|
||||
}
|
||||
|
||||
return textTransform || this._textTransform;
|
||||
}
|
||||
|
||||
private createTabItemSpec(tabStripItem: TabStripItem): org.nativescript.widgets.TabItemSpec {
|
||||
const tabItemSpec = new org.nativescript.widgets.TabItemSpec();
|
||||
|
||||
if (tabStripItem.isLoaded) {
|
||||
const nestedLabel = tabStripItem.label;
|
||||
let title = nestedLabel.text;
|
||||
|
||||
// TEXT-TRANSFORM
|
||||
const textTransform = this.getItemLabelTextTransform(tabStripItem);
|
||||
title = getTransformedText(title, textTransform);
|
||||
tabItemSpec.title = title;
|
||||
|
||||
// BACKGROUND-COLOR
|
||||
const backgroundColor = tabStripItem.style.backgroundColor;
|
||||
tabItemSpec.backgroundColor = backgroundColor ? backgroundColor.android : this.getTabBarBackgroundArgbColor();
|
||||
|
||||
// COLOR
|
||||
let itemColor = this.selectedIndex === tabStripItem._index ? this._selectedItemColor : this._unSelectedItemColor;
|
||||
const color = itemColor || nestedLabel.style.color;
|
||||
tabItemSpec.color = color && color.android;
|
||||
|
||||
// FONT
|
||||
const fontInternal = nestedLabel.style.fontInternal;
|
||||
if (fontInternal) {
|
||||
tabItemSpec.fontSize = fontInternal.fontSize;
|
||||
tabItemSpec.typeFace = fontInternal.getAndroidTypeface();
|
||||
}
|
||||
|
||||
// ICON
|
||||
const iconSource = tabStripItem.image && tabStripItem.image.src;
|
||||
if (iconSource) {
|
||||
const icon = this.getIcon(tabStripItem, itemColor);
|
||||
|
||||
if (icon) {
|
||||
// TODO: Make this native call that accepts string so that we don't load Bitmap in JS.
|
||||
// tslint:disable-next-line:deprecation
|
||||
tabItemSpec.iconDrawable = icon;
|
||||
} else {
|
||||
// TODO:
|
||||
// traceMissingIcon(iconSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tabItemSpec;
|
||||
}
|
||||
|
||||
private getIcon(tabStripItem: TabStripItem, color?: Color): android.graphics.drawable.BitmapDrawable {
|
||||
const iconSource = tabStripItem.image && tabStripItem.image.src;
|
||||
if (!iconSource) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let is: ImageSource;
|
||||
if (isFontIconURI(iconSource)) {
|
||||
const fontIconCode = iconSource.split("//")[1];
|
||||
const target = tabStripItem.image ? tabStripItem.image : tabStripItem;
|
||||
const font = target.style.fontInternal;
|
||||
if (!color) {
|
||||
color = target.style.color;
|
||||
}
|
||||
is = ImageSource.fromFontIconCodeSync(fontIconCode, font, color);
|
||||
} else {
|
||||
is = ImageSource.fromFileOrResourceSync(iconSource);
|
||||
}
|
||||
|
||||
let imageDrawable: android.graphics.drawable.BitmapDrawable;
|
||||
if (is && is.android) {
|
||||
let image = is.android;
|
||||
|
||||
if (this.tabStrip && this.tabStrip.isIconSizeFixed) {
|
||||
image = this.getFixedSizeIcon(image);
|
||||
}
|
||||
|
||||
imageDrawable = new android.graphics.drawable.BitmapDrawable(appResources, image);
|
||||
} else {
|
||||
// TODO
|
||||
// traceMissingIcon(iconSource);
|
||||
}
|
||||
|
||||
return imageDrawable;
|
||||
}
|
||||
|
||||
private getFixedSizeIcon(image: android.graphics.Bitmap): android.graphics.Bitmap {
|
||||
const inWidth = image.getWidth();
|
||||
const inHeight = image.getHeight();
|
||||
|
||||
const iconSpecSize = getIconSpecSize({ width: inWidth, height: inHeight });
|
||||
|
||||
const widthPixels = iconSpecSize.width * layout.getDisplayDensity();
|
||||
const heightPixels = iconSpecSize.height * layout.getDisplayDensity();
|
||||
|
||||
const scaledImage = android.graphics.Bitmap.createScaledBitmap(image, widthPixels, heightPixels, true);
|
||||
|
||||
return scaledImage;
|
||||
}
|
||||
|
||||
// private setAdapterItems(items: Array<TabStripItem>) {
|
||||
// if (this.shouldUpdateAdapter(items)) {
|
||||
// (<any>this._pagerAdapter).items = items;
|
||||
|
||||
// const length = items ? items.length : 0;
|
||||
// if (length === 0) {
|
||||
// this._tabLayout.setItems(null, null);
|
||||
// this._pagerAdapter.notifyDataSetChanged();
|
||||
// return;
|
||||
// }
|
||||
|
||||
// const tabItems = new Array<org.nativescript.widgets.TabItemSpec>();
|
||||
// items.forEach((item: TabStripItem, i, arr) => {
|
||||
// const tabItemSpec = createTabItemSpec(item);
|
||||
// (<any>item).index = i;
|
||||
// (<any>item).tabItemSpec = tabItemSpec;
|
||||
// tabItems.push(tabItemSpec);
|
||||
// });
|
||||
|
||||
// const tabLayout = this._tabLayout;
|
||||
// tabLayout.setItems(tabItems, this._viewPager);
|
||||
// items.forEach((item, i, arr) => {
|
||||
// const tv = tabLayout.getTextViewForItemAt(i);
|
||||
// item.setNativeView(tv);
|
||||
// });
|
||||
|
||||
// this._pagerAdapter.notifyDataSetChanged();
|
||||
// }
|
||||
// }
|
||||
|
||||
public updateAndroidItemAt(index: number, spec: org.nativescript.widgets.TabItemSpec) {
|
||||
this._tabsBar.updateItemAt(index, spec);
|
||||
}
|
||||
|
||||
public getTabBarBackgroundColor(): android.graphics.drawable.Drawable {
|
||||
return this._tabsBar.getBackground();
|
||||
}
|
||||
|
||||
public setTabBarBackgroundColor(value: android.graphics.drawable.Drawable | Color): void {
|
||||
if (value instanceof Color) {
|
||||
this._tabsBar.setBackgroundColor(value.android);
|
||||
} else {
|
||||
this._tabsBar.setBackground(tryCloneDrawable(value, this.nativeViewProtected.getResources()));
|
||||
}
|
||||
|
||||
this.updateTabStripItems();
|
||||
}
|
||||
|
||||
private updateTabStripItems(): void {
|
||||
this.tabStrip.items.forEach((tabStripItem: TabStripItem) => {
|
||||
if (tabStripItem.nativeView) {
|
||||
const tabItemSpec = this.createTabItemSpec(tabStripItem);
|
||||
this.updateAndroidItemAt(tabStripItem._index, tabItemSpec);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public getTabBarHighlightColor(): number {
|
||||
return getDefaultAccentColor(this._context);
|
||||
}
|
||||
|
||||
public setTabBarHighlightColor(value: number | Color) {
|
||||
const color = value instanceof Color ? value.android : value;
|
||||
this._tabsBar.setSelectedIndicatorColors([color]);
|
||||
this.getTabsBar().setSelectedIndicatorColors([color]);
|
||||
}
|
||||
|
||||
private setItemsColors(items: Array<TabStripItem>): void {
|
||||
items.forEach((item) => {
|
||||
if (item.nativeView) {
|
||||
this._setItemColor(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public getTabBarSelectedItemColor(): Color {
|
||||
return this._selectedItemColor;
|
||||
}
|
||||
|
||||
public setTabBarSelectedItemColor(value: Color) {
|
||||
this._selectedItemColor = value;
|
||||
this.setItemsColors(this.tabStrip.items);
|
||||
}
|
||||
|
||||
public getTabBarUnSelectedItemColor(): Color {
|
||||
return this._unSelectedItemColor;
|
||||
}
|
||||
|
||||
public setTabBarUnSelectedItemColor(value: Color) {
|
||||
this._unSelectedItemColor = value;
|
||||
this.setItemsColors(this.tabStrip.items);
|
||||
}
|
||||
|
||||
private updateItem(tabStripItem: TabStripItem): void {
|
||||
// TODO: Should figure out a way to do it directly with the the nativeView
|
||||
const tabStripItemIndex = this.tabStrip.items.indexOf(tabStripItem);
|
||||
const tabItemSpec = this.createTabItemSpec(tabStripItem);
|
||||
this.updateAndroidItemAt(tabStripItemIndex, tabItemSpec);
|
||||
}
|
||||
|
||||
public setTabBarItemTitle(tabStripItem: TabStripItem, value: string): void {
|
||||
this.updateItem(tabStripItem);
|
||||
}
|
||||
|
||||
public setTabBarItemBackgroundColor(tabStripItem: TabStripItem, value: android.graphics.drawable.Drawable | Color): void {
|
||||
this.updateItem(tabStripItem);
|
||||
}
|
||||
|
||||
public _setItemColor(tabStripItem: TabStripItem) {
|
||||
const itemColor = (tabStripItem._index === this.selectedIndex) ? this._selectedItemColor : this._unSelectedItemColor;
|
||||
if (!itemColor) {
|
||||
return;
|
||||
}
|
||||
|
||||
// set label color
|
||||
tabStripItem.nativeViewProtected.setTextColor(itemColor.android);
|
||||
|
||||
// set icon color
|
||||
this.setIconColor(tabStripItem, itemColor);
|
||||
}
|
||||
|
||||
private setIconColor(tabStripItem: TabStripItem, color?: Color) {
|
||||
const tabBarItem = this._tabsBar.getViewForItemAt(tabStripItem._index);
|
||||
|
||||
const drawable = this.getIcon(tabStripItem, color);
|
||||
const imgView = <android.widget.ImageView>tabBarItem.getChildAt(0);
|
||||
imgView.setImageDrawable(drawable);
|
||||
if (color) {
|
||||
imgView.setColorFilter(color.android);
|
||||
}
|
||||
}
|
||||
|
||||
public setTabBarItemColor(tabStripItem: TabStripItem, value: number | Color): void {
|
||||
const itemColor = (tabStripItem._index === this.selectedIndex) ? this._selectedItemColor : this._unSelectedItemColor;
|
||||
if (itemColor) {
|
||||
// the itemColor is set through the selectedItemColor and unSelectedItemColor properties
|
||||
// so it does not respect the css color
|
||||
return;
|
||||
}
|
||||
|
||||
const androidColor = value instanceof Color ? value.android : value;
|
||||
tabStripItem.nativeViewProtected.setTextColor(androidColor);
|
||||
}
|
||||
|
||||
public setTabBarIconColor(tabStripItem: TabStripItem, value: number | Color): void {
|
||||
const itemColor = (tabStripItem._index === this.selectedIndex) ? this._selectedItemColor : this._unSelectedItemColor;
|
||||
if (itemColor) {
|
||||
// the itemColor is set through the selectedItemColor and unSelectedItemColor properties
|
||||
// so it does not respect the css color
|
||||
return;
|
||||
}
|
||||
|
||||
this.setIconColor(tabStripItem);
|
||||
}
|
||||
|
||||
public setTabBarIconSource(tabStripItem: TabStripItem, value: number | Color): void {
|
||||
this.updateItem(tabStripItem);
|
||||
}
|
||||
|
||||
public setTabBarItemFontInternal(tabStripItem: TabStripItem, value: Font): void {
|
||||
if (value.fontSize) {
|
||||
tabStripItem.nativeViewProtected.setTextSize(value.fontSize);
|
||||
}
|
||||
tabStripItem.nativeViewProtected.setTypeface(value.getAndroidTypeface());
|
||||
}
|
||||
|
||||
public getTabBarItemTextTransform(tabStripItem: TabStripItem): TextTransform {
|
||||
return this.getItemLabelTextTransform(tabStripItem);
|
||||
}
|
||||
|
||||
public setTabBarItemTextTransform(tabStripItem: TabStripItem, value: TextTransform): void {
|
||||
const nestedLabel = tabStripItem.label;
|
||||
const title = getTransformedText(nestedLabel.text, value);
|
||||
tabStripItem.nativeViewProtected.setText(title);
|
||||
}
|
||||
|
||||
public getTabBarTextTransform(): TextTransform {
|
||||
return this._textTransform;
|
||||
}
|
||||
|
||||
public setTabBarTextTransform(value: TextTransform): void {
|
||||
let items = this.tabStrip && this.tabStrip.items;
|
||||
if (items) {
|
||||
items.forEach((tabStripItem) => {
|
||||
if (tabStripItem.label && tabStripItem.nativeViewProtected) {
|
||||
const nestedLabel = tabStripItem.label;
|
||||
const title = getTransformedText(nestedLabel.text, value);
|
||||
tabStripItem.nativeViewProtected.setText(title);
|
||||
}
|
||||
});
|
||||
}
|
||||
this._textTransform = value;
|
||||
}
|
||||
|
||||
[selectedIndexProperty.setNative](value: number) {
|
||||
protected setSelectedItem(value: number) {
|
||||
const smoothScroll = true;
|
||||
|
||||
// TODO
|
||||
@@ -956,21 +532,6 @@ export class Tabs extends TabsBase {
|
||||
this._viewPager.setCurrentItem(value, smoothScroll);
|
||||
}
|
||||
|
||||
[itemsProperty.getDefault](): TabContentItem[] {
|
||||
return null;
|
||||
}
|
||||
[itemsProperty.setNative](value: TabContentItem[]) {
|
||||
this.setItems(value);
|
||||
selectedIndexProperty.coerce(this);
|
||||
}
|
||||
|
||||
[tabStripProperty.getDefault](): TabStrip {
|
||||
return null;
|
||||
}
|
||||
[tabStripProperty.setNative](value: TabStrip) {
|
||||
this.setTabStripItems(value.items);
|
||||
}
|
||||
|
||||
[swipeEnabledProperty.getDefault](): boolean {
|
||||
// TODO: create native method and get native?
|
||||
return true;
|
||||
@@ -985,15 +546,4 @@ export class Tabs extends TabsBase {
|
||||
[offscreenTabLimitProperty.setNative](value: number) {
|
||||
this._viewPager.setOffscreenPageLimit(value);
|
||||
}
|
||||
}
|
||||
|
||||
function tryCloneDrawable(value: android.graphics.drawable.Drawable, resources: android.content.res.Resources): android.graphics.drawable.Drawable {
|
||||
if (value) {
|
||||
const constantState = value.getConstantState();
|
||||
if (constantState) {
|
||||
return constantState.newDrawable(resources);
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
7
nativescript-core/ui/tabs/tabs.d.ts
vendored
7
nativescript-core/ui/tabs/tabs.d.ts
vendored
@@ -5,9 +5,8 @@
|
||||
|
||||
import { EventData, Property } from "../core/view";
|
||||
import { TabContentItem } from "../tab-navigation-base/tab-content-item";
|
||||
import {
|
||||
SelectedIndexChangedEventData, TabNavigationBase
|
||||
} from "../tab-navigation-base/tab-navigation-base";
|
||||
import { SelectedIndexChangedEventData } from "../tab-navigation-base/tab-navigation-base";
|
||||
import { TabbableComponent } from "../tabbable-component";
|
||||
import { TabStrip } from "../tab-navigation-base/tab-strip";
|
||||
|
||||
export * from "../tab-navigation-base/tab-content-item";
|
||||
@@ -18,7 +17,7 @@ export * from "../tab-navigation-base/tab-strip-item";
|
||||
/**
|
||||
* Represents a swipeable tabs view.
|
||||
*/
|
||||
export class Tabs extends TabNavigationBase {
|
||||
export class Tabs extends TabbableComponent {
|
||||
/**
|
||||
* Gets or sets the items of the Tabs.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user