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,一经查实,立即删除!

相关文章

实用 Tips 分享|用 File Browser 上传大文件

在使用 OpenBayes 平台过程中&#xff0c;很多用户都会遇到上传大文件 (&#xff1e;100 G) 的情况&#xff0c;无论是上传至数据仓库&#xff0c;还是在工作空间中完成上传&#xff0c;过大的文件在传输过程中常会出现上传中断、速度过慢等情况。 针对这一问题&#xff0c;我…

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

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

python中[[“jfk“,“kul“],[“jfk“,“nrt“]]是如何进行sort()排序的

在Python中&#xff0c;列表&#xff08;list&#xff09;的sort()方法默认按照元素的字典顺序进行排序。对于包含字符串的列表&#xff0c;它会按照字符串的字典顺序&#xff08;即字母顺序&#xff09;进行排序。 然而&#xff0c;对于嵌套列表&#xff08;即列表中的元素也…

【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…

linux kernel物理内存概述(七)

目录 一、内核中小内存、频繁分配和释放场景 二、slab是内存池化技术 三、内核中使用slab对象池的地方 四、slab内核设计 使用比页小的内存&#xff0c;内核的处理方式使用slab 一、内核中小内存、频繁分配和释放场景 slab首先会向伙伴系统一次性申请一个或者多个物理内存…

医学大数据|文献阅读|有关“肠癌+机器学习”的研究记录

目录 1.机器学习算法识别结直肠癌中的免疫相关lncRNA signature 2.基于机器学习的糖酵解相关分子分类揭示了结直肠癌癌症患者预后、TME和免疫疗法的差异&#xff0c;2区7 3.整合深度学习-病理组学、放射组学和免疫评分预测结直肠癌肺转移患者术后结局 4.最新7.4分纯生信&am…

二叉树的前、中、后序遍历

leetcode144. 二叉树的前序遍历 leetcode94. 二叉树的中序遍历 leetcode145. 二叉树的后序遍历 思路 这里前中后序遍历&#xff0c;其实指的就是中间节点的遍历顺序 前序遍历&#xff1a;中左右 中序遍历&#xff1a;左中右 后序遍历&#xff1a;左右中 self.result.append(r…

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

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

如何简单使用Java程序的异常捕获

我们在开发中难免会出现程序错误的问题&#xff0c;但是如果出错了的话那么整个程序就会报错&#xff0c;这样在开发中是不允许的&#xff0c;能传递报错消息但是系统不能崩盘&#xff0c;这就需要使用异常区捕获了 如何自己创建一个异常呢&#xff0c;如下 Component public…

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;提供调整相关性算法的能…

Android调用浏览器打开指定页面

一、启动Android默认浏览器 在Android程序中我们可以通过发送隐式Intent来启动系统默认的浏览器。如果手机本身安装了多个浏览器而又没有设置默认浏览器的话&#xff0c;系统将让用户选择使用哪个浏览器来打开连接。 Uri uri Uri.parse("https://www.baidu.com"); …

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

目录 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;本是同畜…

js的运算符

?? 是根据 null 和 undefined 判断的&#xff0c; || 是根据 布尔值 false 判断的, 左侧为true 不看右侧 && 左侧为false 不看右侧 ?. ”可选链操作符“&#xff0c;允许读取位于连接对象链深处的属性的值&#xff0c;而不必明确验证链中的每个引用是否有效。在引…

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

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

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

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

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

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

密码学——流密码和分组密码

引言: 首先回顾一下一次一密密码的概念:密钥随机产生,而且只使用一次,是一种理想的理论安全的加密方案。这种加密方式无条件安全,且加密和解密均为加法运算,效率较高。但是由于密钥长度与明文长度一样长,密钥共享困难,实用性受限。   那么在实际应用中更具实用性的加…