8.7 发布—WinterCG 兼容性第一部分
了解更多

扩展 Kotlin 和 Java 类

子类化 Kotlin 和 Java 类

以下示例演示如何在 NativeScript 中子类化 Kotlin/Java 类

kotlin
class MyButton(context: Context) : android.widget.Button(context) {
    override fun setEnabled(enabled: Boolean) {
        super.setEnabled(enabled)
    }
}

val btn = MyButton(context)
java
public class MyButton extends android.widget.Button {
    public MyButton(Context context) {
        super(context);
    }

    @Override
    public void setEnabled(boolean enabled) {
        super.setEnabled(enabled);
    }
}

MyButton btn = new MyButton(context);

在 NativeScript 中执行相同的操作

ts
let constructorCalled = false

@NativeClass
class MyButton extends android.widget.Button {
  constructor() {
    super()
    constructorCalled = true

    // necessary when extending TypeScript constructors
    return global.__native(this)
  }

  setEnabled(enabled: boolean): void {
    this.super.setEnabled(enabled)
  }
}

const button = new MyButton(context)
js
let constructorCalled = false
const MyButton = android.widget.Button.extend({
  // constructor
  init: function () {
    constructorCalled = true
  },

  setEnabled: function (enabled) {
    this.super.setEnabled(enabled)
  },
})

const button = new MyButton(context)

注意

在上面的示例中,setEnabled 函数中 this 关键字的使用指向代理扩展原生实例的 JavaScript 对象。this.super 的使用提供了对基类方法实现的访问。

创建从基 Java java.lang.Object 类扩展的匿名 Java 类

ts
@NativeClass
class MyClass extends java.lang.Object {
  constructor() {
    super()
    // necessary when extending TypeScript constructors
    return global.__native(this)
  }

  toString(): string {
    // override Object's toString
  }
}

const myClassInstance = new MyClass()
js
const MyClass = java.lang.Object({
  init() {
    // constructor
  },

  toString() {
    // override Object's toString
  },
})

const myClassInstance = new MyClass()

创建命名 Java 类

要创建从 java.lang.Object 类扩展的命名 Java 类

ts
@NativeClass
@JavaProxy('org.nativescript.example.MyClass')
class MyClass extends java.lang.Object {
  // constructor
  constructor() {
    super()
    // necessary when extending TypeScript constructors
    return global.__native(this)
  }

  toString(): string {
    // override Object's toString
  }
}

const myClassInstance = new MyClass()

// note: this may result in a TypeScript error because the namespace is not typed
// this can be safely ignored with // @ts-ignore
// or by type-casting to `any` for example.
const myClassInstance2 = new org.nativescript.example.MyClass()
const myClassInstance3 = new (org as any).nativescript.example.MyClass()
js
const MyClass = java.lang.Object('org.nativescript.example.MyClass', {
  init() {
    // constructor
  },

  toString() {
    // override Object's toString
  },
})

const myClassInstance = new MyClass()
const myClassInstance2 = new org.nativescript.example.MyClass()

提示

在处理 NativeScript 中的类扩展和接口实现时需要注意的一点是,与 Java 不同,在 Java 中可以使用 new java.arbitrary.abstract.Class() { } 扩展抽象类,在 NativeScript 中,类需要根据前面的示例进行扩展 - 在 java.arbitrary.abstract.Class 上使用 extend 函数,或者在 TypeScript 中使用 extends 类语法。

自定义 Android 应用程序和活动

NativeScript 提供了一种方法来创建自定义 android.app.Applicationandroid.app.Activity 实现。

扩展 Android 应用程序

在项目文件夹的根目录中创建一个新的 TypeScript 文件 - 如果您使用的是纯 JS,则将其命名为 application.android.tsapplication.android.js

注意

注意 *.android 后缀 - 我们希望此文件仅打包到 Android。

自定义应用程序类的最小示例

ts
// the `JavaProxy` decorator specifies the package and the name for the native *.JAVA file generated.
@NativeClass()
@JavaProxy('org.nativescript.example.Application')
class Application extends android.app.Application {
  public onCreate(): void {
    super.onCreate()

    // At this point modules have already been initialized
    // Enter custom initialization code here
  }

  public attachBaseContext(baseContext: android.content.Context) {
    super.attachBaseContext(baseContext)

    // This code enables MultiDex support for the application (if needed)
    // androidx.multidex.MultiDex.install(this);
  }
}
js
const superProto = android.app.Application.prototype

