使用 React + Capacitor 构建 Android 混合应用外壳:集成扫码、定位与 NFC 功能实战

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.jsonscripts 中,之后运行 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 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CC_Waiting

我,大学未工作,感谢您的打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值