【笔记】Trae+Andrioid Studio+Kotlin开发安卓WebView应用

article/2025/9/7 20:33:35

文章目录

  • 简介
  • 依赖
  • 步骤
    • AS(Andriod Studio)创建项目
    • AS创建虚拟机
    • TRAE CN 修改项目
      • 新增按键捕获功能
    • 新增WebView
    • WebView加载本地资源
      • 在按键回调中向WebView注入JS代码
  • 最终关键代码
  • 吐槽

简介

使用Trae配合Andriod Studio开发一个内嵌WebView的安卓应用, 在WebView中加载本地资源, 在APP中捕获按键事件对WebView中的内容进行操作;

依赖

  • Trae CN (https://www.trae.com.cn/)
  • Andriod Studio (https://developer.android.google.cn/studio?hl=zh-cn), 以下简称AS
    • 吃内存, 占用了我大约6GB内存
    • 下载项目依赖和安卓虚拟机(约2GB)依赖网络
  • 基础的编程知识

步骤

AS(Andriod Studio)创建项目

  • 打开AS, 创建一个Empty Activity
    在这里插入图片描述
    在这里插入图片描述
  • 在AS打开新窗口后, 等待右下角进度条初始化完毕(下载依赖和编译)

AS创建虚拟机

  • 在右上角创建虚拟机
    在这里插入图片描述
    在这里插入图片描述
  • 找到镜像, 如果是灰色的, 需要点击下载图标进入下载界面(大约2G, 非常依赖网络)
    在这里插入图片描述
    在这里插入图片描述
  • 启动模板项目
    在这里插入图片描述
    在这里插入图片描述

TRAE CN 修改项目

  • 使用Trae打开该文件夹
  • 按下Ctrl+P, 在顶部打开的输入框中输入Main, 选择MainActivity.kt
    在这里插入图片描述
  • 按下Ctrl+U开启右侧AI工具

新增按键捕获功能

  • 输入提示"我想要安卓应用可以捕获键盘的上下2个方向键并打印日志信息"

    • 生成完毕后, 在提示框的上方选择"全部接受"
      在这里插入图片描述
  • 打开AS, 会自动刷新代码, 点击右上角的绿色重启按钮或者启动按钮重新编译和启动
    在这里插入图片描述
    在这里插入图片描述

  • 切换到日志查看界面
    在这里插入图片描述

  • 鼠标点击模拟器中的应用的白色区域, 后按下上下键, 可看到日志输出

新增WebView

  • 输入提示"我想将Greeting中的Text控件替换为Webview"
    • 生成完毕后, 选择"全部接受"
  • 这里有个报错信息, 把鼠标移动到红色的文字上会给出提示, 点击import修复一下
    在这里插入图片描述
  • 回到AS, 点击右上角的绿色重启按钮或者启动按钮重新编译和启动
  • 出现了网络连接的错误
    在这里插入图片描述
  • 回到TRAE, 输入提示信息界面提示"webpage not avalible"
    • 在点击"全部接受"前, 我看了修复内容, 它没添加网络权限还把地址改成google了, 这个方案是不行的, 所以点击"全部拒绝"
  • 重新输入提示"应该是没有配置网络权限, 修复一下"
    • 它给"AndroidManifest.xml"的文件内容清空了, 添加了一行权限代码, 这个方案是不行的, 所以点击"全部拒绝"
  • 重新输入提示"应该是没有配置网络权限, 添加一下相关的权限, 注意不要影响原有的配置信息"
    • 这次正确的添加了权限, 选择接受
      在这里插入图片描述
  • 回到AS重启项目, 结果提示xml有问题, 仔细一看, 它代码加错位置了, 手动调整一下, 向下挪动一行
    在这里插入图片描述
  • 还是无法打开页面
  • 回到TRAE输入提示信息"还是提示"webpage not avalible", 是不是还缺少哪个网络权限", 给出新的修复
    在这里插入图片描述
  • 重启后还是不能打开页面, 重启一下虚拟机
    在这里插入图片描述
    在这里插入图片描述
  • 还是不行, 换了百度网址也不行, 打开自带的谷歌浏览器也访问不了网络, 最后是在下拉的通知栏里重新开关wifi后解决(上一步操作没有成功停止虚拟机)
    在这里插入图片描述
  • 此时发现按键事件的捕获失效了
  • 输入提示信息"按键事件被Webview捕获了导致应用程序的无法捕获上下键了, 修复一下" (这里我之前尝试过几次提问, 最后发现是Webview优先处理了事件导致的)
    在这里插入图片描述
  • 回到AS, 编译报错了, 把提示信息MainActivity.kt:61:17 Modifier 'override' is not applicable to 'local function'发给TRAE
    • 新回复删除了override解决了报错, 但是没有解决按键的问题
  • 重新输入提示"不行, 按键事件还是没有被APP捕获到", 生成了新回复
    在这里插入图片描述
  • 重启项目, 成功解决按键问题

WebView加载本地资源

  • 输入提示"我想打印webview都请求了哪些链接"
    • 新增了一个shouldInterceptRequest方法, 这里的斜杠是多余的, 导致没能正确打印日志, 手动删除一下
      在这里插入图片描述
      在这里插入图片描述
  • 输入提示信息"我想在shouldInterceptRequest中拦截请求, 然后加载本地的资源文件"
    在这里插入图片描述
  • 输入提示"本地资源文件需要放在哪个目录, 帮我生成一个示例"
    • 这里给出了2步, 一个是手动创建本地文件夹和文件, 一个是修改MainActivity.kt(没有自动修改, 可以点击应用按钮)
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
  • 还需要修改一下拦截的地址(下面的日志打印又改回错误的了, 手动修复一下)
    在这里插入图片描述
  • 有2个导入报错, 鼠标移动到红色文字上, 给出了提示信息, 点击import导入一下, 重启项目
    在这里插入图片描述

在按键回调中向WebView注入JS代码

  • 输入提示信息"我想在onKeyDown的回调中修改WebView中的显示内容"
    • 给了一个通过findViewById实现的方法, 接受后发现不能用, 退回一下
      在这里插入图片描述
  • 输入提示信息"我想在onKeyDown的回调中修改Greeting中创建的WebView中的显示内容"
    • 这次他给WebView加了个全局变量webViewInstance, 但是修改后的代码少了花括号, 手动修复一下
  • 发现没效果, 手动加了日志打印了一下日志发现webViewInstance是空的
  • 输入提示信息"webViewInstance是null, 是不是哪里有问题, 导致没赋值成功"
    • 只是加了日志, 没解决
  • 输入提示信息"Greeting 中返回 webView 的时候, webView 还没初始化吧?"
    • 发癫了, 胡乱输出
  • 输入提示信息"AndroidView 中是不是异步执行的, 导致返回的webview是null"
    • 偷懒了, 不想解决问题, 一味的加日志
  • 输入提示信息"已打印日志确认Greeting中返回的webView是null"
    • 还是加日志
      在这里插入图片描述
  • 输入提示信息"通过日志发现WebViewReturn提示webView有值, WebViewRequests处为null, 且WebViewRequests 先WebViewReturn输出, 可以确定AndroidView异步执行导致的返回null, 修复一下"
    • 这次改成了回调的方式, 成功解决
  • 输入提示信息"不要跳转到new-url, 而是执行JS代码"
    在这里插入图片描述
  • 手动修改一下
    在这里插入图片描述
  • 没注意看, 重新输入提示"不要使用webViewInstance?.loadUrl, 使用注入JS的方式"
    在这里插入图片描述
  • 发现没效果, 打开Edge, 访问"edge://inspect/#devices", chrome不行, 会超时然后显示404
    在这里插入图片描述
  • 在控制台直接粘贴JS, 发现js没有问题
  • 输入提示信息"evaluateJavascript 没有效果", 给出新修复
    在这里插入图片描述
  • 重启, 按下上键, 成功解决
    在这里插入图片描述

最终关键代码

  • MainActivity.kt
package com.example.dm2import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.example.dm2.ui.theme.DM2Theme
import android.util.Log
import android.view.KeyEvent
import android.webkit.WebView
import android.webkit.WebViewClient
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView
import java.io.IOExceptionvar webViewInstance: WebView? = nullclass MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {DM2Theme {Surface(modifier = Modifier.fillMaxSize(),color = MaterialTheme.colorScheme.background) {Greeting("Android") { webView ->webViewInstance = webView}}}}}override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {when (keyCode) {KeyEvent.KEYCODE_DPAD_UP -> {Log.d("KeyEvent", "上方向键被按下" + webViewInstance)webViewInstance?.evaluateJavascript("document.body.innerText = new Date();", null)return true}}return super.onKeyDown(keyCode, event)}
}@Composable
fun Greeting(name: String, modifier: Modifier = Modifier, onWebViewCreated: (WebView) -> Unit = {}) {val context = LocalContext.currentAndroidView(factory = {ctx ->val webView = WebView(ctx).apply {settings.javaScriptEnabled = trueLog.d("WebViewInit", "WebView实例已创建: $this")webViewClient = object : WebViewClient() {override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): android.webkit.WebResourceResponse? {val url = request.url.toString()if (url.equals("https://www.example.com/")) {try {val inputStream = context.assets.open("example.html")return WebResourceResponse("text/html", "UTF-8", inputStream)} catch (e: IOException) {e.printStackTrace()}}Log.d("WebViewRequests", "请求链接: ${request.url}")return super.shouldInterceptRequest(view, request)}}loadUrl("https://www.example.com")isFocusable = falseisFocusableInTouchMode = falsefun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {return false}}onWebViewCreated(webView)return@AndroidView webView},modifier = modifier)
}@Preview(showBackground = true)
@Composable
fun GreetingPreview() {DM2Theme {Greeting("Android")}
}
  • AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"><uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /><applicationandroid:allowBackup="true"android:dataExtractionRules="@xml/data_extraction_rules"android:fullBackupContent="@xml/backup_rules"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/Theme.DM2"tools:targetApi="31"><activityandroid:name=".MainActivity"android:exported="true"android:label="@string/app_name"android:theme="@style/Theme.DM2"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application></manifest>
  • example.html
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>示例页面</title>
</head>
<body><h1>这是一个本地资源文件示例</h1><p>该文件从 Android 的 assets 目录加载。</p>
</body>
</html>