// the first parameter of the `extend` call defines the package and the name for the native *.JAVA file generated.
android.app.Application.extend('org.nativescript.example.Application', {
  onCreate: function () {
    superProto.onCreate.call(this)

    // At this point modules have already been initialized
    // Enter custom initialization code here
  },
  attachBaseContext: function (base) {
    superProto.attachBaseContext.call(this, base)
    // This code enables MultiDex support for the application (if needed compile androidx.multidex:multidex)
    // androidx.multidex.MultiDex.install(this);
  },
})

要使用自定义应用程序类,请修改 App_Resources/Android/ 中的 AndroidManifest.xml 中的应用程序条目

xml
<application
  android:name="org.nativescript.example.Application"
  android:allowBackup="true"
  android:icon="@mipmap/ic_launcher"
  android:label="@string/app_name"
  android:theme="@style/AppTheme">

最后,为了正确包含自定义应用程序类,扩展的 Android 应用程序应添加到 webpack.config.js 文件中。

js
const webpack = require('@nativescript/webpack')

module.exports = (env) => {
  webpack.init(env)

  webpack.chainWebpack((config) => {
    if (webpack.Utils.platform.getPlatformName() === 'android') {
      // make sure the path to the applicatioon.android.(js|ts)
      // is relative to the webpack.config.js
      config.entry('application').add('./application.android')
    }
  })

  return webpack.resolveConfig()
}

application.android.ts 的源代码被单独捆绑为 application.js 文件,该文件在启动时从本机 Application.java 类加载。

bundle.jsvendor.js 文件在应用程序启动时加载不够早。这就是为什么 application.android.ts 中的逻辑需要被单独捆绑,以便在应用程序生命周期的需要时尽快加载。

注意

如果 application.android.ts 需要外部模块,这种方法将不起作用。

扩展 Android 活动

@nativescript/core 附带默认的 androidx.appcompat.app.AppCompatActivity 实现,它引导 NativeScript 应用程序,而无需强制用户在每个项目中声明其自定义活动。在某些情况下,您可能需要实现自定义 Android 活动。

创建新的 activity.android.ts 或使用纯 JS 时创建 activity.android.js

注意

注意 .android 后缀 - 我们只希望此文件在 Android 上。

基本活动可以按如下方式实现

ts
import {
  Frame,
  Application,
  setActivityCallbacks,
  AndroidActivityCallbacks,
} from '@nativescript/core'

@NativeClass()
@JavaProxy('org.nativescript.example.CustomActivity')
class CustomActivity extends androidx.appcompat.app.AppCompatActivity {
  public isNativeScriptActivity

  private _callbacks: AndroidActivityCallbacks

  public onCreate(savedInstanceState: android.os.Bundle): void {
    Application.android.init(this.getApplication())
    // Set the isNativeScriptActivity in onCreate (as done in the original NativeScript activity code)
    // The JS constructor might not be called because the activity is created from Android.
    this.isNativeScriptActivity = true
    if (!this._callbacks) {
      setActivityCallbacks(this)
    }

    this._callbacks.onCreate(
      this,
      savedInstanceState,
      this.getIntent(),
      super.onCreate
    )
  }

  public onNewIntent(intent: android.content.Intent): void {
    this._callbacks.onNewIntent(
      this,
      intent,
      super.setIntent,
      super.onNewIntent
    )
  }

  public onSaveInstanceState(outState: android.os.Bundle): void {
    this._callbacks.onSaveInstanceState(
      this,
      outState,
      super.onSaveInstanceState
    )
  }

  public onStart(): void {
    this._callbacks.onStart(this, super.onStart)
  }

  public onStop(): void {
    this._callbacks.onStop(this, super.onStop)
  }

  public onDestroy(): void {
    this._callbacks.onDestroy(this, super.onDestroy)
  }

  public onPostResume(): void {
    this._callbacks.onPostResume(this, super.onPostResume)
  }

  public onBackPressed(): void {
    this._callbacks.onBackPressed(this, super.onBackPressed)
  }

  public onRequestPermissionsResult(
    requestCode: number,
    permissions: Array<string>,
    grantResults: Array<number>
  ): void {
    this._callbacks.onRequestPermissionsResult(
      this,
      requestCode,
      permissions,
      grantResults,
      undefined /*TODO: Enable if needed*/
    )
  }

  public onActivityResult(
    requestCode: number,
    resultCode: number,
    data: android.content.Intent
  ): void {
    this._callbacks.onActivityResult(
      this,
      requestCode,
      resultCode,
      data,
      super.onActivityResult
    )
  }
}
js
import { Frame, Application, setActivityCallbacks } from '@nativescript/core'

