NativeScript: Disable all controls while ActivityIndicator is shown

早过忘川 提交于 2019-12-02 04:23:49
Nathanael

There are a couple way you can do this;

  1. You can use a ngIf or binding on isEnabled to disable it based on a data bound value.

  2. You can create a simple routine that you call (my preferred method).

require("nativescript-dom"); 
function screenEnabled(isEnabled) {
       runAgainstTagNames('TextEdit', function(e) { e.isEnabled = isEnabled; });
       runAgainstTagNames('Button', function(e) { e.isEnabled = isEnabled; }); 
}

The nativescript-dom plugin has the runAgainst*, or getElementBy* wrappers to talk to the native layer like you were talking to a html dom.

Full disclosure, I'm the author of nativescript-dom, it is one of the plugins that I use in almost every app/demo I do.

Here is my solution for NativeScript+Angular:

  1. setControlInteractionState() is recursive.
  2. the TextField cursor is hidden (using native android API).

XML:

<GridLayout #mainGrid rows="*" columns="*">  
  <!-- Main page content here... -->
  <GridLayout *ngIf="isBusy" rows="*" columns="*">
     <GridLayout rows="*" columns="*" style="background-color: black; opacity: 0.35">
     </GridLayout>
     <ActivityIndicator width="60" height="60" busy="true">
     </ActivityIndicator>
  </GridLayout>
</GridLayout>

or

<GridLayout #mainGrid rows="*" columns="*">  
  <!-- Main page content here... -->      
</GridLayout>
<GridLayout *ngIf="isBusy" rows="*" columns="*">
   <GridLayout rows="*" columns="*" style="background-color: black; opacity: 0.35">
   </GridLayout>
   <ActivityIndicator width="60" height="60" busy="true">
   </ActivityIndicator>
</GridLayout>

TypeScript:

import { Component, ViewChild, ElementRef } from "@angular/core";
import { View } from "ui/core/view";
import { LayoutBase } from "ui/layouts/layout-base";
import { isAndroid, isIOS } from "platform";

@Component({
    templateUrl: "./SignIn.html"
})
export class SignInComponent {

    @ViewChild("mainGrid")
    MainGrid: ElementRef;

    isBusy: boolean = false;

    submit() : void {
        try {
            this.isBusy = true;
            setControlInteractionState(<View>this.MainGrid.nativeElement, false);
            //sign-in here...
        }
        finally {
            this.isBusy = false;
            setControlInteractionState(<View>this.MainGrid.nativeElement, true);
        }
    }

    setControlInteractionState(view: View, isEnabled: boolean) : void {
        view.isUserInteractionEnabled = isEnabled;
        if (isAndroid) {
            if (view.android instanceof android.widget.EditText) {
                let control = <android.widget.EditText>view.android;
                control.setCursorVisible(isEnabled);
            }
        }
        if (view instanceof LayoutBase) {
            let layoutBase = <LayoutBase>view;
            for (let i = 0, length = layoutBase.getChildrenCount(); i < length; i++) {
                let child = layoutBase.getChildAt(i);
                setControlInteractionState(child, isEnabled);
            }
        }
    }

}

NS 2.5.0

Expanding @KTCO's answer to get the size of the overlay exactly the same as the main grid:

import { Size, View } from "tns-core-modules/ui/core/view";
import { GridLayout } from "tns-core-modules/ui/layouts/grid-layout/grid-layout";
...
...
dialogSize: Size;
mainGrid: GridLayout;
...
submit() {
  this.mainGrid = <GridLayout>this.MainGrid.nativeElement;
  this.dialogSize = this.mainGrid.getActualSize();
  .....
  .....

<GridLayout *ngIf="isBusy" rows="auto" columns="auto">
  <GridLayout rows="*" columns="*" [width]="dialogSize.width" [height]="dialogSize.height" style="background-color: black; opacity: 0.35">
  </GridLayout>
  <ActivityIndicator width="50" height="50" busy="true">
  </ActivityIndicator>
</GridLayout>

I know this is a little old, but sometimes I just got to do things my way. If you want to accomplish this programmatically:


const excludedView = someViewToBeExcluded;

const enabler = (parentView:View, enable:boolean) => {
  parentView.eachChildView(childView => {
    if (childView != excludedView) {
      enabler(childView, enable);
      childView.isEnabled = enable;
    }          
    return true;
  });
};

enabler(page, false);

Note: This will not disable/enable the initial parentView (ie. the page in this example)

I found an easiest solution in Angular, so i am posting here for any future reference. First in app.component.html file i added a Grid and ScrollView like following:

 <GridLayout>
    <page-router-outlet></page-router-outlet>
    <!-- hack to block UI -->
    <ScrollView isUserInteractionEnabled="false" *ngIf="isLoading">
        <ActivityIndicator busy="true"></ActivityIndicator>     
    </ScrollView>
</GridLayout>

Notice the page-router-outlet which is inside the Grid. By default it will be place at row="0". The next thing is ScrollView which has isUserInteractionEnabled set to false. Now in your app.component.ts file add a a variable called isLoading and toggle it using some kind of events e.g RxJs Observable events

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!