吐槽

  • java是大家闺秀, js是小伙子, kotlin简直是耍**, 那语法糖看的我抓耳挠腮

http://www.hkcw.cn/article/vktVkVXZOJ.shtml

相关文章

kibana解析Excel文件,生成mapping es导入Excel

一、Excel转为CSV格式 在线免费网站&#xff1a;EXCEL转CSV - 免费在线将EXCEL文件转换成CSV (cdkm.com) 二、登录kibana 点击左边菜单栏找到Machine Learning&#xff0c; 进入后上面菜单选择Data Visualizer&#xff0c;然后上穿转好的csv格式的Excel 点击导入输入建立的m…

4d毫米波雷达开源算法4DRadarSLAM运行

1.开源代码和论文 &#xff08;1&#xff09;论文名称&#xff1a; 4DRadarSLAM: A 4D Imaging Radar SLAM System for Large-scale Environments based on Pose Graph Optimization &#xff08;2&#xff09;开源代码国内下载地址 https://gitcode.com/gh_mirrors/4d/4DRada…

安卓手机照片在这个目录/storage/emulated/999/DCIM/Camera下的导出解决方案

这里写自定义目录标题 背景解决方案导出过程 导出的成功照片展示 背景 想导出手机的相册照片,发现部分照片无法导出 &#xff0c;原因是在文件管理根本找不到这个目录/storage/emulated/999/DCIM/ 。 照片位置截图&#xff1a; 对应的安卓手机的文件管理截图如下 发现没有…

