Nativescript: TabView.androidOffscreenTabLimit property

Created on 28 Sep 2016  路  6Comments  路  Source: NativeScript/NativeScript

Please, provide the details below:

It blown my head for 3 days, while I tried to find a solution and found that the issue comes because TabView recreates every tab after it becomes inactive which leads to huge freeze if you have some heavy components.

I have 5 tabs. In the second one I have a list view with 100 rows.

At first load, all tabs are showing very fast.

If i open tab 4 or 5, next time i open tab 2, i have a freeze of 2 seconds. I saw that the list view is recreated every time after the tab becomes inactive. And it becomes inactive only when i open a tab which is not the next or previous one.

If i switch from 1 or 3 to second tab(and I haven't switched before to 4 or 5) the change is instant. But if I switch to 4 or 5, the next time I open a tab with ListView the switch takes minimum 2 seconds.

The switch to the other tabs(without a list views) doesn't need more than 300-400ms even if they were inactive before. Tested with both ListView and RadListView. And I use async pipes right now.

If I remove the ListView component every tab switch takes maximum 300-400ms for reload.

By the way, I use my Samsung Galaxy S7 for testing, and I didn't had any other application hanging like this since i bought it(6months ago)

Did you verify this is a real problem by searching [Stack Overflow]

Yes, i checked documentation, stackoverflow, all related nativescript issues and all the internet related blog posts.

Tell us about the problem

I need to prevent recreation of TabView content after the tab becomes inactive, because the switch to a tab with a ListView freezes de UI for 2 seconds.

Which platform(s) does your issue occur on?

Android

Please provide the following version numbers that your issue occurs with:

  • CLI: 2.3.0
  • Cross-platform modules: 2.3.0 and tested also with @next
  • Runtime(s): 2.3.0
  • Plugin(s):
    @angular/common: 2.0.1

    @angular/compiler: 2.0.1

    @angular/core: 2.0.1

    @angular/forms: 2.0.1

    @angular/http: 2.0.1

    @angular/platform-browser: 2.0.1

    @angular/platform-browser-dynamic: 2.0.1

    @angular/platform-server: 2.0.1

    @angular/router: 3.0.1

    nativescript-angular: 1.0.1

    nativescript-drop-down: ^1.3.2

    nativescript-intl: 0.0.4

    nativescript-statusbar: ^1.0.0

    nativescript-telerik-ui: ^1.4.1

    Please tell us how to recreate the issue in as much detail as possible.

angular app with 5 tabs(tabview), and insert one ListView/RadListView in one of the tabs, then switch the tabs. Everytime when you switch to the tab with the ListView(from a tab which is not next one or previous one) you get a freeze of 2 seconds.

Is there code involved? If so, please share the minimal amount of code needed to recreate the problem.

I will try to make a small demo with the problem soon.

bug done android

Most helpful comment

Hi @adisoftbn,
thank you for the additional info.

We will research what is causing slow navigation between the items inside the TabView. I am attaching link to a sample project where this issue could be reproduced. As a temporary solution you could setOffscreenPageLimit method to increase the items that will be loaded in the memory for android.

Workaround

main-page.xml

<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo">
  <TabView id="tabViewContainer" selectedIndex="{{index}}" loaded="loadedTabView">
      <TabView.items>
          <TabViewItem title="Tab 1">
              <TabViewItem.view>
                  <Label text="This is Label in Tab 1" />
              </TabViewItem.view>
          </TabViewItem>
          <TabViewItem title="Tab 2">
              <TabViewItem.view>
                  <GridLayout>
                      <ListView items="{{ source }}"  loaded="onLoaded" itemLoading="onItemLoading" itemTap="onItemTap">
                          <ListView.itemTemplate>
                              <StackLayout>
                                  <Label text="{{title}}" textWrap="true" />
                              </StackLayout>
                          </ListView.itemTemplate>
                      </ListView>
                  </GridLayout>
              </TabViewItem.view>
          </TabViewItem>
           <TabViewItem title="Tab 3">
              <TabViewItem.view>
                  <Label text="This is Label in Tab 3" />
              </TabViewItem.view>
          </TabViewItem>
           <TabViewItem title="Tab 4">
              <TabViewItem.view>
                  <Label text="This is Label in Tab 4" />
              </TabViewItem.view>
          </TabViewItem>
           <TabViewItem title="Tab 5">
              <TabViewItem.view>
                  <Label text="This is Label in Tab 5" />
              </TabViewItem.view>
          </TabViewItem>
      </TabView.items>
  </TabView>

</Page>
import { EventData } from 'data/observable';
import { Page } from 'ui/page';
import { HelloWorldModel } from './main-view-model';
import {ObservableArray} from "data/observable-array";
import {Observable, PropertyChangeData} from "data/observable";
import {TabView} from "ui/tab-view";
import {isAndroid} from "platform"


// Event handler for Page "navigatingTo" event attached in main-page.xml
export function navigatingTo(args: EventData) {
  // Get the event sender
  let page = <Page>args.object;
  let array = new ObservableArray();
  let observable = new Observable();
  observable.set("source", array);
  for(var i=0; i<500; i++){
    array.push({title:"title "+i});
  }
  page.bindingContext = observable;
}


export function loadedTabView(args:EventData){
  let tabview:TabView = <TabView>args.object;
  console.log((<any>tabview)._viewPager);
  if(isAndroid){
    (<any>tabview)._viewPager.setOffscreenPageLimit(3);
  }
}

Angular

app.component.html

<TabView #tabview (loaded)="tabviewloaded($event)">
  <StackLayout *tabItem="{title: 'Tab1'}">
    <Label text="This is Label in Tab 1"></Label>
  </StackLayout>
  <StackLayout *tabItem="{title: 'Tab2'}">
    <ListView [items]="myItems" (itemTap)="onItemTap($event)">
        <template let-item="item" let-i="index" let-odd="odd" let-even="even">
            <StackLayout [class.odd]="odd" [class.even]="even">
                <Label [text]='"index: " + i'></Label>
                <Label [text]='"[" + item.id +"] " + item.name'></Label>
            </StackLayout>
        </template>
    </ListView>
  </StackLayout>
  <StackLayout *tabItem="{title: 'Tab3'}">
    <Label text="This is Label in Tab 3"></Label>
  </StackLayout>
  <StackLayout *tabItem="{title: 'Tab4'}">
    <Label text="This is Label in Tab 4"></Label>
  </StackLayout>
  <StackLayout *tabItem="{title: 'Tab5'}">
    <Label text="This is Label in Tab 5"></Label>
  </StackLayout>
</TabView>

app.component.ts

import {Component, OnInit} from "@angular/core";
import {isAndroid} from "platform";
import {TabView} from "ui/tab-view"

class DataItem {
    constructor(public id: number, public name: string) { }
}


@Component({
    selector: "my-app",
    templateUrl: "app.component.html",
})
export class AppComponent {
    public myItems: Array<DataItem>;
    private counter: number;

    constructor() {

    }
    ngOnInit(){
        this.myItems = [];
        this.counter = 0;
        for (var i = 0; i < 500; i++) {
            this.myItems.push(new DataItem(i, "data item " + i));
            this.counter = i;
        }
    }

    public onItemTap(args) {
        console.log("------------------------ ItemTapped: " + args.index);
    }


    public tabviewloaded(args){
        var tabview:TabView = <TabView>args.object;
        if(isAndroid){
         (<any>tabview)._viewPager.setOffscreenPageLimit(3);
        }
    }
}

Regards,
@tsonevn

All 6 comments

Hi @adisoftbn,
thank you for your interest in NativeScript.

I reviewed your scenario , however this is something related with the platform. In your case when you have 5 TabView items and has selected the second one, where is the ListView, the next and the previous items has been loaded on the memory and the navigation between the first three items will be instant. However if you have selected the fifth item and return back to this with the ListView, the ListView will always be recreated.

I hope this information helps.
Regards,
@tsonevn

Hey @tsonevn,

Doesn't this help: http://stackoverflow.com/questions/28494637/android-how-to-stop-refreshing-fragments-on-tab-change ?

I saw a lot of android apps that dont have this problem.

Best regards,
Adrian Nicoara

Hi @adisoftbn,
thank you for the additional info.

We will research what is causing slow navigation between the items inside the TabView. I am attaching link to a sample project where this issue could be reproduced. As a temporary solution you could setOffscreenPageLimit method to increase the items that will be loaded in the memory for android.

Workaround

main-page.xml

<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo">
  <TabView id="tabViewContainer" selectedIndex="{{index}}" loaded="loadedTabView">
      <TabView.items>
          <TabViewItem title="Tab 1">
              <TabViewItem.view>
                  <Label text="This is Label in Tab 1" />
              </TabViewItem.view>
          </TabViewItem>
          <TabViewItem title="Tab 2">
              <TabViewItem.view>
                  <GridLayout>
                      <ListView items="{{ source }}"  loaded="onLoaded" itemLoading="onItemLoading" itemTap="onItemTap">
                          <ListView.itemTemplate>
                              <StackLayout>
                                  <Label text="{{title}}" textWrap="true" />
                              </StackLayout>
                          </ListView.itemTemplate>
                      </ListView>
                  </GridLayout>
              </TabViewItem.view>
          </TabViewItem>
           <TabViewItem title="Tab 3">
              <TabViewItem.view>
                  <Label text="This is Label in Tab 3" />
              </TabViewItem.view>
          </TabViewItem>
           <TabViewItem title="Tab 4">
              <TabViewItem.view>
                  <Label text="This is Label in Tab 4" />
              </TabViewItem.view>
          </TabViewItem>
           <TabViewItem title="Tab 5">
              <TabViewItem.view>
                  <Label text="This is Label in Tab 5" />
              </TabViewItem.view>
          </TabViewItem>
      </TabView.items>
  </TabView>

</Page>
import { EventData } from 'data/observable';
import { Page } from 'ui/page';
import { HelloWorldModel } from './main-view-model';
import {ObservableArray} from "data/observable-array";
import {Observable, PropertyChangeData} from "data/observable";
import {TabView} from "ui/tab-view";
import {isAndroid} from "platform"


// Event handler for Page "navigatingTo" event attached in main-page.xml
export function navigatingTo(args: EventData) {
  // Get the event sender
  let page = <Page>args.object;
  let array = new ObservableArray();
  let observable = new Observable();
  observable.set("source", array);
  for(var i=0; i<500; i++){
    array.push({title:"title "+i});
  }
  page.bindingContext = observable;
}


export function loadedTabView(args:EventData){
  let tabview:TabView = <TabView>args.object;
  console.log((<any>tabview)._viewPager);
  if(isAndroid){
    (<any>tabview)._viewPager.setOffscreenPageLimit(3);
  }
}

Angular

app.component.html

<TabView #tabview (loaded)="tabviewloaded($event)">
  <StackLayout *tabItem="{title: 'Tab1'}">
    <Label text="This is Label in Tab 1"></Label>
  </StackLayout>
  <StackLayout *tabItem="{title: 'Tab2'}">
    <ListView [items]="myItems" (itemTap)="onItemTap($event)">
        <template let-item="item" let-i="index" let-odd="odd" let-even="even">
            <StackLayout [class.odd]="odd" [class.even]="even">
                <Label [text]='"index: " + i'></Label>
                <Label [text]='"[" + item.id +"] " + item.name'></Label>
            </StackLayout>
        </template>
    </ListView>
  </StackLayout>
  <StackLayout *tabItem="{title: 'Tab3'}">
    <Label text="This is Label in Tab 3"></Label>
  </StackLayout>
  <StackLayout *tabItem="{title: 'Tab4'}">
    <Label text="This is Label in Tab 4"></Label>
  </StackLayout>
  <StackLayout *tabItem="{title: 'Tab5'}">
    <Label text="This is Label in Tab 5"></Label>
  </StackLayout>
</TabView>

app.component.ts

import {Component, OnInit} from "@angular/core";
import {isAndroid} from "platform";
import {TabView} from "ui/tab-view"

class DataItem {
    constructor(public id: number, public name: string) { }
}


@Component({
    selector: "my-app",
    templateUrl: "app.component.html",
})
export class AppComponent {
    public myItems: Array<DataItem>;
    private counter: number;

    constructor() {

    }
    ngOnInit(){
        this.myItems = [];
        this.counter = 0;
        for (var i = 0; i < 500; i++) {
            this.myItems.push(new DataItem(i, "data item " + i));
            this.counter = i;
        }
    }

    public onItemTap(args) {
        console.log("------------------------ ItemTapped: " + args.index);
    }


    public tabviewloaded(args){
        var tabview:TabView = <TabView>args.object;
        if(isAndroid){
         (<any>tabview)._viewPager.setOffscreenPageLimit(3);
        }
    }
}

Regards,
@tsonevn

Thank you 馃憤

You rock :D works like a charm!! 100% fluid!!
Changed the value to 4 to be able to test it more further with memory consumption and so on.

Best regards,
Adrian Nicoara

androidOffscreenTabLimit property doesn't work fine when position is bottom. More detail is in this issue.

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

NordlingDev picture NordlingDev  路  3Comments

dhanalakshmitawwa picture dhanalakshmitawwa  路  3Comments

valentinstoychev picture valentinstoychev  路  3Comments

kn9ts picture kn9ts  路  3Comments

pocesar picture pocesar  路  3Comments