PS:本文介绍如何使用前端 React 开发 Android 外壳应用,以及如何在页面中调用 Android 硬件功能,以扫码、定位、NFC 为例。
React 构建 Android 外壳
一、创建项目
npm create vite@latest
二、添加依赖
1、核心插件 CapacitorJS
2. 安装插件
npm i @capacitor/core
npm i -D @capacitor/cli
3、初始化 Capacitor
npx cap init
运行后会创建 capacitor.config.json 文件,该文件记录了项目构建的输出目录(webDir),通常对应 Angular 项目的 www、React 项目的 build、Vue 项目的 public 等目录。
{
"appId": "com.sggk.dongte.warehouse.app",
"appName": "XXXApp安装后显示的名称",
"webDir": "dist"
}
三、创建 Android 项目
安装 Capacitor 核心运行时后,即可添加 Android 平台支持。
npm i @capacitor/android
# 创建 Android 项目
npx cap add android
运行后,会在项目根目录生成一个 android 文件夹,其中包含了转换后的 Android 项目代码。
同步代码
创建本地项目后,您可以通过运行以下命令将 Web 应用程序同步到本地项目。
npx cap sync
[^]: npx cap sync 会将您构建的 Web 包(默认位于 Capacitor 配置文件的 webDir 目录中)复制到您的本地项目,并安装本地项目的依赖项。如果修改了 Capacitor 配置文件,也会同步过去。也就是说,项目初始化后,后续任何配置或代码的更改,都只需运行 npx cap sync 命令来同步。
将同步命令添加到 package.json 的 scripts 中,之后运行 npm run build:cap 命令即可构建并同步代码到 Android 项目。
"build:cap": "vite build && npx cap sync",
四、添加扫码、定位、NFC相关依赖
# 按需安装你使用的插件
npm install @capacitor/barcode-scanner # 扫码
npm install @capacitor/geolocation # 定位
npm install @capgo/capacitor-nfc # NFC
npm install @capacitor/app # 首页或登录页面返回
# 安装 sg-capacitor-bridge 插件
Capacitor 插件的 iframe + postMessage 桥接库。用于 Android 包装应用通过 iframe 加载远程网页应用时,让网页应用可以调用父窗口的 Capacitor 原生插件。
npm i git+https://gitee.com/cc_nbplus/android-ifream-calls-hardware.git
五、前端页面(Android 外壳)
1、capacitor.config.ts 配置
import type {
CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'com.xx.xx.app', // 安卓包名
appName: 'xxx 程序安装后桌面显示的名称',
webDir: 'dist',
plugins: {
CapacitorHttp: {
enabled: true // 跨域
}
},
server: {
androidScheme: 'http' // 保证 HTTPS 请求成功
}
};
export default config;
2、路由 routers
import { createHashRouter, Navigate } from "react-router";
import Home from "../pages/Home";
import BackButtonGuard from "../components/BackButtonGuard";
const router = createHashRouter([
{
element: <BackButtonGuard />, // 用于首页或登录页再次返回退出程序
children: [
{"path": "/home", element: <Home/>},
{"path": "/", element: <Navigate to={"/home"}/>}
]
}
])
export default router;
3. App.tsx
import {RouterProvider} from "react-router";
import routers from "@/routers";
function App() {
return <RouterProvider router={routers} />
}
export default App
4.Home.tsx
import {useEffect, useRef, useState} from 'react'
import {createBridgeHost, builtins} from 'sg-capacitor-bridge'
import {CapacitorBarcodeScanner} from '@capacitor/barcode-scanner'
import {App} from "@capacitor/app";
// import {Camera} from "@capacitor/camera"
const REMOTE_URL = 'http://yflxt.fs-ss.com:9662/fsInspectionTest/'
export default function Home() {
const iframeRef = useRef<HTMLIFrameElement>(null)
const [toast, setToast] = useState<string | null>(null)
useEffect(() => {
const host = createBridgeHost({
getIframe: () => iframeRef.current,
})
// 注册扫码插件
host.use(builtins.barcodeScanner.setup(CapacitorBarcodeScanner))
const cleanupBackButton = builtins.navigation.setupBackButton({
host,
app: App,
onExitPrompt: () => {
setToast('再按一次退出应用')
setTimeout(() => setToast(null), 2000)
}
})
// host.use(builtins.camera.setup(Camera))
return () => {
cleanupBackButton()
host.destroy()
}
}, [])
return (
<>
<iframe
ref={iframeRef}
src={REMOTE_URL}
style={
{
position: 'fixed',
top: 0,
left: 0,
width: '100vw',
height: '100vh',
border: 'none',
margin: 0,
padding: 0,
}}
/>
{toast && (
<div style={
{
position: 'fixed', bottom: 80, left: '50%', transform: 'translateX(-50%)',
background: 'rgba(0,0,0,0.7)', color: '#fff', padding: '10px 24px',
borderRadius: 20, fontSize: 14, zIndex: 9999, pointerEvents: 'none',
}}>
{toast}
</div>
)}
</>
)
}
5. 首页或登录页再次返回退出程序(ifream不可用,可用在后面)
hooks/useBackButtonHandler.ts
import {
useEffect, useRef } from "react";
import {
App, type BackButtonListenerEvent } from "@capacitor/app";
import {
useLocation, useNavigate

245

被折叠的 条评论
为什么被折叠?