C++进阶--C++11(04)

文章目录 C进阶--C11&#xff08;04&#xff09;lambdalambda表达式语法捕捉列表lambda的应用lambda的原理 包装器functionbind 总结结语 很高兴和大家见面&#xff0c;给生活加点impetus&#xff01;&#xff01;开启今天的编程之路&#xff01;&#xff01; 今天我们进一步c…

动态设置微信小程序页面标题(navigationBarTitleText属性)

前言&#xff1a; 最近在公司进行小程序研发的时候&#xff0c;产品给出了一个动态加载页面标题的需求&#xff0c;经过调研之后将结果在这里与各位伙伴进行分享。 代码展示&#xff1a; 在.json文件中进行初始配置&#xff1a; { "usingComponents": {}, &q…

PostgreSQL数据库配置SSL操作说明书

背景&#xff1a; 因为postgresql或者mysql目前通过docker安装&#xff0c;只需要输入主机IP、用户名、密码即可访问成功&#xff0c;这样其实是不安全的&#xff0c;可能会通过一些手段获取到用户名密码导致数据被窃取。而ES、kafka等也是通过用户名/密码方式连接&#xff0c;…

基于python 将图像上同一行距离相近的矩形框融合

import os import cv2 import numpy as npdef get_files(path):""" 获取指定路径下所有文件名称 """files []for filename in os.listdir(path):if os.path.isfile(os.path.join(path, filename)):files.append(filename)return filesdef split…

拉取gitlab项目

一、下载nvm管理node 先下载配置好nvm,再用nvm下载node 下载链接&#xff1a;开始 下载nvm - nvm中文官网 情况&#xff1a;npm i 下载依赖缓慢&#xff0c;可能是node版本不对&#xff0c;可能node版本太高 可能得问题&#xff1a;使用nvm 下载低版本的node时&#xff0c;…

Opencv4 c++ 自用笔记 01 Mat类

Mat类 Mat类用来保存矩阵类型的数据&#xff0c;包括向量、矩阵、灰度、通道数。主要分为两部分&#xff0c;矩阵头和矩阵指针。 &#xff08;单通道为灰度图像&#xff0c;3通道或4通道为彩色图像&#xff09; 矩阵头中包含矩阵形状、存储方法、地址、引用次数等。矩阵指针…

