跳到主要内容
版本:v3

实现 Android 平台功能

插件开发已接近尾声,现在只需完成 Android 平台的实现!

向 Capacitor 注册插件

前提条件: 继续之前请先熟悉 Capacitor 原生 Android 代码开发文档

通过运行 npx cap open android 在 Android Studio 中打开 Capacitor 应用的 Android 项目。展开 app 模块下的 java 文件夹,右键点击应用的 Java 包,选择 New -> Package 创建名为 plugins 的子包。再右键点击 plugins 包,重复上述过程创建 ScreenOrientation 子包。

接着右键 ScreenOrientation 包,选择 New -> Java File 创建新文件 ScreenOrientationPlugin.java,同样方法再创建 ScreenOrientation.java

将以下代码复制到 ScreenOrientationPlugin.java

package io.ionic.cap.plugin.plugins.ScreenOrientation;

import com.getcapacitor.Plugin;
import com.getcapacitor.PluginCall;
import com.getcapacitor.PluginMethod;
import com.getcapacitor.annotation.CapacitorPlugin;

@CapacitorPlugin(name = "ScreenOrientation")
public class ScreenOrientationPlugin extends Plugin {

@PluginMethod()
public void orientation(PluginCall call) {
call.resolve();
}

@PluginMethod()
public void lock(PluginCall call) {
call.resolve();
}

@PluginMethod()
public void unlock(PluginCall call) {
call.resolve();
}
}

在项目的 MainActivity 中注册插件类以实现 Java 与 JavaScript 的桥接。打开 MainActivity.java 添加 onCreate() 方法:

package io.ionic.cap.plugin;

import android.os.Bundle;
import com.getcapacitor.BridgeActivity;
import io.ionic.cap.plugin.plugins.ScreenOrientation.ScreenOrientationPlugin;

public class MainActivity extends BridgeActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
registerPlugin(ScreenOrientationPlugin.class);
}
}

获取当前屏幕方向

与 iOS 实现类似,我们先处理获取当前屏幕方向的功能。打开 ScreenOrientation.java 设置类结构并编写获取方向的方法:

package io.ionic.cap.plugin.plugins.ScreenOrientation;

import android.view.Surface;
import androidx.appcompat.app.AppCompatActivity;

public class ScreenOrientation {
private AppCompatActivity activity;

public ScreenOrientation(AppCompatActivity activity) {
this.activity = activity;
}

public String getCurrentOrientationType() {
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
return fromRotationToOrientationType(rotation);
}

private String fromRotationToOrientationType(int rotation) {
switch (rotation) {
case Surface.ROTATION_90:
return "landscape-primary";
case Surface.ROTATION_180:
return "portrait-secondary";
case Surface.ROTATION_270:
return "landscape-secondary";
default:
return "portrait-primary";
}
}
}

然后在 ScreenOrientationPlugin.java 中关联 orientation 方法:

package io.ionic.cap.plugins.ScreenOrientation;

import com.getcapacitor.JSObject;
/* 其余导入省略 */

@CapacitorPlugin(name = "ScreenOrientation")
public class ScreenOrientationPlugin extends Plugin {

private ScreenOrientation implementation;

@Override
public void load() {
implementation = new ScreenOrientation(getActivity());
}

@PluginMethod()
public void orientation(PluginCall call) {
JSObject ret = new JSObject();
String type = implementation.getCurrentOrientationType();
ret.put("type", type);
call.resolve(ret);
}

/* 其余代码省略 */
}

load() 方法是初始化 ScreenOrientation 类实例的最佳位置。

在 Android Studio 中运行应用(真机或模拟器),查看 Logcat 应能看到调用日志:

V/Capacitor/Plugin: To native (Capacitor plugin): callbackId: 89582874, pluginId: ScreenOrientation, methodName: orientation

注意: 日志中的具体值会不同,示例中的 89582874 是插件方法调用的随机 ID。

监听屏幕方向变化

Android 将设备旋转视为运行时配置变更,我们需要让插件能够处理配置变更

Capacitor 提供可重写方法 handleOnConfigurationChanged() 来响应这类变更。

首先在 ScreenOrientationPlugin 类中添加导入:

import android.content.res.Configuration;

然后添加以下方法:

@Override
public void handleOnConfigurationChanged(Configuration newConfig) {
super.handleOnConfigurationChanged(newConfig);
this.onOrientationChanged();
}

private void onOrientationChanged() {
JSObject ret = new JSObject();
String type = implementation.getCurrentOrientationType();
ret.put("type", type);
notifyListeners("screenOrientationChange", ret);
}

Android 在配置变更时会返回完整的新配置对象,这带来两个问题:

  1. 如何确保只在方向变化时通知监听器?
  2. 如何确定配置变更是由方向变化引起?

我们需要让插件记录之前的 newConfig.orientation 值用于比较。

ScreenOrientation 类中添加:

@Nullable private int configOrientation;

public boolean hasOrientationChanged(int orientation) {
if (orientation == configOrientation) {
return false;
} else {
this.configOrientation = orientation;
return true;
}
}

记得为 ScreenOrientation.java 导入 androidx.annotation.Nullable

然后更新 ScreenOrientationPlugin.java 中的 handleOnConfigurationChanged()

@Override
public void handleOnConfigurationChanged(Configuration newConfig) {
super.handleOnConfigurationChanged(newConfig);
if(implementation.hasOrientationChanged(newConfig.orientation)) {
this.onOrientationChanged();
}
}

现在插件只会在方向确实发生变化时通知监听器。

锁定与解锁屏幕方向

与 iOS 实现类似,我们需要将 JavaScript 的 OrientationType 映射为原生枚举值。Android 需映射到 ActivityInfo 的枚举值。在 ScreenOrientation 类中添加:

private int fromOrientationTypeToEnum(String orientationType) {
switch (orientationType) {
case "landscape-primary":
return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
case "landscape-secondary":
return ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
case "portrait-secondary":
return ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
default:
// 默认 portrait-primary
return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
}
}

确保为 ScreenOrientation.java 导入 android.content.pm.ActivityInfo

接着在 ScreenOrientation 类中添加 lock() 方法:

public void lock(String orientationType) {
int orientationEnum = fromOrientationTypeToEnum(orientationType);
activity.setRequestedOrientation(orientationEnum);
}

ScreenOrientationPlugin 类调用该方法:

@PluginMethod()
public void lock(PluginCall call) {
String orientationType = call.getString("orientation");
if(orientationType == null) {
call.reject("必须提供 'orientation' 参数");
return;
}
implementation.lock(orientationType);
call.resolve();
}

注意我们检查了 orientation 参数是否为空。

要解锁屏幕方向,需将 activity 方向设为未指定值。在 ScreenOrientation 类中添加:

public void unlock() {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}

然后在 ScreenOrientationPlugin 类中调用:

@PluginMethod()
public void unlock(PluginCall call) {
implementation.unlock();
call.resolve();
}

测试功能!

在 Android Studio 中运行应用(真机或模拟器)。点击 "Rotate My Device" 按钮将使屏幕旋转至横屏模式,继续旋转可看到方向已被锁定。点击 "Confirm Signature" 将解锁屏幕方向。

注意: 测试前请确保设备的 自动旋转 设置已开启。

恭喜!你已成功构建了支持 Web、iOS 和 Android 的 Capacitor 插件!👏 👏 👏

目前 ScreenOrientation 插件是本地插件,仅供当前应用使用。这完全没问题!很多时候插件只需在特定应用中使用。不过如果你想在多应用中复用插件,最后一步我们将学习如何打包插件。