AppCompat toolbar

This commit is contained in:
vakrilov
2015-08-27 10:37:58 +03:00
parent 91d539dbcb
commit 254e4a55a1
21 changed files with 245662 additions and 232454 deletions

477265
android17.d.ts vendored
View File

File diff suppressed because one or more lines are too long

View File

@@ -11,3 +11,7 @@ ScrollView {
font-size: 24;
margin: 6 0;
}
.custom-action-bar {
background-color: lightcoral;
}

View File

@@ -1,5 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Page>
<Page.actionBar>
<ActionBar title="Title" cssClass="custom-action-bar">
<ActionBar.actionItems>
<ActionItem text="hi" />
</ActionBar.actionItems>
</ActionBar>
</Page.actionBar>
<ScrollView>
<StackLayout>
<Button tap="itemTap" text="action bar hidden" tag="action-bar-hidden" />

View File

@@ -6,6 +6,7 @@ import frameModule = require("ui/frame");
import stackLayoutModule = require("ui/layouts/stack-layout");
import textViewModule = require("ui/text-view");
import colorModule = require("color");
import observable = require("data/observable");
export function createPage() {
var generateItems = function () {
@@ -27,7 +28,7 @@ export function createPage() {
}
var clickHandlerFactory = function (nextIndex: number, length: number) {
return function () {
return function () {
if (nextIndex < length) {
tabView.selectedIndex = nextIndex;
}
@@ -35,8 +36,8 @@ export function createPage() {
tabView.items = generateItems();
}
}
}
var clickHandler = clickHandlerFactory(nextIndex, length);
}
var clickHandler = clickHandlerFactory(nextIndex, length);
goToNextTabButton.on(buttonModule.Button.tapEvent, clickHandler);
// Second button
@@ -118,6 +119,14 @@ export function createPage() {
}
var tabView = new tabViewModule.TabView();
tabView.items = generateItems();
tabView.on("selectedIndexChanged", (changedArgs) => {
console.log("tabView.selectedIndexChanged new: " + changedArgs.newIndex + " old: " + changedArgs.oldIndex);
});
tabView.on("propertyChange", (changedArgs) => {
var args = <observable.PropertyChangeData>changedArgs;
console.log("tabView.propertyChange property: " + args.propertyName + " value:" + args.value);
});
var page = new pagesModule.Page();
page.content = tabView;

View File

@@ -1,6 +1,6 @@
import page = require("ui/page");
import layout = require("ui/layouts/grid-layout");
import button = require("ui/button");
import {Button} from "ui/button";
import TKUnit = require("../TKUnit");
import helper = require("./layout-helper");
import view = require("ui/core/view");
@@ -34,14 +34,14 @@ export class MyGridLayout extends layout.GridLayout {
}
}
var tmp: button.Button;
var tmp: Button;
var newPage: page.Page;
var rootLayout: MyGridLayout;
export function setUpModule() {
var pageFactory = function (): page.Page {
newPage = new page.Page;
tmp = new button.Button();
tmp = new Button();
tmp.text = "Loading test";
newPage.content = tmp;
return newPage;
@@ -83,25 +83,27 @@ function colSpan(view: view.View): number {
}
export function test_row_defaultValue() {
TKUnit.assert(tmp !== null);
TKUnit.assertEqual(row(tmp), 0, "'row' property default value should be 0.");
var test = new Button();
TKUnit.assert(test !== null);
TKUnit.assertEqual(row(test), 0, "'row' property default value should be 0.");
}
export function test_rowSpan_defaultValue() {
TKUnit.assert(tmp !== null);
TKUnit.assertEqual(rowSpan(tmp), 1, "'rowSpan' property default value should be 1.");
var test = new Button();
TKUnit.assert(test !== null);
TKUnit.assertEqual(rowSpan(test), 1, "'rowSpan' property default value should be 1.");
}
export function test_column_defaultValue() {
TKUnit.assert(tmp !== null);
TKUnit.assertEqual(col(tmp), 0, "'column' property default value should be 0.");
var test = new Button();
TKUnit.assert(test !== null);
TKUnit.assertEqual(col(test), 0, "'column' property default value should be 0.");
}
export function test_columnSpan_defaultValue() {
TKUnit.assert(tmp !== null);
TKUnit.assertEqual(colSpan(tmp), 1, "'columnSpan' property default value should be 1.");
var test = new Button();
TKUnit.assert(test !== null);
TKUnit.assertEqual(colSpan(test), 1, "'columnSpan' property default value should be 1.");
}
export function test_getRow_shouldThrow_onNullValues() {
@@ -550,10 +552,10 @@ export var test_codesnippets = function () {
// ### Add views to grid layout
// ``` JavaScript
var btn1 = new button.Button();
var btn2 = new button.Button();
var btn3 = new button.Button();
var btn4 = new button.Button();
var btn1 = new Button();
var btn2 = new Button();
var btn3 = new Button();
var btn4 = new Button();
gridLayout.addChild(btn1);
gridLayout.addChild(btn2);
gridLayout.addChild(btn3);

View File

@@ -1,29 +1,11 @@
import tabViewModule = require("ui/tab-view");
export function getNativeTabCount(tabView: tabViewModule.TabView): number {
var actionBar = _getActionBar(tabView);
if (actionBar) {
return actionBar.getTabCount();
}
return tabView.android.getAdapter().getCount();
var pagerAdapter: android.support.v4.view.PagerAdapter = (<any>tabView)._pagerAdapter;
return pagerAdapter.getCount();
}
export function selectNativeTab(tabView: tabViewModule.TabView, index: number): void {
var actionBar = _getActionBar(tabView);
if (actionBar) {
actionBar.setSelectedNavigationItem(index);
}
else {
tabView.android.setCurrentItem(index);
}
}
function _getActionBar(tabView: tabViewModule.TabView) {
if (!tabView.android) {
return undefined;
}
var activity = <android.app.Activity>tabView.android.getContext();
return activity.getActionBar();
}
var viewPager: android.support.v4.view.ViewPager = (<any>tabView)._viewPager;
return viewPager.setCurrentItem(index);
}

View File

@@ -571,7 +571,8 @@ function testLoadedAndUnloadedAreFired_WhenNavigatingAwayAndBack(enablePageCache
function _clickTheFirstButtonInTheListViewNatively(tabView: tabViewModule.TabView) {
if (tabView.android) {
var androidListView = <android.widget.ListView>tabView.android.getChildAt(0);
var viewPager: android.support.v4.view.ViewPager = (<any>tabView)._viewPager;
var androidListView = <android.widget.ListView>viewPager.getChildAt(0);
(<android.widget.Button>androidListView.getChildAt(0)).performClick();
}
else {

View File

@@ -196,7 +196,7 @@ export var test_addView_WillThrow_IfView_IsAlreadyAdded = function () {
views[1]._addView(newButton);
} catch (e) {
thrown = true;
TKUnit.assert(e.message === "View already has a parent.");
TKUnit.assert(e.message.indexOf("View already has a parent.") >= 0);
}
TKUnit.assert(thrown);

View File

@@ -162,13 +162,16 @@ export var test_events_onDetachedAndRemovedFromNativeVisualTree_AreRaised_WhenNa
// this is an event fired from CustomLayoutView when a child is removed from the native visual tree
// therefore this event is fired for StackLayout and Button (which is inside StackLayout).
TKUnit.assertEqual(removeFromNativeVisualTreeListener.receivedEvents.length, 2);
TKUnit.assertEqual(removeFromNativeVisualTreeListener.receivedEvents.length, 3);
TKUnit.assertEqual(removeFromNativeVisualTreeListener.receivedEvents[0].name, "childInLayoutRemovedFromNativeVisualTree");
TKUnit.assertEqual(removeFromNativeVisualTreeListener.receivedEvents[0].sender, views[1]);
TKUnit.assertEqual(removeFromNativeVisualTreeListener.receivedEvents[1].name, "childInLayoutRemovedFromNativeVisualTree");
TKUnit.assertEqual(removeFromNativeVisualTreeListener.receivedEvents[1].sender, views[2]);
TKUnit.assertEqual(removeFromNativeVisualTreeListener.receivedEvents[2].name, "childInLayoutRemovedFromNativeVisualTree");
TKUnit.assertEqual(removeFromNativeVisualTreeListener.receivedEvents[2].sender, views[3]);
trace.removeEventListener(onDetachedListener);
trace.removeEventListener(removeFromNativeVisualTreeListener);
};

View File

@@ -76,7 +76,8 @@ export function test_loadWithOptionsNoXML_CSSIsApplied() {
};
export function test_loadInheritedPageAndResolveFromChild() {
helper.navigateToModuleAndRunTest("./xml-declaration/inherited-page", null, (page) => {
var basePath = "xml-declaration/";
helper.navigateToModuleAndRunTest(basePath + "inherited-page", null, (page) => {
let contentLabel = <Label>page.content;
TKUnit.assertEqual("Inherited and loaded", contentLabel.text);

View File

@@ -142,6 +142,13 @@
getBorderWidth(): number;
setBorderWidth(width: number): void;
}
export class SlidingTabLayout extends android.widget.HorizontalScrollView {
constructor(context: android.content.Context);
setDistributeEvenly(distributeEvenly: boolean): void;
setViewPager(viewPager: android.support.v4.view.ViewPager): void;
}
}
}
}

View File

@@ -119,14 +119,6 @@ export class ActionBar extends view.View implements dts.ActionBar {
//
}
public _updateAndroid(menu: android.view.IMenu) {
//
}
public _onAndroidItemSelected(itemId: number): boolean {
return false;
}
public _addArrayFromBuilder(name: string, value: Array<any>) {
if (name === ACTION_ITEMS) {
this.actionItems.setItems(value);

View File

@@ -7,6 +7,7 @@ import imageSource = require("image-source");
import enums = require("ui/enums");
import application = require("application");
import dts = require("ui/action-bar");
import view = require("ui/core/view");
var ACTION_ITEM_ID_OFFSET = 1000;
var API_LVL = android.os.Build.VERSION.SDK_INT;
@@ -60,6 +61,7 @@ export class AndroidActionBarSettings implements dts.AndroidActionBarSettings {
export class ActionBar extends common.ActionBar {
private _appResources: android.content.res.Resources;
private _android: AndroidActionBarSettings;
private _toolbar: android.support.v7.widget.Toolbar;
get android(): AndroidActionBarSettings {
return this._android;
@@ -70,7 +72,7 @@ export class ActionBar extends common.ActionBar {
}
get _nativeView() {
return undefined;
return this._toolbar;
}
constructor() {
@@ -80,10 +82,42 @@ export class ActionBar extends common.ActionBar {
this._android = new AndroidActionBarSettings(this);
}
public _createUI() {
this._toolbar = new android.support.v7.widget.Toolbar(this._context);
var owner = this;
this._toolbar.setOnMenuItemClickListener(new android.support.v7.widget.Toolbar.OnMenuItemClickListener({
onMenuItemClick: function (item: android.view.IMenuItem): boolean {
var itemId = item.getItemId();
return owner._onAndroidItemSelected(itemId);
}
}));
this.update();
}
public update() {
if (this.page && this.page.frame && this.page.frame.android && this.page.frame.android.activity) {
this.page.frame.android.activity.invalidateOptionsMenu();
if (!this._toolbar) {
return;
}
if (this.page.actionBarHidden) {
this._toolbar.setVisibility(android.view.View.GONE);
// If action bar is hidden - no need to fill it with items.
return;
}
this._toolbar.setVisibility(android.view.View.VISIBLE);
// Add menu items
this._addActionItems();
// Set title
this._updateTitleAndTitleView();
// Set home icon
this._updateIcon();
// Set navigation button
this._updateNavigationButton();
}
public _onAndroidItemSelected(itemId: number): boolean {
@@ -101,98 +135,64 @@ export class ActionBar extends common.ActionBar {
return false;
}
public _updateAndroid(menu: android.view.IMenu) {
var actionBar: android.app.ActionBar = frame.topmost().android.actionBar;
if (this.page.actionBarHidden) {
if (actionBar.isShowing()) {
actionBar.hide();
}
// If action bar is hidden - no need to fill it with items.
return;
}
// Assure action bar is showing;
if (!actionBar.isShowing()) {
actionBar.show();
}
this._addActionItems(menu);
// Set title
this._updateTitleAndTitleView(actionBar);
// Set home icon
this._updateIcon(actionBar);
// Set navigation button
this._updateNavigationButton(actionBar);
}
public _updateNavigationButton(actionBar: android.app.ActionBar) {
public _updateNavigationButton() {
var navButton = this.navigationButton;
if (navButton) {
// No API to set the icon in pre-lvl 18
if (API_LVL >= 18) {
var drawableOrId = getDrawableOrResourceId(navButton.icon, this._appResources);
if (!drawableOrId) {
drawableOrId = 0;
var drawableOrId = getDrawableOrResourceId(navButton.icon, this._appResources);
this._toolbar.setNavigationIcon(drawableOrId);
this._toolbar.setNavigationOnClickListener(new android.view.View.OnClickListener({
onClick: function (v) {
if (navButton) {
navButton._raiseTap();
}
}
setHomeAsUpIndicator(actionBar, drawableOrId);
}
actionBar.setDisplayHomeAsUpEnabled(true);
}));
}
else {
actionBar.setDisplayHomeAsUpEnabled(false);
this._toolbar.setNavigationIcon(null);
}
}
public _updateIcon(actionBar: android.app.ActionBar) {
var icon = this.android.icon;
if (types.isDefined(icon)) {
var drawableOrId = getDrawableOrResourceId(icon, this._appResources);
if (drawableOrId) {
actionBar.setIcon(drawableOrId);
}
}
else {
var defaultIcon = application.android.nativeApp.getApplicationInfo().icon;
actionBar.setIcon(defaultIcon);
}
public _updateIcon() {
var visibility = getIconVisibility(this.android.iconVisibility);
actionBar.setDisplayShowHomeEnabled(visibility);
}
public _updateTitleAndTitleView(actionBar: android.app.ActionBar) {
if (this.titleView) {
actionBar.setCustomView(this.titleView.android);
actionBar.setDisplayShowCustomEnabled(true);
actionBar.setDisplayShowTitleEnabled(false);
if (visibility) {
var icon = this.android.icon;
if (types.isDefined(icon)) {
var drawableOrId = getDrawableOrResourceId(icon, this._appResources);
if (drawableOrId) {
this._toolbar.setLogo(drawableOrId);
}
}
else {
var defaultIcon = application.android.nativeApp.getApplicationInfo().icon;
this._toolbar.setLogo(defaultIcon);
}
}
else {
actionBar.setCustomView(null);
actionBar.setDisplayShowCustomEnabled(false);
actionBar.setDisplayShowTitleEnabled(true);
this._toolbar.setLogo(null);
}
}
public _updateTitleAndTitleView() {
if (!this.titleView) {
// No title view - show the title
var title = this.title;
if (types.isDefined(title)) {
actionBar.setTitle(title);
this._toolbar.setTitle(title);
} else {
var appContext = application.android.context;
var appInfo = appContext.getApplicationInfo();
var appLabel = appContext.getPackageManager().getApplicationLabel(appInfo);
if (appLabel) {
actionBar.setTitle(appLabel);
this._toolbar.setTitle(appLabel);
}
}
}
}
public _addActionItems(menu: android.view.IMenu) {
public _addActionItems() {
var menu = this._toolbar.getMenu();
var items = this.actionItems.getItems();
for (var i = 0; i < items.length; i++) {
@@ -213,52 +213,46 @@ export class ActionBar extends common.ActionBar {
public _onTitlePropertyChanged() {
var topFrame = frame.topmost();
if (topFrame && topFrame.currentPage === this.page) {
this._updateTitleAndTitleView(frame.topmost().android.actionBar);
this._updateTitleAndTitleView();
}
}
public _onIconPropertyChanged() {
var topFrame = frame.topmost();
if (topFrame && topFrame.currentPage === this.page) {
this._updateIcon(frame.topmost().android.actionBar);
this._updateIcon();
}
}
public _clearAndroidReference() {
// don't clear _android field!
this._toolbar = undefined;
}
}
var setHomeAsUpIndicatorWithResoruceId: java.lang.reflect.Method;
var setHomeAsUpIndicatorWithDrawable: java.lang.reflect.Method;
function setHomeAsUpIndicator(actionBar: android.app.ActionBar, drawableOrId: any) {
try {
// TODO: Remove reflection as soon as AppCopmat libs are available
var paramsArr = java.lang.reflect.Array.newInstance(java.lang.Object.class, 1);
if (types.isNumber(drawableOrId)) {
if (!setHomeAsUpIndicatorWithResoruceId) {
// get setHomeAsUpIndicator(resourceId: number) method with reflection and cache it
let typeArr = java.lang.reflect.Array.newInstance(java.lang.Class.class, 1);
typeArr[0] = java.lang.Integer.TYPE;
setHomeAsUpIndicatorWithResoruceId = actionBar.getClass().getMethod("setHomeAsUpIndicator", typeArr);
public _addViewToNativeVisualTree(child: view.View, atIndex?: number): boolean {
super._addViewToNativeVisualTree(child);
if (this._toolbar && child._nativeView) {
if (types.isNullOrUndefined(atIndex) || atIndex >= this._nativeView.getChildCount()) {
this._toolbar.addView(child._nativeView);
}
paramsArr[0] = new java.lang.Integer(drawableOrId);
setHomeAsUpIndicatorWithResoruceId.invoke(actionBar, paramsArr);
} else {
if (!setHomeAsUpIndicatorWithDrawable) {
// get setHomeAsUpIndicator(drawable) method with reflection and cache it
let typeArr = java.lang.reflect.Array.newInstance(java.lang.Class.class, 1);
typeArr[0] = android.graphics.drawable.Drawable.class;
setHomeAsUpIndicatorWithDrawable = actionBar.getClass().getMethod("setHomeAsUpIndicator", typeArr);
else {
this._toolbar.addView(child._nativeView, atIndex);
}
paramsArr[0] = drawableOrId;
setHomeAsUpIndicatorWithDrawable.invoke(actionBar, paramsArr);
return true;
}
return false;
}
catch (e) {
trace.write("Failed to set navigation icon: " + e, trace.categories.Error, trace.messageType.error);
public _removeViewFromNativeVisualTree(child: view.View): void {
super._removeViewFromNativeVisualTree(child);
if (this._toolbar && child._nativeView) {
this._toolbar.removeView(child._nativeView);
trace.notifyEvent(child, "childInLayoutRemovedFromNativeVisualTree");
}
}
}

View File

@@ -50,8 +50,6 @@ declare module "ui/action-bar" {
//@private
_isEmpty(): boolean
_updateAndroid(menu: android.view.IMenu);
_onAndroidItemSelected(itemId: number): boolean
//@endprivate
_addArrayFromBuilder(name: string, value: Array<any>): void;

View File

@@ -900,7 +900,7 @@ export class View extends proxy.ProxyObject implements definition.View {
}
if (view._parent) {
throw new Error("View already has a parent.");
throw new Error("View already has a parent. View: " + view + " Parent: " + view._parent);
}
view._parent = this;
@@ -954,7 +954,7 @@ export class View extends proxy.ProxyObject implements definition.View {
*/
public _removeView(view: View) {
if (view._parent !== this) {
throw new Error("View not added to this instance.");
throw new Error("View not added to this instance. View: " + view + " CurrentParent: " + view._parent + " ExpectedParent: " + this);
}
this._removeViewCore(view);

View File

@@ -134,25 +134,6 @@ class PageFragmentBody extends android.app.Fragment {
super.onDetach();
trace.write(this.getTag() + ".onDetach();", trace.categories.NativeLifecycle);
}
onCreateOptionsMenu(menu: android.view.IMenu, inflater: android.view.MenuInflater) {
super.onCreateOptionsMenu(menu, inflater);
var page: pages.Page = this.entry.resolvedPage;
page.actionBar._updateAndroid(menu);
}
onOptionsItemSelected(item: android.view.IMenuItem) {
var page: pages.Page = this.entry.resolvedPage;
var itemId = item.getItemId();
if (page.actionBar._onAndroidItemSelected(itemId)) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
function onFragmentShown(fragment: PageFragmentBody) {

View File

@@ -49,7 +49,7 @@ export class Page extends contentView.ContentView implements dts.Page {
this.style._setValue(styleModule.backgroundColorProperty, "white", dependencyObservable.ValueSource.Inherited);
this._applyCss();
if (this.actionBarHidden !== undefined) {
this._updateActionBar(this.actionBarHidden);
}
@@ -261,13 +261,4 @@ export class Page extends contentView.ContentView implements dts.Page {
resetCssValuesFunc(this);
view.eachDescendant(this, resetCssValuesFunc);
}
public _addViewToNativeVisualTree(view: view.View): boolean {
// ActionBar is added to the native visual tree by default
if (view === this.actionBar) {
return true;
}
return super._addViewToNativeVisualTree(view);
}
}

View File

@@ -1,7 +1,12 @@
import pageCommon = require("ui/page/page-common");
import definition = require("ui/page");
import view = require("ui/core/view");
import trace = require("trace");
import color = require("color");
import actionBarModule = require("ui/action-bar");
import types = require("utils/types");
import gridModule = require("ui/layouts/grid-layout");
import enums = require("ui/enums");
global.moduleMerge(pageCommon, exports);
@@ -31,13 +36,46 @@ class DialogFragmentClass extends android.app.DialogFragment {
return dialog;
}
};
export class Page extends pageCommon.Page {
private _isBackNavigation = false;
private _grid: org.nativescript.widgets.GridLayout;
constructor(options?: definition.Options) {
super(options);
}
get android(): android.view.ViewGroup {
return this._grid;
}
get _nativeView(): android.view.ViewGroup {
return this._grid;
}
public _createUI() {
this._grid = new org.nativescript.widgets.GridLayout(this._context);
this._grid.addRow(new org.nativescript.widgets.ItemSpec(1, org.nativescript.widgets.GridUnitType.auto));
this._grid.addRow(new org.nativescript.widgets.ItemSpec(1, org.nativescript.widgets.GridUnitType.star));
}
public _addViewToNativeVisualTree(child: view.View, atIndex?: number): boolean {
// Set the row property for the child
if (this._nativeView && child._nativeView) {
if (child instanceof actionBarModule.ActionBar) {
gridModule.GridLayout.setRow(child, 0);
child.horizontalAlignment = enums.HorizontalAlignment.stretch;
child.verticalAlignment = enums.VerticalAlignment.top;
}
else {
gridModule.GridLayout.setRow(child, 1);
}
}
return super._addViewToNativeVisualTree(child, atIndex);
}
public _onDetached(force?: boolean) {
var skipDetached = !force && this.frame.android.cachePagesOnNavigate && !this._isBackNavigation;

View File

@@ -177,4 +177,13 @@ export class Page extends pageCommon.Page {
viewModule.View.layoutChild(this, this.actionBar, 0, 0, right - left, bottom - top);
super.onLayout(left, top, right, bottom);
}
public _addViewToNativeVisualTree(view: viewModule.View): boolean {
// ActionBar is added to the native visual tree by default
if (view === this.actionBar) {
return true;
}
return super._addViewToNativeVisualTree(view);
}
}

View File

@@ -3,38 +3,32 @@ import definition = require("ui/tab-view");
import dependencyObservable = require("ui/core/dependency-observable");
import view = require("ui/core/view");
import trace = require("trace");
import imageSource = require("image-source");
import types = require("utils/types");
import app = require("application");
import page = require("ui/page");
var VIEWS_STATES = "_viewStates";
var RESOURCE_PREFIX = "res://";
//var RESOURCE_PREFIX = "res://";
global.moduleMerge(common, exports);
class ViewPagerClass extends android.support.v4.view.ViewPager {
private owner: TabView;
export class TabViewItem extends common.TabViewItem {
public _tab: android.app.ActionBar.Tab;
public _parent: TabView;
constructor(ctx, owner: TabView) {
super(ctx);
this.owner = owner;
return global.__native(this);
public _update() {
if (this._parent && this._tab) {
var androidApp = app.android;
var resources = androidApp.context.getResources();
this._tab.setText(this.title);
this._parent._setIcon(this.iconSource, this._tab, resources, androidApp.packageName);
}
}
protected onVisibilityChanged(changedView: android.view.View, visibility: number) {
super.onVisibilityChanged(changedView, visibility);
this.owner._onVisibilityChanged(changedView, visibility);
}
};
}
class PagerAdapterClass extends android.support.v4.view.PagerAdapter {
private owner: TabView;
private items: any;
private items: Array<definition.TabViewItem>;
constructor(owner: TabView, items) {
constructor(owner: TabView, items: Array<definition.TabViewItem>) {
super();
this.owner = owner;
@@ -133,408 +127,98 @@ class PagerAdapterClass extends android.support.v4.view.PagerAdapter {
}
};
export class TabViewItem extends common.TabViewItem {
public _tab: android.app.ActionBar.Tab;
public _parent: TabView;
class PageChangedListener extends android.support.v4.view.ViewPager.SimpleOnPageChangeListener {
private _owner: TabView;
constructor(owner: TabView) {
super();
this._owner = owner;
return global.__native(this);
}
public _update() {
if (this._parent && this._tab) {
var androidApp = app.android;
var resources = androidApp.context.getResources();
this._tab.setText(this.title);
this._parent._setIcon(this.iconSource, this._tab, resources, androidApp.packageName);
}
public onPageSelected(position: number) {
this._owner.selectedIndex = position;
}
}
export class TabView extends common.TabView {
private _android: android.support.v4.view.ViewPager;
private _grid: org.nativescript.widgets.GridLayout;
private _tabLayout: org.nativescript.widgets.SlidingTabLayout;
private _viewPager: android.support.v4.view.ViewPager;
private _pagerAdapter: android.support.v4.view.PagerAdapter;
private _tabListener: android.app.ActionBar.TabListener;
private _pageChangeListener: android.support.v4.view.ViewPager.OnPageChangeListener;
private _originalActionBarNavigationMode: number;
private _originalActionBarIsShowing: boolean;
private _listenersSuspended = false;
private _tabsAddedByMe = new Array<android.app.ActionBar.Tab>();
private _tabsCache = {};
private _androidViewId: number;
private _iconsCache = {};
constructor() {
super();
private _pageChagedListener: PageChangedListener;
var that = new WeakRef(this);
this._tabListener = new android.app.ActionBar.TabListener({
get owner() {
return that.get();
},
onTabSelected: function (tab: android.app.ActionBar.Tab, transaction) {
var owner = this.owner;
if (!owner) {
return;
}
if (owner._listenersSuspended || !owner.isLoaded) {
// Don't touch the selectedIndex property if the control is not yet loaded
// or if we are currently rebinding it -- i.e. _removeTabs and _addTabs.
return;
}
var index = owner._tabsCache[tab.hashCode()];
trace.write("TabView.TabListener.onTabSelected(" + index + ");", common.traceCategory);
owner.selectedIndex = index;
},
onTabUnselected: function (tab: android.app.ActionBar.Tab, transaction) {
//
},
onTabReselected: function (tab: android.app.ActionBar.Tab, transaction) {
//
}
});
this._pageChangeListener = new android.support.v4.view.ViewPager.OnPageChangeListener({
get owner() {
return that.get();
},
onPageSelected: function (index: number) {
var owner = this.owner;
if (!owner) {
return;
}
if (owner._listenersSuspended || !owner.isLoaded) {
// Don't touch the selectedIndex property if the control is not yet loaded
// or if we are currently rebinding it -- i.e. _removeTabs and _addTabs.
return;
}
trace.write("TabView.OnPageChangeListener.onPageSelected(" + index + ");", common.traceCategory);
owner.selectedIndex = index;
},
onPageScrollStateChanged: function (state: number) {
//
},
onPageScrolled: function (index: number, offset: number, offsetPixels: number) {
//
}
});
}
get android(): android.support.v4.view.ViewPager {
return this._android;
get android(): android.view.View {
return this._grid;
}
public _createUI() {
trace.write("TabView._createUI(" + this._android + ");", common.traceCategory);
trace.write("TabView._createUI(" + this + ");", common.traceCategory);
this._android = new ViewPagerClass(this._context, this);
this._grid = new org.nativescript.widgets.GridLayout(this._context);
this._grid.addRow(new org.nativescript.widgets.ItemSpec(1, org.nativescript.widgets.GridUnitType.auto));
this._grid.addRow(new org.nativescript.widgets.ItemSpec(1, org.nativescript.widgets.GridUnitType.star));
this._tabLayout = new org.nativescript.widgets.SlidingTabLayout(this._context);
this._tabLayout.setDistributeEvenly(true);
this._grid.addView(this._tabLayout);
this._viewPager = new android.support.v4.view.ViewPager(this._context);
var lp = new org.nativescript.widgets.CommonLayoutParams()
lp.row = 1;
this._viewPager.setLayoutParams(lp);
this._grid.addView(this._viewPager);
if (!this._androidViewId) {
this._androidViewId = android.view.View.generateViewId();
}
this._android.setId(this._androidViewId);
this._grid.setId(this._androidViewId);
this._android.setOnPageChangeListener(this._pageChangeListener);
}
/* tslint:disable */
public _onVisibilityChanged(changedView: android.view.View, visibility: number) {
/* tslint:enable */
trace.write("TabView._onVisibilityChanged:" + this.android + " isShown():" + this.android.isShown(), common.traceCategory);
if (this.isLoaded && this.android && this.android.isShown()) {
this._setAdapterIfNeeded();
this._addTabsIfNeeded();
this._setNativeSelectedIndex(this.selectedIndex);
}
else {
// Remove the tabs from the ActionBar only when navigating away from a cached page containing a TabView.
if (TabView._isProxyOfOrDescendantOfNativeView(this, changedView)) {
this._removeTabsIfNeeded();
}
// If the application is being brought to the background (i.e. minimized with the Home Button) we do not need to remove anything.
}
}
private static _isProxyOfOrDescendantOfNativeView(view: view.View, nativeView: android.view.View): boolean {
if (view.android === nativeView) {
return true;
}
if (!view.parent) {
return false;
}
return TabView._isProxyOfOrDescendantOfNativeView(view.parent, nativeView);
}
public _onAttached(context: android.content.Context) {
trace.write("TabView._onAttached(" + context + ");", common.traceCategory);
super._onAttached(context);
}
public _onDetached(force?: boolean) {
trace.write("TabView._onDetached(" + force + ");", common.traceCategory);
super._onDetached(force);
}
public onLoaded() {
trace.write("TabView.onLoaded(); selectedIndex: " + this.selectedIndex +"; items: " + this.items + ";", common.traceCategory);
super.onLoaded();
// If we are loading a TabView inside a hidden fragment this check will prevent it from polluting the action bar.
if (this.android && this.android.isShown()) {
// Cover the case when pageCacheOnNavigate is enabled - set adapter in loaded as the TabView is already
// attached and _onItemsPropertyChangedSetNativeValue will not be called
this._setAdapterIfNeeded();
this._addTabsIfNeeded();
this._setNativeSelectedIndex(this.selectedIndex);
}
}
public onUnloaded() {
trace.write("TabView.onUnloaded();", common.traceCategory);
this._removeTabsIfNeeded();
this._unsetAdapter();
super.onUnloaded();
}
private _addTabsIfNeeded() {
if (this.items && this.items.length > 0 && this._tabsAddedByMe.length === 0) {
this._listenersSuspended = true;
this._addTabs(this.items);
this._listenersSuspended = false;
}
}
private _removeTabsIfNeeded() {
if (this._tabsAddedByMe.length > 0) {
this._listenersSuspended = true;
this._removeTabs(this.items);
this._listenersSuspended = false;
}
this._pageChagedListener = new PageChangedListener(this);
(<any>this._viewPager).addOnPageChangeListener(this._pageChagedListener);
}
public _onItemsPropertyChangedSetNativeValue(data: dependencyObservable.PropertyChangeData) {
trace.write("TabView._onItemsPropertyChangedSetNativeValue(" + data.oldValue + " ---> " + data.newValue + ");", common.traceCategory);
this._listenersSuspended = true;
if (data.oldValue) {
this._removeTabs(data.oldValue);
this._unsetAdapter();
this._viewPager.setAdapter(null);
this._pagerAdapter = null;
this._tabLayout.setViewPager(null);
}
if (data.newValue) {
this._addTabs(data.newValue);
this._setAdapter(data.newValue);
var items: Array<definition.TabViewItem> = data.newValue;
items.forEach((item, idx, arr) => {
if (types.isNullOrUndefined(item.view)) {
throw new Error("View of TabViewItem at index " + idx + " is " + item.view);
}
});
this._pagerAdapter = new PagerAdapterClass(this, data.newValue);
this._viewPager.setAdapter(this._pagerAdapter);
this._tabLayout.setViewPager(this._viewPager);
}
this._updateSelectedIndexOnItemsPropertyChanged(data.newValue);
this._listenersSuspended = false;
}
private _setAdapterIfNeeded() {
if (!this._pagerAdapter && this.items && this.items.length > 0) {
this._setAdapter(this.items);
}
}
private _setAdapter(items) {
this._pagerAdapter = new PagerAdapterClass(this, items);
this._android.setAdapter(this._pagerAdapter);
}
private _unsetAdapter() {
if (this._pagerAdapter) {
this._android.setAdapter(null);
this._pagerAdapter = null;
}
}
public _addTabs(newItems: Array<definition.TabViewItem>) {
var parentPage = <page.Page>this.page;
if (parentPage && parentPage.actionBarHidden) {
return;
}
trace.write("TabView._addTabs(" + newItems + ");", common.traceCategory);
super._addTabs(newItems);
var actionBar = this._getActionBar();
if (!actionBar) {
return;
}
if (this._tabsAddedByMe.length > 0) {
throw new Error("TabView has already added its tabs to the ActionBar.");
}
// Save the original navigation mode so we can restore it later.
this._originalActionBarNavigationMode = actionBar.getNavigationMode();
// Tell the action-bar to display tabs.
actionBar.setNavigationMode(android.app.ActionBar.NAVIGATION_MODE_TABS);
this._originalActionBarIsShowing = actionBar.isShowing();
actionBar.show();
// TODO: Where will be the support for more ActionBar settings like Title, Navigation buttons, etc.?
var i: number = 0;
var length = newItems.length;
var item: TabViewItem;
var tab: android.app.ActionBar.Tab;
var androidApp = app.android;
var resources = androidApp.context.getResources();
for (i; i < length; i++) {
item = <TabViewItem>newItems[i];
tab = actionBar.newTab();
item._tab = tab;
item._parent = this;
tab.setText(item.title);
this._setIcon(item.iconSource, tab, resources, androidApp.packageName);
tab.setTabListener(this._tabListener);
actionBar.addTab(tab);
this._tabsCache[tab.hashCode()] = i;
this._tabsAddedByMe.push(tab);
}
}
public _setIcon(iconSource: string, tab: android.app.ActionBar.Tab, resources: android.content.res.Resources, packageName: string): void {
if (!iconSource) {
return;
}
if (iconSource.indexOf(RESOURCE_PREFIX) === 0 && resources) {
var resourceId: number = resources.getIdentifier(iconSource.substr(RESOURCE_PREFIX.length), 'drawable', packageName);
if (resourceId > 0) {
tab.setIcon(resourceId);
}
}
else {
var drawable: android.graphics.drawable.BitmapDrawable;
drawable = this._iconsCache[iconSource];
if (!drawable) {
var is = imageSource.fromFileOrResource(iconSource);
if (is) {
drawable = new android.graphics.drawable.BitmapDrawable(is.android);
this._iconsCache[iconSource] = drawable;
}
}
if (drawable) {
tab.setIcon(drawable);
}
}
}
public _removeTabs(oldItems: Array<definition.TabViewItem>) {
var parentPage = <page.Page>this.page;
if (parentPage && parentPage.actionBarHidden) {
return;
}
trace.write("TabView._removeTabs(" + oldItems + ");", common.traceCategory);
super._removeTabs(oldItems);
var i = 0;
if (oldItems && oldItems.length) {
var item: TabViewItem;
for (; i < oldItems.length; i++) {
item = <TabViewItem>oldItems[i];
item._tab = null;
item._parent = null;
}
}
var actionBar = this._getActionBar();
if (!actionBar) {
return;
}
// Remove all the existing tabs added by this instance
i = actionBar.getTabCount() - 1;
var tab: android.app.ActionBar.Tab;
var index;
for (; i >= 0; i--) {
tab = actionBar.getTabAt(i);
index = this._tabsAddedByMe.indexOf(tab);
if (index > -1) {// This tab was added by me.
actionBar.removeTabAt(i);
tab.setTabListener(null);
delete this._tabsCache[tab.hashCode()];
this._tabsAddedByMe.splice(index, 1);// Remove the tab from this._tabsAddedByMe
}
}
if (this._tabsAddedByMe.length > 0) {
throw new Error("TabView did not remove all of its tabs from the ActionBar.");
}
if (this._originalActionBarNavigationMode !== undefined) {
actionBar.setNavigationMode(this._originalActionBarNavigationMode);
}
if (!this._originalActionBarIsShowing) {
actionBar.hide();
}
}
public _onSelectedIndexPropertyChangedSetNativeValue(data: dependencyObservable.PropertyChangeData) {
trace.write("TabView._onSelectedIndexPropertyChangedSetNativeValue(" + data.oldValue + " ---> " + data.newValue + ");", common.traceCategory);
super._onSelectedIndexPropertyChangedSetNativeValue(data);
this._setNativeSelectedIndex(data.newValue);
var index = data.newValue;
if (!types.isNullOrUndefined(index)) {
// Select the respective page in the ViewPager
var viewPagerSelectedIndex = this._viewPager.getCurrentItem();
if (viewPagerSelectedIndex !== index) {
trace.write("TabView this._viewPager.setCurrentItem(" + index + ", true);", common.traceCategory);
this._viewPager.setCurrentItem(index, true);
}
}
var args = { eventName: TabView.selectedIndexChangedEvent, object: this, oldIndex: data.oldValue, newIndex: data.newValue };
this.notify(args);
}
private _setNativeSelectedIndex(index: number) {
if (types.isNullOrUndefined(index)) {
return;
}
// Select the respective tab in the ActionBar.
var actionBar = this._getActionBar();
if (actionBar && index < actionBar.getNavigationItemCount() && index !== actionBar.getSelectedNavigationIndex()) {
trace.write("TabView actionBar.setSelectedNavigationItem("+index+")", common.traceCategory);
actionBar.setSelectedNavigationItem(index);
}
// Select the respective page in the ViewPager
var viewPagerSelectedIndex = this._android.getCurrentItem();
if (viewPagerSelectedIndex !== index) {
trace.write("TabView this._android.setCurrentItem("+index+", true);", common.traceCategory);
this._android.setCurrentItem(index, true);
}
}
public _loadEachChildView() {
// Do nothing here, since the children will be loaded and
// attached when PageAdapter.instantiateItem is called by Android.
}
public _unloadEachChildView() {
// Do nothing here, since the children will be unloaded and
// detached when PageAdapter.destroyItem is called by Android.
}
private _getActionBar(): android.app.ActionBar {
if (!this._android) {
// TODO: We may have the ActionBar not enabled as a Window feature. We may extend the control to support custom tabs through the PageTabStrip component.
return undefined;
}
var activity = <android.app.Activity>this._android.getContext();
return activity.getActionBar();
}
}
}

View File

@@ -62,7 +62,7 @@ declare module "ui/tab-view" {
/**
* 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: android.support.v4.view.ViewPager;
android: 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.