WanAndroid(鸿蒙版)开发的第一篇

前言

DevEco Studio版本:4.0.0.600

WanAndroid的API链接:玩Android 开放API-玩Android - wanandroid.com

为了兼容HarmonyOS,我这边以Arkts--API9为例进行实现

通过华为官网渠道目前下载的版本还是3.1的,这边提供下4.0版本下载的渠道:

 DevEco Studio 4.0版本下载地址:DevEcoStudio 4.0版本下载

4.0版本的配套关系

注:一些基础知识点和默认配置项不做详细说明了,本文基于大家有一定的鸿蒙基础能力。有不明白的可以留言咨询

实现效果:

玩安卓演示视频

项目创建

为了兼容HarmonyOS(目前手机API版本只到9),compile SDK : 9

模块构建

根据项目功能模块划分了:HomeModuleProjectModuleNavigationModuleMineModule四个功能模块,加上基础模块BaseLibrary和主模块entry组成项目的基础架构。

File-->new-->Module新建模块

其他几个模块依照上图为例依次创建,创建后如下图所示:

BaseLibrary资源链接:https://download.csdn.net/download/Abner_Crazy/88908326

项目页面实现

1、启动页面(SplashPage)

新建SplashPage页面:new-->Page创建页面,或者new-->ArkTS File创建文件(需要手动在main_pages.json中添加页面引用)

SplashPage详细代码:

import router from '@ohos.router'
import LogUtils from '@app/BaseLibrary/src/main/ets/utils/LogUtils'@Entry
@Component
struct SplashPage {@State counter: number = 5@State message: string = 'Wan Android'private intervalID: number = -1build() {RelativeContainer() {Text(this.message).fontSize(52).fontWeight(FontWeight.Bold).id('textContent').alignRules({center: { anchor: '__container__', align: VerticalAlign.Center }, middle: { anchor: '__container__', align: HorizontalAlign.Center }})//倒计时Text(`${this.counter}s后跳转`).fontSize(15).fontColor(Color.Gray).id('textJump').alignRules({right: { anchor: '__container__', align: HorizontalAlign.End },top: { anchor: '__container__', align: VerticalAlign.Top }}).padding({ left: 10, right: 10, top: 5, bottom: 5 }).margin({ top: 20, right: 20 }).borderWidth(1).borderRadius(5).borderColor(Color.Gray).onClick(() => {this.goIntent()})}.width('100%').height('100%').onAppear(() => {LogUtils.info('222222222  显示')this.intervalID = setInterval(() => {this.counter = this.counter - 1if (this.counter == 0) {clearInterval(this.intervalID)this.goIntent()}}, 1000);}).onDisAppear(() => {LogUtils.info('222222222  隐藏')this.counter = 5clearInterval(this.intervalID)})}private goIntent(): void {let userLoginStatus = false //TODO:获取登录状态,后面实现if (userLoginStatus) {router.replaceUrl({ url: 'pages/MainPage' })} else {router.replaceUrl({ url: 'pages/LoginPage' })}}
}

2、登录页面(LoginPage)

LoginPage页面详细代码:

import { Constants, HttpManager, LoadingDialog, RequestMethod, UserManager } from "@app/BaseLibrary"
import LogUtils from '@app/BaseLibrary/src/main/ets/utils/LogUtils'
import { RegisterBean } from '../bean/RegisterBean'
import promptAction from '@ohos.promptAction'
import { LoginBean } from '../bean/LoginBean'
import router from '@ohos.router'const TAG = 'LoginPage--- ';@Entry
@Component
struct LoginPage {@State username: string = "" //用户名@State password: string = "" //密码hideJump: boolean = router.getParams()?.['hideJump']build() {RelativeContainer() {Image($r("app.media.ic_back")).width(36).height(32).id('imageBack').margin(20).alignRules({left: { anchor: '__container__', align: HorizontalAlign.Start },top: { anchor: '__container__', align: VerticalAlign.Top }}).onClick(() => {router.back()})if (!this.hideJump) {Text('跳过').fontSize(15).fontColor(Color.Gray).id('textJump').alignRules({right: { anchor: '__container__', align: HorizontalAlign.End },top: { anchor: '__container__', align: VerticalAlign.Top }}).padding({ left: 10, right: 10, top: 5, bottom: 5 }).margin({ top: 20, right: 20 }).borderWidth(1).borderRadius(5).borderColor(Color.Gray).onClick(() => {router.clear()router.replaceUrl({ url: 'pages/MainPage' })})}Text('登录').fontSize(50).fontColor(Color.Black).fontWeight(FontWeight.Bold).id('textLogin').margin({ top: 30 }).alignRules({middle: { anchor: '__container__', align: HorizontalAlign.Center },top: { anchor: 'imageBack', align: VerticalAlign.Bottom }})//用户名和密码Column() {Row() {Image($r('app.media.username')).width(32).height(32)TextInput({ placeholder: '请输入用户名' }).width(300).margin({ left: 16 }).onChange((value: string) => {this.username = value})}Row() {Image($r('app.media.password')).width(32).height(32)TextInput({ placeholder: '请输入密码' }).type(InputType.Password).width(300).margin({ left: 16 }).onChange((value: string) => {this.password = value})}.margin({ top: 20 })}.id('columnLogin').margin({ top: 80 }).alignRules({middle: { anchor: '__container__', align: HorizontalAlign.Center },top: { anchor: 'textLogin', align: VerticalAlign.Bottom }})Button("注册").width(340).height(60).onClick(() => {this.registerData()}).id('buttonLogin').margin({ bottom: 80 }).alignRules({middle: { anchor: '__container__', align: HorizontalAlign.Center },bottom: { anchor: '__container__', align: VerticalAlign.Bottom }})Button("登录").width(340).height(60).onClick(() => {this.dialogController.open()this.loginData()}).id('buttonRegister').backgroundColor(Color.Pink).margin({ bottom: 40 }).alignRules({middle: { anchor: '__container__', align: HorizontalAlign.Center },bottom: { anchor: 'buttonLogin', align: VerticalAlign.Top }})}.width('100%').height('100%')}/*** 登录请求*/private loginData(): void {if (this.username.trim().length == 0 || this.password.trim().length == 0) {promptAction.showToast({ message: "用户名或密码不能为空" })return}HttpManager.getInstance().request<LoginBean>({method: RequestMethod.POST,header: { "Content-Type": "application/json" },url: `https://www.wanandroid.com/user/login?username=${this.username}&password=${this.password}`, //wanAndroid的API:登录}).then((result: LoginBean) => {this.dialogController.close()LogUtils.info(TAG, "content  encodeURIComponent: " + encodeURIComponent(this.username) + "   result:" + JSON.stringify(result))if (result.errorCode == 0) {AppStorage.SetOrCreate(Constants.APPSTORAGE_ISLOGIN, true)AppStorage.SetOrCreate(Constants.APPSTORAGE_USERNAME, encodeURIComponent(this.username))AppStorage.SetOrCreate(Constants.APPSTORAGE_PASSWORD, this.password)AppStorage.SetOrCreate(Constants.APPSTORAGE_TOKEN_PASS, this.cookiesMatch(JSON.stringify(result.header)))promptAction.showToast({ message: "登录成功" })UserManager.setLogIn(true)router.clear()router.replaceUrl({ url: 'pages/MainPage' })} else {promptAction.showToast({ message: result.errorMsg })}}).catch((error) => {this.dialogController.close()promptAction.showToast({ message: "登录失败" })LogUtils.info(TAG, "error: " + JSON.stringify(error))})}/*** 注册请求*/private registerData(): void {if (this.username.trim().length == 0 || this.password.trim().length == 0) {promptAction.showToast({ message: "用户名或密码不能为空" })return}HttpManager.getInstance().request<RegisterBean>({method: RequestMethod.POST,header: { "Content-Type": "application/json" },url: `https://www.wanandroid.com/user/register?username=${this.username}&password=${this.password}&repassword=${this.password}`, //wanAndroid的API:注册}).then((result: RegisterBean) => {LogUtils.info(TAG, "result: " + JSON.stringify(result))if (result.errorCode == 0) {promptAction.showToast({ message: "注册成功" })} else {promptAction.showToast({ message: result.errorMsg })}}).catch((error) => {promptAction.showToast({ message: "注册失败" })LogUtils.info(TAG, "error: " + JSON.stringify(error))})}/*** cookies正则匹配,获取token_pass* @returns*/private cookiesMatch(header: string): string {LogUtils.info(TAG, " cookiesMatch   header: " + header)let content = header.match(/token_pass_wanandroid_com=(\S*);/)[1]LogUtils.info(TAG, "  cookiesMatch  content: " + content)return content}private dialogController = new CustomDialogController({builder: LoadingDialog({ content: '登录中...' }),customStyle: true,alignment: DialogAlignment.Center, // 可设置dialog的对齐方式,设定显示在底部或中间等,默认为底部显示})
}

登录界面用到的HttpManager网络请求参考之前的文章:鸿蒙自定义Http网络访问组件-CSDN博客

登录成功后通过AppStorage保存登录状态:APPSTORAGE_ISLOGIN,用户名:APPSTORAGE_USERNAME(注:需要执行encodeURIComponent(this.username)进行编码,解决部分特殊用户名乱码问题),密码:APPSTORAGE_PASSWORD,用户登录后的token:APPSTORAGE_TOKEN_PASS(执行 cookiesMatch方法进行正则匹配)

AppStorage.SetOrCreate(Constants.APPSTORAGE_ISLOGIN, true)
AppStorage.SetOrCreate(Constants.APPSTORAGE_USERNAME, encodeURIComponent(this.username))
AppStorage.SetOrCreate(Constants.APPSTORAGE_PASSWORD, this.password)
AppStorage.SetOrCreate(Constants.APPSTORAGE_TOKEN_PASS, this.cookiesMatch(JSON.stringify(result.header)))

3、主页面(MainPage)

MainPage页面详细代码:

import { Constants } from "@app/BaseLibrary"
import LogUtils from "@app/BaseLibrary/src/main/ets/utils/LogUtils"
import { MainTitleBar } from '../widget/MainTitleBar';@Entry
@Component
struct MainPage {private tabsController: TabsController = new TabsController();@State currentTabIndex: number = Constants.HOME_TAB_INDEX;@State title: string = '首页'@BuilderTabBuilder(title: string, index: number, normalIcon: Resource, selectIcon: Resource) {Column() {Image(this.currentTabIndex == index ? selectIcon : normalIcon).width(25).height(25)Text(title).margin({ top: 4 }).fontSize("10fp").fontColor(this.currentTabIndex == index ? "#1296db" : "#999999")}.justifyContent(FlexAlign.Center).height(56).width('100%').onClick(() => {this.currentTabIndex = index;this.tabsController.changeIndex(this.currentTabIndex)})}build() {Column() {MainTitleBar({ title: this.title, showSearch: this.currentTabIndex == 0 })Tabs({barPosition: BarPosition.End,controller: this.tabsController,}) {// 首页TabContent() {// HomePage()}.padding({ left: 12, right: 12 }).tabBar(this.TabBuilder(Constants.HOME_TITLE, Constants.HOME_TAB_INDEX, $r('app.media.ic_bottom_normal_home'), $r('app.media.ic_bottom_select_home')))// 项目TabContent() {// ProjectPage()}.tabBar(this.TabBuilder(Constants.PROJECT_TITLE, Constants.PROJECT_TAB_INDEX, $r('app.media.ic_bottom_normal_project'), $r('app.media.ic_bottom_select_project')))// 导航TabContent() {// NavigationPage()}.padding({ left: 12, right: 12 }).tabBar(this.TabBuilder(Constants.NAVIGATION_TITLE, Constants.NAVIGATION_TAB_INDEX, $r('app.media.ic_bottom_normal_navigation'), $r('app.media.ic_bottom_select_navigation')))//我的TabContent() {// MinePage()}.tabBar(this.TabBuilder(Constants.MINE_TITLE, Constants.MINE_TAB_INDEX, $r('app.media.ic_bottom_normal_mine'), $r('app.media.ic_bottom_select_mine')))}.width('100%').height('100%').flexShrink(1).scrollable(false).animationDuration(100).barHeight(56).backgroundColor($r('app.color.bar_backgroundColor')).barMode(BarMode.Fixed).onChange((index: number) => {this.title = this.getTitle(index)this.currentTabIndex = index;})}.width('100%').height('100%').backgroundColor(Color.White)}private getTitle(index: number): string {let titleList = [Constants.HOME_TITLE, Constants.PROJECT_TITLE, Constants.NAVIGATION_TITLE, Constants.MINE_TITLE]LogUtils.info("3333333333333333  titleList: " + titleList[index])return titleList[index]}
}
MainTitleBar详细代码:
import router from '@ohos.router'@Preview
@Component
export struct MainTitleBar {@Prop title: string@Prop showSearch: booleanbuild() {RelativeContainer() {Text(this.title).fontSize(24).fontColor(Color.White).fontWeight(FontWeight.Bold).id("textTitle").alignRules({center: { anchor: '__container__', align: VerticalAlign.Center },middle: { anchor: '__container__', align: HorizontalAlign.Center }})Image($r('app.media.ic_search')).width(25).height(25).id("imageSearch").margin({ right: 16 }).alignRules({center: { anchor: '__container__', align: VerticalAlign.Center },right: { anchor: '__container__', align: HorizontalAlign.End }}).onClick(() => {router.pushUrl({url:'pages/search/SearchPage'})}).visibility(this.showSearch ? Visibility.Visible : Visibility.None)}.backgroundColor('#1296db').width('100%').height(56)}
}

4、界面效果

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/730909.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从2个角度来简单讨论一下伦敦金走势图怎么看

进入伦敦金市场之后&#xff0c;投资者无时无刻都在思考着一个问题&#xff0c;那就是伦敦金走势怎么看&#xff1f;关于这个问题&#xff0c;其实在市场中有很多的文章和视频去介绍&#xff0c;在书店里也有很多投资前贤所写的书籍讨论过这个问题。但是他们都有一个特征&#…

【SpringMVC】快速体验 SpringMVC接收数据 第一期

文章目录 一、SpringMVC 介绍1.1 主要作用1.2 核心组件和调用流程理解 二、快速体验三、SpringMVC接收数据3.1 访问路径设置3.1.1 精准路径匹配3.1.2 模糊路径匹配3.1.3 类和方法级别区别3.1.4 附带请求方式限制3.1.5 进阶注解 与 常见配置问题 3.2 接收参数&#xff08;重点&a…

复杂业务场景下,如何优雅的使用设计模式来优化代码?

1、引言 本文以一个实际案例来介绍在解决业务需求的路上&#xff0c;如何通过常用的设计模式来逐级优化我们的代码&#xff0c;以把我们所了解的到设计模式真实的应用于实战。 2、背景 假定我们现在有一个订单流程管理系统&#xff0c;这个系统对于用户发起的一笔订单&#…

HNU-算法设计与分析-甘晴void学习感悟

前言 算法设计与分析&#xff0c;仅就课程而言&#xff0c;似乎是数据结构与算法分析的延续 教材使用&#xff1a; 课程 关于课程&#xff0c;橙学长讲的非常清晰&#xff0c;我深以为然。 HNUCS-大三课程概览-CSDN博客文章浏览阅读1.3k次&#xff0c;点赞5次&#xff0c;收…

安装nginx:手动安装和yum安装

本文在centos7.9下分别尝试了yum安装和手动安装&#xff0c;记录一下试验过程。为后来者少踩点坑。 下载 下载地址&#xff1a;链接 。建议下载稳定版本&#xff0c;也就是Stable Version&#xff0c;这里下载的是 nginx-1.24.0 # 我下载在如下文件夹 mkdir/opt/apps cd /op…

ES入门五:组合查询

带有组合功能的Api有以下几个&#xff1a; Bool Query&#xff1a;布尔查询&#xff0c;可以组合多个过滤语句来过滤文档Boosting Query&#xff1a;在postive块中指定匹配文档的语句&#xff0c;同时降低在negative块中也匹配的文档的得分&#xff0c;提供调整相关性算法的能…

代码随想录 回溯算法-排序

目录 46.全排序 47.全排列|| 332.重新安排行程 46.全排序 46. 全排列 中等 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[1,2,3],[1,…

蓝桥杯每日一题:烤鸡dfs

这道题考察了dfs的应用&#xff0c;题干十分有趣&#xff0c;思考过程对以后类似题目也有很强的参考性&#xff0c;一起来学习吧&#xff01; 题目&#xff1a; # 烤鸡 ## 题目背景 猪猪 Hanke 得到了一只鸡。 ## 题目描述 猪猪 Hanke 特别喜欢吃烤鸡&#xff08;本是同畜…

蓝桥杯练习题——归并排序

1.火柴排队 思路 1.求最小值的时候&#xff0c;可以直接按升序排序&#xff0c;这样得到的值就是最小值 2.求最小交换次数的时候&#xff0c;不能直接排序&#xff0c;因为只能交换相邻的数&#xff0c;只需要知道他们的相对大小&#xff0c;所以可以先用离散化&#xff0c;把…

清华大学1748页CTF竞赛入门指南,完整版开放下载!

CTF是一种针对信息安全领域的经济性挑战&#xff0c;旨在通过解决一系列的难题来寻找隐藏的“flag”。CTF比赛战队一般是以高校、科研单位、企业、信息安全从业者或社会团体组成。对于网安爱好者及从业者来说&#xff0c;拥有“CTF参赛经验”也是求职中的加分项。 前几天分享的…

什么是智慧公厕?智慧公厕设备有哪些

在现代社会&#xff0c;公共厕所作为城市基础设施的重要一环&#xff0c;承载着城市卫生、居民生活品质的重要责任。然而&#xff0c;传统公厕存在的问题仍然不可忽视&#xff1a;脏乱差、资源浪费、安全隐患等等。 为了解决这些问题&#xff0c;针对公共厕所日常使用、运营管…

六、长短时记忆网络语言模型(LSTM)

为了解决深度神经网络中的梯度消失问题&#xff0c;提出了一种特殊的RNN模型——长短期记忆网络&#xff08;Long Short-Term Memory networks, LSTM&#xff09;&#xff0c;能够有效的传递和表达长时间序列中的信息并且不会导致长时间前的有用信息被忽略。 长短时记忆网络原理…

ORA/GSA -- 学习记录

brief over-representation analysis(ORA),过表“达”分析&#xff0c;就是我们做多分组的RNAseq数据解析后会得到一些差异表达的gene&#xff0c;有些时候是单独拿出一个差异gene去解释表型&#xff0c;缺点是欠缺证据力度。有些人就把一些相关的差异gene放在一块儿解释&…

网络编程---网络编程入门、UDP通信程序、TCP通信程序

1.网络编程入门 1.网络编程概述 网络编程&#xff1a; 在网络通信协议下&#xff0c;实现网络互连的不同计算机上运行的程序间可以进行数据传输 计算机网络&#xff1a; 是指将地理位置不同的具有独立功能的多台计算机及其外部设备&#xff0c;通过通信线路连接起来&#…

J1周-ResNet-50算法

本文为&#x1f517;365天深度学习训练营 中的学习记录博客 原作者&#xff1a;K同学啊|接辅导、项目定制 我的环境&#xff1a; 1.语言&#xff1a;python3.7 2.编译器&#xff1a;pycharm 3.深度学习框架Tensorflow/Pytorch 1.8.0cu111 一、问题引出 CNN能够提取低、中、…

qnx启动中控屏黑屏

bmetrics_service boot metrics service, 用于记录统计启动性能信息,读取/dev/bmetrics可以获取到这些信息 # use memorydump memorydump Sets the debug cookies, copies MMU info into reset_info asinfo, sets the secure monitor(TZ) dump buffer, starts tracelogger Usa…

VR全景技术在VR看房中有哪些应用,能带来哪些好处

引言&#xff1a; 随着科技的不断发展&#xff0c;虚拟现实&#xff08;VR&#xff09;技术在房地产行业中的应用也越来越广泛。其中&#xff0c;VR全景技术在VR看房中的运用尤为突出。今天&#xff0c;让我们一起深入探讨VR全景技术在VR看房中的应用及其带来的种种好处。 一、…

ES入门二:文档的基本操作

索引管理 创建索引 删除索引 文档管理 创建文档 如果有更新需求&#xff0c;使用第一种如果有唯一性校验&#xff0c;使用第二种如果需要系统给你创建文档Id&#xff0c;使用第三种 &#xff08;这个性能更好&#xff09; 相比第一种&#xff0c;第三种的写入效率更高&#xf…

基础GamePlay知识-碰撞检测

将会持续更新gameplay的一些基础知识&#xff0c;一同学习。 扇形检测 扇形检测是Gameplay里面很常见的场景。比如荒野乱斗中&#xff0c;大部分的近战角色都是扇形攻击。在扇形范围内就认为是受击。 扇形检测只有两个参数&#xff0c;一个是扇形的角度一个是扇形的半径大小。…

直播预告|小白开箱: 云数据库在五朵云上的评测

3 月 7 日&#xff0c;周四晚上 19:00-20:30 由明说三人行组织&#xff0c;邀请了 NineData 国际总经理(GM) Ni Demai、云猿生数据 CTO &#xff06; 联合创始人子嘉&#xff0c;和《明说三人行》创始人 &主持人明叔&#xff0c;共同围绕《小白开箱: 云数据库在五朵云上的评…