const superProto = androidx.appcompat.app.AppCompatActivity.prototype

androidx.appcompat.app.AppCompatActivity.extend(
  'org.nativescript.example.CustomActivity',
  {
    onCreate(savedInstanceState) {
      // Used to make sure the App is inited in case onCreate is called before the rest of the framework
      Application.android.init(this.getApplication())

      // Set the isNativeScriptActivity in onCreate (as done in the original NativeScript activity code)
      // The JS constructor might not be called because the activity is created from Android.
      this.isNativeScriptActivity = true
      if (!this._callbacks) {
        setActivityCallbacks(this)
      }
      // Modules will take care of calling super.onCreate, do not call it here
      this._callbacks.onCreate(
        this,
        savedInstanceState,
        this.getIntent(),
        superProto.onCreate
      )

      // Add custom initialization logic here
    },
    onNewIntent(intent) {
      this._callbacks.onNewIntent(
        this,
        intent,
        superProto.setIntent,
        superProto.onNewIntent
      )
    },
    onSaveInstanceState(outState) {
      this._callbacks.onSaveInstanceState(
        this,
        outState,
        superProto.onSaveInstanceState
      )
    },
    onStart() {
      this._callbacks.onStart(this, superProto.onStart)
    },
    onStop() {
      this._callbacks.onStop(this, superProto.onStop)
    },
    onDestroy() {
      this._callbacks.onDestroy(this, superProto.onDestroy)
    },
    onPostResume() {
      this._callbacks.onPostResume(this, superProto.onPostResume)
    },
    onBackPressed() {
      this._callbacks.onBackPressed(this, superProto.onBackPressed)
    },
    onRequestPermissionsResult(requestCode, permissions, grantResults) {
      this._callbacks.onRequestPermissionsResult(
        this,
        requestCode,
        permissions,
        grantResults,
        undefined
      )
    },
    onActivityResult(requestCode, resultCode, data) {
      this._callbacks.onActivityResult(
        this,
        requestCode,
        resultCode,
        data,
        superProto.onActivityResult
      )
    },
    /* Add any other events you need to capture */
  }
)

注意

this._callbacks 属性由 frame.setActivityCallbacks 方法自动分配给您的扩展类。它实现了 AndroidActivityCallbacks 接口 并允许核心在重要活动事件发生时收到通知。使用这些回调非常重要,因为 NativeScript 的许多部分都依赖于它们!

接下来,修改 App_Resources/Android/src/main/AndroidManifest.xml 中的活动

xml
<activity
  android:name="org.nativescript.example.CustomActivity"
  android:label="@string/title_activity_kimera"
  android:configChanges="keyboardHidden|orientation|screenSize">

要将新活动包含在构建中,请确保使用以下方法将其添加到 webpack.config.js

js
const webpack = require('@nativescript/webpack')

module.exports = (env) => {
  env.appComponents = (env.appComponents || []).concat(['./activity.android'])

  webpack.init(env)

  return webpack.resolveConfig()
}

实现 Kotlin 和 Java 接口

下一个示例展示了如何在 NativeScript 中使用 Kotlin/Java 实现接口。在 NativeScript 中继承类和实现接口之间的主要区别在于 extend 关键字的使用。通过将实现对象传递给接口构造函数来实现接口。语法与 Java 匿名类 相同。

kotlin
button.setOnClickListener {
    // Perform action on click
}
java
button.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
        // Perform action on click
    }
});

可以在 NativeScript 中实现相同的操作

ts
button.setOnClickListener(
  new android.view.View.OnClickListener({
    onClick() {
      // Perform action on click
    },
  })
)

或者,您可以使用以下模式来使用 TypeScript 实现命名接口

ts
@NativeClass()
@Interfaces([android.view.View.OnClickListener])
class ClickListener
  extends java.lang.Object
  implements android.view.View.OnClickListener
{
  constructor() {
    super()

    // necessary when extending TypeScript constructors
    return global.__native(this)
  }

  onClick(view: android.view.View): void {
    // Perform action on click
  }
}

nativeView.setOnClickListener(new ClickListener())

或使用 JavaScript

js
const ClickListener = java.lang.Object.extend({
  // the interfaces to use
  interfaces: [android.view.View.OnClickListener],
  onClick() {
    // Perform action on click
  },
})

nativeView.setOnClickListener(new ClickListener())
下一页
iOS