高级概念
Android 子类化和实现接口
NativeScript 为您提供对原生平台的完全访问权限,您可以在 JavaScript/TypeScript 代码中子类化原生类、实现原生接口等等。
扩展 Kotlin 和 Java 类
子类化 Kotlin 和 Java 类
以下示例演示如何在 NativeScript 中子类化 Kotlin/Java 类
class MyButton(context: Context) : android.widget.Button(context) {
override fun setEnabled(enabled: Boolean) {
super.setEnabled(enabled)
}
}
val btn = MyButton(context)
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 中执行相同的操作
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)
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 类
@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()
const MyClass = java.lang.Object({
init() {
// constructor
},
toString() {
// override Object's toString
},
})
const myClassInstance = new MyClass()
创建命名 Java 类
要创建从 java.lang.Object
类扩展的命名 Java 类
@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()
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.Application
和 android.app.Activity 实现。
扩展 Android 应用程序
在项目文件夹的根目录中创建一个新的 TypeScript 文件 - 如果您使用的是纯 JS,则将其命名为 application.android.ts
或 application.android.js
。
注意
注意 *.android
后缀 - 我们希望此文件仅打包到 Android。
自定义应用程序类的最小示例
// 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);
}
}
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
中的应用程序条目
<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 文件中。
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.js
和 vendor.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 上。
基本活动可以按如下方式实现
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
)
}
}
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
中的活动
<activity
android:name="org.nativescript.example.CustomActivity"
android:label="@string/title_activity_kimera"
android:configChanges="keyboardHidden|orientation|screenSize">
要将新活动包含在构建中,请确保使用以下方法将其添加到 webpack.config.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 匿名类 相同。
button.setOnClickListener {
// Perform action on click
}
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Perform action on click
}
});
可以在 NativeScript 中实现相同的操作
button.setOnClickListener(
new android.view.View.OnClickListener({
onClick() {
// Perform action on click
},
})
)
或者,您可以使用以下模式来使用 TypeScript 实现命名接口
@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
const ClickListener = java.lang.Object.extend({
// the interfaces to use
interfaces: [android.view.View.OnClickListener],
onClick() {
// Perform action on click
},
})
nativeView.setOnClickListener(new ClickListener())