From 2518655ab32b9f42c3b1b6d8f822375a9d77e30c Mon Sep 17 00:00:00 2001 From: Alexander Djenkov Date: Thu, 1 Aug 2019 10:02:05 +0300 Subject: [PATCH 1/5] fix(tabs-android): tabStripItem color not applied (#7617) --- .../org/nativescript/widgets/TabStrip.java | 8 + .../org/nativescript/widgets/TabsBar.java | 441 ++++++++++++++++++ tns-core-modules/ui/tabs/tabs.android.ts | 62 +-- .../android/org.nativescript.widgets.d.ts | 26 +- 4 files changed, 504 insertions(+), 33 deletions(-) create mode 100644 tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/TabsBar.java diff --git a/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/TabStrip.java b/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/TabStrip.java index be7970d42..557be52a1 100644 --- a/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/TabStrip.java +++ b/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/TabStrip.java @@ -152,6 +152,7 @@ class TabStrip extends LinearLayout { } } + // Used by TabLayout (the 'old' tab-view control) void onViewPagerPageChanged(int position, float positionOffset) { mSelectedPosition = position; mSelectionOffset = positionOffset; @@ -159,6 +160,13 @@ class TabStrip extends LinearLayout { updateTabsTextColor(); } + // Used by TabsBar + void onTabsViewPagerPageChanged(int position, float positionOffset) { + mSelectedPosition = position; + mSelectionOffset = positionOffset; + invalidate(); + } + int getSelectedPosition(){ return mSelectedPosition; } diff --git a/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/TabsBar.java b/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/TabsBar.java new file mode 100644 index 000000000..c53661cc7 --- /dev/null +++ b/tns-core-modules-widgets/android/widgets/src/main/java/org/nativescript/widgets/TabsBar.java @@ -0,0 +1,441 @@ +/* + * Copyright 2014 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.nativescript.widgets; + +import android.content.Context; +import android.graphics.Typeface; +import androidx.viewpager.widget.PagerAdapter; +import androidx.viewpager.widget.ViewPager; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.util.SparseArray; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.HorizontalScrollView; +import android.widget.ImageView; +import android.widget.ImageView.ScaleType; +import android.widget.LinearLayout; +import android.widget.TextView; + +/** + * To be used with ViewPager to provide a tab indicator component which give + * constant feedback as to the user's scroll progress. + *

+ * To use the component, simply add it to your view hierarchy. Then in your + * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call + * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is + * being used for. + *

+ * The colors can be customized in two ways. The first and simplest is to + * provide an array of colors via {@link #setSelectedIndicatorColors(int...)}. + * The alternative is via the {@link TabColorizer} interface which provides you + * complete control over which color is used for any individual position. + *

+ */ +public class TabsBar extends HorizontalScrollView { + /** + * Allows complete control over the colors drawn in the tab layout. Set with + * {@link #setCustomTabColorizer(TabColorizer)}. + */ + public interface TabColorizer { + + /** + * @return return the color of the indicator used when {@code position} + * is selected. + */ + int getIndicatorColor(int position); + + } + + private static final int TITLE_OFFSET_DIPS = 24; + private static final int TAB_VIEW_PADDING_DIPS = 16; + private static final int TAB_VIEW_TEXT_SIZE_SP = 12; + private static final int TEXT_MAX_WIDTH = 180; + private static final int SMALL_MIN_HEIGHT = 48; + private static final int LARGE_MIN_HEIGHT = 72; + + private int mTitleOffset; + + private boolean mDistributeEvenly = true; + + private TabItemSpec[] mTabItems; + private ViewPager mViewPager; + private SparseArray mContentDescriptions = new SparseArray(); + private ViewPager.OnPageChangeListener mViewPagerPageChangeListener; + + private final TabStrip mTabStrip; + + public TabsBar(Context context) { + this(context, null); + } + + public TabsBar(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public TabsBar(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + // Disable the Scroll Bar + setHorizontalScrollBarEnabled(false); + // Make sure that the Tab Strips fills this View + setFillViewport(true); + + mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density); + + mTabStrip = new TabStrip(context); + addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + } + + /** + * Set the custom {@link TabColorizer} to be used. + * + * If you only require simple customisation then you can use + * {@link #setSelectedIndicatorColors(int...)} to achieve similar effects. + */ + public void setCustomTabColorizer(TabColorizer tabColorizer) { + //mTabStrip.setCustomTabColorizer(tabColorizer); + } + + public void setDistributeEvenly(boolean distributeEvenly) { + mDistributeEvenly = distributeEvenly; + } + + /** + * Sets the colors to be used for indicating the selected tab. These colors + * are treated as a circular array. Providing one color will mean that all + * tabs are indicated with the same color. + */ + public void setSelectedIndicatorColors(int... colors) { + mTabStrip.setSelectedIndicatorColors(colors); + this.mSelectedIndicatorColors = colors; + } + + private int[] mSelectedIndicatorColors; + public int[] getSelectedIndicatorColors() { + return this.mSelectedIndicatorColors; + } + + public void setTabTextColor(int color){ + mTabStrip.setTabTextColor(color); + } + + public int getTabTextColor(){ + return mTabStrip.getTabTextColor(); + } + + public void setSelectedTabTextColor(int color){ + mTabStrip.setSelectedTabTextColor(color); + } + + public int getSelectedTabTextColor(){ + return mTabStrip.getSelectedTabTextColor(); + } + + public void setTabTextFontSize(float fontSize){ + mTabStrip.setTabTextFontSize(fontSize); + } + + public float getTabTextFontSize(){ + return mTabStrip.getTabTextFontSize(); + } + + /** + * Set the {@link ViewPager.OnPageChangeListener}. When using + * {@link TabsBar} you are required to set any + * {@link ViewPager.OnPageChangeListener} through this method. This is so + * that the layout can update it's scroll position correctly. + * + * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener) + */ + public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) { + mViewPagerPageChangeListener = listener; + } + + /** + * Sets the associated view pager. Note that the assumption here is that the + * pager content (number of tabs and tab titles) does not change after this + * call has been made. + */ + public void setViewPager(ViewPager viewPager) { + this.setItems(null, viewPager); + } + + public void setItems(TabItemSpec[] items, ViewPager viewPager) { + mTabStrip.removeAllViews(); + + mViewPager = viewPager; + mTabItems = items; + if (viewPager != null) { + viewPager.addOnPageChangeListener(new InternalViewPagerListener()); + populateTabStrip(); + } + } + + /** + * Updates the UI of an item at specified index + */ + public void updateItemAt(int position, TabItemSpec tabItem) { + LinearLayout ll = (LinearLayout)mTabStrip.getChildAt(position); + ImageView imgView = (ImageView)ll.getChildAt(0); + TextView textView = (TextView)ll.getChildAt(1); + this.setupItem(ll, textView, imgView, tabItem); + } + + /** + * Gets the TextView for tab item at index + */ + public TextView getTextViewForItemAt(int index){ + LinearLayout ll = this.getViewForItemAt(index); + return (ll != null) ? (TextView)ll.getChildAt(1) : null; + } + + /** + * Gets the LinearLayout container for tab item at index + */ + public LinearLayout getViewForItemAt(int index){ + LinearLayout result = null; + + if(this.mTabStrip.getChildCount() > index){ + result = (LinearLayout)this.mTabStrip.getChildAt(index); + } + + return result; + } + + /** + * Gets the number of realized tabs. + */ + public int getItemCount(){ + return this.mTabStrip.getChildCount(); + } + + /** + * Create a default view to be used for tabs. + */ + protected View createDefaultTabView(Context context, TabItemSpec tabItem) { + float density = getResources().getDisplayMetrics().density; + int padding = (int) (TAB_VIEW_PADDING_DIPS * density); + + LinearLayout ll = new LinearLayout(context); + ll.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT)); + ll.setGravity(Gravity.CENTER); + ll.setOrientation(LinearLayout.VERTICAL); + TypedValue outValue = new TypedValue(); + getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground, outValue, true); + ll.setBackgroundResource(outValue.resourceId); + + ImageView imgView = new ImageView(context); + imgView.setScaleType(ScaleType.FIT_CENTER); + LinearLayout.LayoutParams imgLP = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + imgLP.gravity = Gravity.CENTER; + imgView.setLayoutParams(imgLP); + + TextView textView = new TextView(context); + textView.setGravity(Gravity.CENTER); + textView.setMaxWidth((int) (TEXT_MAX_WIDTH * density)); + textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP); + textView.setTypeface(Typeface.DEFAULT_BOLD); + textView.setEllipsize(TextUtils.TruncateAt.END); + textView.setAllCaps(true); + textView.setMaxLines(2); + textView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + textView.setPadding(padding, 0, padding, 0); + + this.setupItem(ll, textView, imgView, tabItem); + + ll.addView(imgView); + ll.addView(textView); + return ll; + } + + private void setupItem(LinearLayout ll, TextView textView,ImageView imgView, TabItemSpec tabItem){ + float density = getResources().getDisplayMetrics().density; + + if (tabItem.iconId != 0) { + imgView.setImageResource(tabItem.iconId); + imgView.setVisibility(VISIBLE); + } else if (tabItem.iconDrawable != null) { + imgView.setImageDrawable(tabItem.iconDrawable); + imgView.setVisibility(VISIBLE); + } else { + imgView.setVisibility(GONE); + } + + if (tabItem.title != null && !tabItem.title.isEmpty()) { + textView.setText(tabItem.title); + textView.setVisibility(VISIBLE); + } else { + textView.setVisibility(GONE); + } + + if (tabItem.backgroundColor != 0) { + ll.setBackgroundColor(tabItem.backgroundColor); + } + + if (imgView.getVisibility() == VISIBLE && textView.getVisibility() == VISIBLE) { + ll.setMinimumHeight((int) (LARGE_MIN_HEIGHT * density)); + } else { + ll.setMinimumHeight((int) (SMALL_MIN_HEIGHT * density)); + } + + if (mDistributeEvenly) { + LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) ll.getLayoutParams(); + lp.width = 0; + lp.weight = 1; + } + } + + public boolean onTap(int position) { + // to be overridden in JS + return true; + } + + public void onSelectedPositionChange(int position, int prevPosition) { + // to be overridden in JS + } + + private void populateTabStrip() { + final PagerAdapter adapter = mViewPager.getAdapter(); + final OnClickListener tabClickListener = new TabClickListener(); + + for (int i = 0; i < adapter.getCount(); i++) { + View tabView = null; + + TabItemSpec tabItem; + if (this.mTabItems != null && this.mTabItems.length > i) { + tabItem = this.mTabItems[i]; + } else { + tabItem = new TabItemSpec(); + tabItem.title = adapter.getPageTitle(i).toString(); + } + + tabView = createDefaultTabView(getContext(), tabItem); + + tabView.setOnClickListener(tabClickListener); + String desc = mContentDescriptions.get(i, null); + if (desc != null) { + tabView.setContentDescription(desc); + } + + mTabStrip.addView(tabView); + if (i == mViewPager.getCurrentItem()) { + tabView.setSelected(true); + } + } + } + + public void setContentDescription(int i, String desc) { + mContentDescriptions.put(i, desc); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + if (mViewPager != null) { + scrollToTab(mViewPager.getCurrentItem(), 0); + } + } + + private void scrollToTab(int tabIndex, int positionOffset) { + final int tabStripChildCount = mTabStrip.getChildCount(); + if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) { + return; + } + + View selectedChild = mTabStrip.getChildAt(tabIndex); + if (selectedChild != null) { + int targetScrollX = selectedChild.getLeft() + positionOffset; + + if (tabIndex > 0 || positionOffset > 0) { + // If we're not at the first child and are mid-scroll, make sure + // we obey the offset + targetScrollX -= mTitleOffset; + } + + scrollTo(targetScrollX, 0); + } + } + + private class InternalViewPagerListener implements ViewPager.OnPageChangeListener { + private int mScrollState; + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + int tabStripChildCount = mTabStrip.getChildCount(); + if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) { + return; + } + + int prevPosition = mTabStrip.getSelectedPosition(); + + if (prevPosition != position) { + onSelectedPositionChange(position, prevPosition); + } + + mTabStrip.onTabsViewPagerPageChanged(position, positionOffset); + + View selectedTitle = mTabStrip.getChildAt(position); + int extraOffset = (selectedTitle != null) ? (int) (positionOffset * selectedTitle.getWidth()) : 0; + scrollToTab(position, extraOffset); + + if (mViewPagerPageChangeListener != null) { + mViewPagerPageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels); + } + } + + @Override + public void onPageScrollStateChanged(int state) { + mScrollState = state; + + if (mViewPagerPageChangeListener != null) { + mViewPagerPageChangeListener.onPageScrollStateChanged(state); + } + } + + @Override + public void onPageSelected(int position) { + if (mScrollState == ViewPager.SCROLL_STATE_IDLE) { + mTabStrip.onTabsViewPagerPageChanged(position, 0f); + scrollToTab(position, 0); + } + for (int i = 0; i < mTabStrip.getChildCount(); i++) { + mTabStrip.getChildAt(i).setSelected(position == i); + } + if (mViewPagerPageChangeListener != null) { + mViewPagerPageChangeListener.onPageSelected(position); + } + } + + } + + private class TabClickListener implements OnClickListener { + @Override + public void onClick(View v) { + for (int i = 0; i < mTabStrip.getChildCount(); i++) { + if (v == mTabStrip.getChildAt(i)) { + if (onTap(i)) { + mViewPager.setCurrentItem(i); + } + return; + } + } + } + } +} \ No newline at end of file diff --git a/tns-core-modules/ui/tabs/tabs.android.ts b/tns-core-modules/ui/tabs/tabs.android.ts index c7a5e2d55..2b4993203 100644 --- a/tns-core-modules/ui/tabs/tabs.android.ts +++ b/tns-core-modules/ui/tabs/tabs.android.ts @@ -28,7 +28,7 @@ interface PagerAdapter { const TABID = "_tabId"; const INDEX = "_index"; let PagerAdapter: PagerAdapter; -let TabLayout: any; +let TabsBar: any; function makeFragmentName(viewId: number, id: number): string { return "android:viewpager:" + viewId + ":" + id; @@ -234,7 +234,7 @@ function initializeNativeClasses() { } } - class TabLayoutImplementation extends org.nativescript.widgets.TabLayout { + class TabsBarImplementation extends org.nativescript.widgets.TabsBar { constructor(context: android.content.Context, public owner: Tabs) { super(context); @@ -280,7 +280,7 @@ function initializeNativeClasses() { } PagerAdapter = FragmentPagerAdapter; - TabLayout = TabLayoutImplementation; + TabsBar = TabsBarImplementation; } function createTabItemSpec(tabStripItem: TabStripItem): org.nativescript.widgets.TabItemSpec { @@ -337,12 +337,12 @@ function getDefaultAccentColor(context: android.content.Context): number { return defaultAccentColor; } -function setElevation(grid: org.nativescript.widgets.GridLayout, tabLayout: org.nativescript.widgets.TabLayout) { +function setElevation(grid: org.nativescript.widgets.GridLayout, tabsBar: org.nativescript.widgets.TabsBar) { const compat = androidx.core.view.ViewCompat; if (compat.setElevation) { const val = DEFAULT_ELEVATION * layout.getDisplayDensity(); compat.setElevation(grid, val); - compat.setElevation(tabLayout, val); + compat.setElevation(tabsBar, val); } } @@ -357,7 +357,7 @@ function iterateIndexRange(index: number, eps: number, lastIndex: number, callba } export class Tabs extends TabsBase { - private _tabLayout: org.nativescript.widgets.TabLayout; + private _tabsBar: org.nativescript.widgets.TabsBar; private _viewPager: androidx.viewpager.widget.ViewPager; private _pagerAdapter: androidx.viewpager.widget.PagerAdapter; private _androidViewId: number = -1; @@ -393,7 +393,7 @@ export class Tabs extends TabsBase { const context: android.content.Context = this._context; const nativeView = new org.nativescript.widgets.GridLayout(context); const viewPager = new org.nativescript.widgets.TabViewPager(context); - const tabLayout = new TabLayout(context, this); + const tabsBar = new TabsBar(context, this); const lp = new org.nativescript.widgets.CommonLayoutParams(); const primaryColor = ad.resources.getPaletteColor(PRIMARY_COLOR, context); let accentColor = getDefaultAccentColor(context); @@ -409,7 +409,7 @@ export class Tabs extends TabsBase { nativeView.addRow(new org.nativescript.widgets.ItemSpec(1, org.nativescript.widgets.GridUnitType.star)); nativeView.addRow(new org.nativescript.widgets.ItemSpec(1, org.nativescript.widgets.GridUnitType.auto)); - tabLayout.setLayoutParams(lp); + tabsBar.setLayoutParams(lp); } nativeView.addView(viewPager); @@ -419,17 +419,17 @@ export class Tabs extends TabsBase { viewPager.setAdapter(adapter); (viewPager).adapter = adapter; - nativeView.addView(tabLayout); - (nativeView).tabLayout = tabLayout; + nativeView.addView(tabsBar); + (nativeView).tabsBar = tabsBar; - setElevation(nativeView, tabLayout); + setElevation(nativeView, tabsBar); if (accentColor) { - tabLayout.setSelectedIndicatorColors([accentColor]); + tabsBar.setSelectedIndicatorColors([accentColor]); } if (primaryColor) { - tabLayout.setBackgroundColor(primaryColor); + tabsBar.setBackgroundColor(primaryColor); } return nativeView; @@ -442,7 +442,7 @@ export class Tabs extends TabsBase { } const nativeView: any = this.nativeViewProtected; - this._tabLayout = (nativeView).tabLayout; + this._tabsBar = (nativeView).tabsBar; const viewPager = (nativeView).viewPager; viewPager.setId(this._androidViewId); @@ -508,11 +508,11 @@ export class Tabs extends TabsBase { } public disposeNativeView() { - this._tabLayout.setItems(null, null); + this._tabsBar.setItems(null, null); (this._pagerAdapter).owner = null; this._pagerAdapter = null; - this._tabLayout = null; + this._tabsBar = null; this._viewPager = null; super.disposeNativeView(); } @@ -590,7 +590,7 @@ export class Tabs extends TabsBase { private setTabStripItems(items: Array) { const length = items ? items.length : 0; if (length === 0) { - this._tabLayout.setItems(null, null); + this._tabsBar.setItems(null, null); return; } @@ -603,11 +603,11 @@ export class Tabs extends TabsBase { tabItems.push(tabItemSpec); }); - const tabLayout = this._tabLayout; - tabLayout.setItems(tabItems, this._viewPager); - this.tabStrip.setNativeView(tabLayout); + const tabsBar = this._tabsBar; + tabsBar.setItems(tabItems, this._viewPager); + this.tabStrip.setNativeView(tabsBar); items.forEach((item, i, arr) => { - const tv = tabLayout.getTextViewForItemAt(i); + const tv = tabsBar.getTextViewForItemAt(i); item.setNativeView(tv); }); } @@ -643,32 +643,32 @@ export class Tabs extends TabsBase { // } public updateAndroidItemAt(index: number, spec: org.nativescript.widgets.TabItemSpec) { - this._tabLayout.updateItemAt(index, spec); + this._tabsBar.updateItemAt(index, spec); } public getTabBarBackgroundColor(): android.graphics.drawable.Drawable { - return this._tabLayout.getBackground(); + return this._tabsBar.getBackground(); } public setTabBarBackgroundColor(value: android.graphics.drawable.Drawable | Color): void { if (value instanceof Color) { - this._tabLayout.setBackgroundColor(value.android); + this._tabsBar.setBackgroundColor(value.android); } else { - this._tabLayout.setBackground(tryCloneDrawable(value, this.nativeViewProtected.getResources)); + this._tabsBar.setBackground(tryCloneDrawable(value, this.nativeViewProtected.getResources)); } } public getTabBarColor(): number { - return this._tabLayout.getTabTextColor(); + return this._tabsBar.getTabTextColor(); } public setTabBarColor(value: number | Color): void { if (value instanceof Color) { - this._tabLayout.setTabTextColor(value.android); - this._tabLayout.setSelectedTabTextColor(value.android); + this._tabsBar.setTabTextColor(value.android); + this._tabsBar.setSelectedTabTextColor(value.android); } else { - this._tabLayout.setTabTextColor(value); - this._tabLayout.setSelectedTabTextColor(value); + this._tabsBar.setTabTextColor(value); + this._tabsBar.setSelectedTabTextColor(value); } } @@ -678,7 +678,7 @@ export class Tabs extends TabsBase { public setTabBarHighlightColor(value: number | Color) { const color = value instanceof Color ? value.android : value; - this._tabLayout.setSelectedIndicatorColors([color]); + this._tabsBar.setSelectedIndicatorColors([color]); } public setTabBarItemBackgroundColor(tabStripItem: TabStripItem, value: android.graphics.drawable.Drawable | Color): void { diff --git a/tns-platform-declarations/android/org.nativescript.widgets.d.ts b/tns-platform-declarations/android/org.nativescript.widgets.d.ts index 33ab960a1..b78e57aa2 100644 --- a/tns-platform-declarations/android/org.nativescript.widgets.d.ts +++ b/tns-platform-declarations/android/org.nativescript.widgets.d.ts @@ -122,7 +122,7 @@ export class LinearGradientDefinition { constructor(startX: number, endX: number, startY: number, - endY: number, colors: number[], stops?: number[]); + endY: number, colors: number[], stops?: number[]); public getStartX(): number; public getStartY(): number; @@ -398,6 +398,28 @@ getItemCount(): number; } + export class TabsBar extends android.widget.HorizontalScrollView { + constructor(context: android.content.Context); + constructor(context: android.content.Context, attrs: android.util.AttributeSet); + constructor(context: android.content.Context, attrs: android.util.AttributeSet, defStyle: number); + + setSelectedIndicatorColors(color: Array): void; + getSelectedIndicatorColors(): Array; + setTabTextColor(color: number): void; + getTabTextColor(): number; + setSelectedTabTextColor(color: number): void; + getSelectedTabTextColor(): number; + setTabTextFontSize(fontSize: number): void; + getTabTextFontSize(): number; + + setItems(items: Array, viewPager: androidx.viewpager.widget.ViewPager): void; + updateItemAt(position: number, itemSpec: TabItemSpec): void; + + getTextViewForItemAt(index: number): android.widget.TextView; + getViewForItemAt(index: number): android.widget.LinearLayout; + getItemCount(): number; + } + export class BottomNavigationBar extends android.widget.LinearLayout { constructor(context: android.content.Context); constructor(context: android.content.Context, attrs: android.util.AttributeSet); @@ -411,7 +433,7 @@ getTabTextFontSize(): number; onTap(position: number): boolean; - onSelectedPositionChange(position: number, prevPosition: number): void ; + onSelectedPositionChange(position: number, prevPosition: number): void; setSelectedPosition(position: number): void; setItems(items: Array): void; updateItemAt(position: number, itemSpec: TabItemSpec): void; From 056d0bf9e4d6cfb4a5066371ffcfb39dc79d6b2e Mon Sep 17 00:00:00 2001 From: Alexander Djenkov Date: Fri, 2 Aug 2019 09:46:09 +0300 Subject: [PATCH 2/5] fix(bottom-navigation): codebehind creation (#7624) does not show tabstrip --- .../ui/bottom-navigation/bottom-navigation.ios.ts | 14 ++++++++++++-- .../tab-navigation-base/tab-navigation-base.ts | 12 +++++------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/tns-core-modules/ui/bottom-navigation/bottom-navigation.ios.ts b/tns-core-modules/ui/bottom-navigation/bottom-navigation.ios.ts index 2a8307506..71bc0ab0d 100644 --- a/tns-core-modules/ui/bottom-navigation/bottom-navigation.ios.ts +++ b/tns-core-modules/ui/bottom-navigation/bottom-navigation.ios.ts @@ -1,10 +1,11 @@ // Types +import { TabStrip } from "../tab-navigation-base/tab-strip"; import { TabContentItem } from "../tab-navigation-base/tab-content-item"; import { TabStripItem } from "../tab-navigation-base/tab-strip-item"; import { TextTransform } from "../text-base"; //Requires -import { TabNavigationBase, itemsProperty, selectedIndexProperty } from "../tab-navigation-base/tab-navigation-base"; +import { TabNavigationBase, itemsProperty, selectedIndexProperty, tabStripProperty } from "../tab-navigation-base/tab-navigation-base"; import { Font } from "../styling/font"; import { getTransformedText } from "../text-base"; import { Frame } from "../frame"; @@ -137,7 +138,7 @@ class UITabBarControllerDelegateImpl extends NSObject implements UITabBarControl if (tabStripItems[position]) { tabStripItems[position]._emit(TabStripItem.selectEvent); } - + if (tabStripItems[prevPosition]) { tabStripItems[prevPosition]._emit(TabStripItem.unselectEvent); } @@ -610,6 +611,15 @@ export class BottomNavigation extends TabNavigationBase { this.setViewControllers(value); selectedIndexProperty.coerce(this); } + + [tabStripProperty.getDefault](): TabStrip { + return null; + } + + [tabStripProperty.setNative](value: TabStrip) { + this.setViewControllers(this.items); + selectedIndexProperty.coerce(this); + } } interface TabStates { diff --git a/tns-core-modules/ui/tab-navigation-base/tab-navigation-base/tab-navigation-base.ts b/tns-core-modules/ui/tab-navigation-base/tab-navigation-base/tab-navigation-base.ts index 34782b3ce..599a6771a 100644 --- a/tns-core-modules/ui/tab-navigation-base/tab-navigation-base/tab-navigation-base.ts +++ b/tns-core-modules/ui/tab-navigation-base/tab-navigation-base/tab-navigation-base.ts @@ -37,8 +37,8 @@ export class TabNavigationBase extends View implements TabNavigationBaseDefiniti this._addView(value); // selectedIndexProperty.coerce(this); } else if (name === "TabStrip") { + // Setting tabStrip will trigger onTabStripChanged this.tabStrip = value; - this._addView(value); } } @@ -94,14 +94,12 @@ export class TabNavigationBase extends View implements TabNavigationBaseDefiniti } public onTabStripChanged(oldTabStrip: TabStrip, newTabStrip: TabStrip) { - if (oldTabStrip && oldTabStrip.items && oldTabStrip.items.length) { - oldTabStrip.items.forEach(item => this._removeView(item)); + if (oldTabStrip && oldTabStrip.parent) { + this._removeView(oldTabStrip); } - if (newTabStrip && newTabStrip.items && newTabStrip.items.length) { - newTabStrip.items.forEach(item => { - this._addView(item); - }); + if (newTabStrip) { + this._addView(newTabStrip); } } From 49b35714a302f734e7438da1073e4d5806ac7908 Mon Sep 17 00:00:00 2001 From: Martin Yankov Date: Fri, 2 Aug 2019 10:23:03 +0300 Subject: [PATCH 3/5] fix: prevent plugin podfile platform version clash (#7626) --- tns-core-modules/platforms/ios/Podfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tns-core-modules/platforms/ios/Podfile b/tns-core-modules/platforms/ios/Podfile index c1b2f02b7..6c71dc9ff 100644 --- a/tns-core-modules/platforms/ios/Podfile +++ b/tns-core-modules/platforms/ios/Podfile @@ -1 +1,4 @@ +platform :ios, '9.0' +use_frameworks! + pod 'MaterialComponents/Tabs', '~> 84.4' \ No newline at end of file From 6ffe23212b0f37581d1d1b7cedf60fa8b7ce378d Mon Sep 17 00:00:00 2001 From: Dimitar Topuzov Date: Fri, 2 Aug 2019 03:04:35 -0700 Subject: [PATCH 4/5] release: cut the 6.0.5 release (#7628) --- CHANGELOG.md | 11 +++++++++++ tns-core-modules/package.json | 2 +- tns-platform-declarations/package.json | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f8449330..6dfcc0e87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +## [6.0.5](https://github.com/NativeScript/NativeScript/compare/6.0.4...6.0.5) (2019-08-02) + + +### Bug Fixes + +* **bottom-navigation:** codebehind creation ([#7624](https://github.com/NativeScript/NativeScript/issues/7624)) ([056d0bf](https://github.com/NativeScript/NativeScript/commit/056d0bf)) +* **tabs-android:** tabStripItem color not applied ([#7617](https://github.com/NativeScript/NativeScript/issues/7617)) ([2518655](https://github.com/NativeScript/NativeScript/commit/2518655)) +* prevent plugin podfile platform version clash ([#7626](https://github.com/NativeScript/NativeScript/issues/7626)) ([49b3571](https://github.com/NativeScript/NativeScript/commit/49b3571)) + + + ## [6.0.4](https://github.com/NativeScript/NativeScript/compare/6.0.3...6.0.4) (2019-07-30) diff --git a/tns-core-modules/package.json b/tns-core-modules/package.json index ae96bdc46..43d01ac8d 100644 --- a/tns-core-modules/package.json +++ b/tns-core-modules/package.json @@ -1,7 +1,7 @@ { "name": "tns-core-modules", "description": "Telerik NativeScript Core Modules", - "version": "6.0.4", + "version": "6.0.5", "homepage": "https://www.nativescript.org", "repository": { "type": "git", diff --git a/tns-platform-declarations/package.json b/tns-platform-declarations/package.json index 08ff2553f..b4d961f90 100644 --- a/tns-platform-declarations/package.json +++ b/tns-platform-declarations/package.json @@ -1,6 +1,6 @@ { "name": "tns-platform-declarations", - "version": "6.0.4", + "version": "6.0.5", "description": "Platform-specific TypeScript declarations for NativeScript for accessing native objects", "main": "", "scripts": { From 83bca630eb66a9189321e8da3dfc3725f5adcd02 Mon Sep 17 00:00:00 2001 From: Svetoslav Date: Wed, 7 Aug 2019 16:19:51 +0300 Subject: [PATCH 5/5] chore: fix typo (#7651) --- e2e/ui-tests-app/e2e/page-object-base-model.ts | 8 ++++---- .../bottom-navigation-css-properties.e2e-spec.ts | 6 +++--- .../bottom-navigation/bottom-navigation.e2e-spec.ts | 10 +++++----- .../suites/tab-navigation/tab-navigation-base-page.ts | 2 +- .../tab-navigation/tab-view/tab-view-base-page.ts | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/e2e/ui-tests-app/e2e/page-object-base-model.ts b/e2e/ui-tests-app/e2e/page-object-base-model.ts index 8f16dd039..883d1d95a 100644 --- a/e2e/ui-tests-app/e2e/page-object-base-model.ts +++ b/e2e/ui-tests-app/e2e/page-object-base-model.ts @@ -6,9 +6,9 @@ export abstract class PageObjectBaseModel { public navigationHelper: NavigationHelper; - constructor(protected _driver: AppiumDriver, protected _naviagtionLinks: Array, elementCacheStrategy: ElementCacheStrategy = ElementCacheStrategy.none) { - this.navigationHelper = new NavigationHelper(this._driver, this._naviagtionLinks, elementCacheStrategy); - this._driver.imageHelper.options.overwriteActualImage = process.env["OVERWRITE_ACTUALIMAGE"] === "true"; + constructor(protected _driver: AppiumDriver, protected _navigationLinks: Array, elementCacheStrategy: ElementCacheStrategy = ElementCacheStrategy.none) { + this.navigationHelper = new NavigationHelper(this._driver, this._navigationLinks, elementCacheStrategy); + this._driver.imageHelper.options.overwriteActualImage = process.env["OVERWRITE_ACTUAL_IMAGE"] === "true"; this._driver.imageHelper.options.waitBeforeCreatingInitialImageCapture = +process.env["WAIT_BEFORE_CREATING_INITIAL_IMAGE_CAPTURE"] || 9000; this._driver.imageHelper.options.keepOriginalImageSize = false; this._driver.imageHelper.options.tolerance = 0; @@ -20,7 +20,7 @@ export abstract class PageObjectBaseModel { } async endSuite() { - const suiteName = this._naviagtionLinks.join("-"); + const suiteName = this._navigationLinks.join("-"); logInfo(`End of suit ${suiteName} tests!`); } diff --git a/e2e/ui-tests-app/e2e/suites/tab-navigation/bottom-navigation/bottom-navigation-css-properties.e2e-spec.ts b/e2e/ui-tests-app/e2e/suites/tab-navigation/bottom-navigation/bottom-navigation-css-properties.e2e-spec.ts index 1bcd4fd0e..8e50a7006 100644 --- a/e2e/ui-tests-app/e2e/suites/tab-navigation/bottom-navigation/bottom-navigation-css-properties.e2e-spec.ts +++ b/e2e/ui-tests-app/e2e/suites/tab-navigation/bottom-navigation/bottom-navigation-css-properties.e2e-spec.ts @@ -61,10 +61,10 @@ describe(`${imagePrefix}-suite`, async function () { await tabTwo.click(); await driver.imageHelper.compareScreen({ imageName: imageName }); - const imageComparissonresult = driver.imageHelper.hasImageComparisonPassed(); - assert.isTrue(imageComparissonresult); + const imageComparisonResult = driver.imageHelper.hasImageComparisonPassed(); + assert.isTrue(imageComparisonResult); - if (imageComparissonresult) { + if (imageComparisonResult) { const tabOne = await driver.waitForElement(sample.tab1); await tabOne.click(); } diff --git a/e2e/ui-tests-app/e2e/suites/tab-navigation/bottom-navigation/bottom-navigation.e2e-spec.ts b/e2e/ui-tests-app/e2e/suites/tab-navigation/bottom-navigation/bottom-navigation.e2e-spec.ts index 3aeabdce3..d6c537ff0 100644 --- a/e2e/ui-tests-app/e2e/suites/tab-navigation/bottom-navigation/bottom-navigation.e2e-spec.ts +++ b/e2e/ui-tests-app/e2e/suites/tab-navigation/bottom-navigation/bottom-navigation.e2e-spec.ts @@ -1,4 +1,4 @@ -import { nsCapabilities, createDriver, AppiumDriver, DeviceOrientaion, logInfo } from "nativescript-dev-appium"; +import { nsCapabilities, createDriver, AppiumDriver, DeviceOrientation, logInfo } from "nativescript-dev-appium"; import { BottomNavigationBasePage } from "./bottom-navigation-base-page"; import { assert } from "chai"; import { setImageName } from "../../../helpers/image-helper"; @@ -28,8 +28,8 @@ describe(`${suite}-${spec}-suite`, async function () { afterEach(async function () { const orientation = await driver.getOrientation(); - if (orientation === DeviceOrientaion.LANDSCAPE) { - await driver.setOrientation(DeviceOrientaion.PORTRAIT); + if (orientation === DeviceOrientation.LANDSCAPE) { + await driver.setOrientation(DeviceOrientation.PORTRAIT); } if (this.currentTest.state === "failed") { await driver.logTestArtifacts(this.currentTest.title); @@ -186,13 +186,13 @@ describe(`${suite}-${spec}-suite`, async function () { it(`${spec}-fancy-fonts-change-orientation`, async function () { await bottomNavigationBasePage.navigateToSample("fancy-fonts"); - await driver.setOrientation(DeviceOrientaion.LANDSCAPE); + await driver.setOrientation(DeviceOrientation.LANDSCAPE); await driver.imageHelper.compareScreen(); await driver.backgroundApp(1); await driver.imageHelper.compareScreen(); - // await driver.setOrientation(DeviceOrientaion.PORTRAIT); + // await driver.setOrientation(DeviceOrientation.PORTRAIT); await driver.imageHelper.compareScreen(); assert.isTrue(driver.imageHelper.hasImageComparisonPassed()); diff --git a/e2e/ui-tests-app/e2e/suites/tab-navigation/tab-navigation-base-page.ts b/e2e/ui-tests-app/e2e/suites/tab-navigation/tab-navigation-base-page.ts index ab8c76102..595fb2ebb 100644 --- a/e2e/ui-tests-app/e2e/suites/tab-navigation/tab-navigation-base-page.ts +++ b/e2e/ui-tests-app/e2e/suites/tab-navigation/tab-navigation-base-page.ts @@ -15,7 +15,7 @@ export abstract class TabNavigationBasePage extends PageObjectBaseModel { abstract async getItems(); async init(subSuiteName: string) { - this._naviagtionLinks.push(subSuiteName); + this._navigationLinks.push(subSuiteName); await super.initSuite(); } diff --git a/e2e/ui-tests-app/e2e/suites/tab-navigation/tab-view/tab-view-base-page.ts b/e2e/ui-tests-app/e2e/suites/tab-navigation/tab-view/tab-view-base-page.ts index 1796cdd89..9b717c1cf 100644 --- a/e2e/ui-tests-app/e2e/suites/tab-navigation/tab-view/tab-view-base-page.ts +++ b/e2e/ui-tests-app/e2e/suites/tab-navigation/tab-view/tab-view-base-page.ts @@ -10,7 +10,7 @@ export class TabViewBasePage extends PageObjectBaseModel { } async init(subSuiteName: string) { - this._naviagtionLinks.push(subSuiteName); + this._navigationLinks.push(subSuiteName); await super.initSuite(); } } \ No newline at end of file