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

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.