Linux `|` 管道符与 `grep` 命令深度解析与高阶应用指南

Linux `|` 管道符与 `grep` 命令深度解析与高阶应用指南 一、核心机制深度解析1. 管道符 `|` 的底层原理2. grep 的匹配引擎二、高阶组合应用技巧1. 多级过滤管道2. 实时监控与告警3. 结构化数据处理三、企业级应用场景1. 安全审计系统2. 性能监控告警3. 日志关联分析四、性能优…

STP配置

由于我们演示的是STP 但是华为交换机默认的都是MSTP所以要换到STP以下是方法 STP mode &#xff1f; 查看模式 STP mode stp 选择stp 换好了后配置交换机优先级 [SWA]stp priority 4096 Apr 15 2013 16:15:33-08:00 SWA DS/4/DATASYNC_CFGCHANGE:OID 1.3.6.1.4.1.2011.5…

CentOS_7.9 2U物理服务器上部署系统简易操作步骤

近期单位网站革新&#xff0c;鉴于安全加固&#xff0c;计划将原有Windows环境更新到Linux-CentOS 7.9&#xff0c;这版本也没的说&#xff08;绝&#xff09;了&#xff08;版&#xff09;官方停止更新&#xff0c;但无论如何还是被sisi的牵挂着这一大批人&#xff0c;毕竟从接…

《仿盒马》app开发技术分享-- 订单详情页(端云一体)

开发准备 在之前的章节中我们实现了订单的提交&#xff0c;以及提交之后跳转到确认订单页面&#xff0c;在确认订单页面我们添加了一个入口&#xff0c;这个入口是查询订单&#xff0c;当我们点击入口时&#xff0c;我们需要跳转到一个新的界面&#xff0c;这个界面通过接收上…

C# 控制台程序获取用户输入数据验证 不合规返回重新提示输入

在 C# 控制台程序中实现输入验证并循环重试&#xff0c;可以通过以下方式实现高效且用户友好的交互。以下是包含多种验证场景的完整解决方案&#xff1a; 一、通用输入验证框架 public static T GetValidInput<T>(string prompt, Func<string, (bool IsValid, T Val…

Linux:shell脚本常用命令

一、设置主机名称 1、查看主机名称 2、用文件的方式更改主机名称 重启后&#xff1a; 3、 通过命令修改主机名 重启后&#xff1a; 二、网络管理命令 1、查看网卡 2、设置网卡 &#xff08;1&#xff09;网卡未被设置过时 &#xff08;2&#xff09;当网卡被设定&#xff0c…

2025年渗透测试面试题总结-匿名[校招]安全研究员(SAST方向)(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 匿名[校招]安全研究员(SAST方向) 一面问题回答框架 1. 自我介绍 2. 简历深挖&#xff08;漏洞挖掘&#x…

基于Java,SpringBoot,Vue,UniAPP医院预约挂号买药就诊病例微信小程序系统设计

摘要 随着医疗信息化的不断推进以及“互联网医疗”模式的广泛普及&#xff0c;传统医院挂号流程中存在的排队时间长、资源分配不均等问题日益凸显&#xff0c;急需通过数字化手段加以解决。本研究设计并实现了一套基于Java、SpringBoot、Vue与UniAPP技术栈的医院预约挂号微信小…

制作一款打飞机游戏62:添加音效

添加音乐 今天&#xff0c;我们要添加音乐。 首先&#xff0c;打开包含音轨的文件夹&#xff0c;然后使用文本编辑器打开cowshrub文件。接着&#xff0c;打开捐赠卡&#xff0c;复制其中的音乐和音效数据&#xff0c;粘贴到cowshrub文件中&#xff0c;替换原有的音效块&#…

WPF log4net用法

WPF log4net用法 一、在工程中管理NuGet程序包&#xff0c;找到log4net&#xff0c;点击安装&#xff0c;如下图已成功安装&#xff1b; 二、在工程中右键添加新建项&#xff0c;选择应用程序配置文件&#xff08;后缀为.config&#xff09;,然后设置名称&#xff0c;这里设置…

视频监控管理平台EasyCVR安防监控小知识:视频监控AI智能分析的常见部署方式有哪些?

一、方案背景​ 随着视频监控技术迭代&#xff0c;AI智能分析成为提升系统价值的核心。通过实时处理视频流辅助决策&#xff0c;广泛应用于智慧社区、园区等场景。由于AI算法部署方式多样且各有适用场景&#xff0c;因此合理选择部署方式是项目成功的关键。 二、主流AI算法部署…