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

news/2024/7/21 8:30:08 标签: 玩安卓, harmonyos, OpenHarmony

前言

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 = -1

   build() {
      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 - 1
            if (this.counter == 0) {
               clearInterval(this.intervalID)
               this.goIntent()
            }
         }, 1000);
      })
      .onDisAppear(() => {
         LogUtils.info('222222222  隐藏')
         this.counter = 5
         clearInterval(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 = '首页'

   @Builder
   TabBuilder(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: boolean

   build() {
      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.niftyadmin.cn/n/5407399.html

相关文章

深度学习_19_卷积

理论&#xff1a; 目前问题在于识别图片所需要的模型权重数量会比较大 一般图片像素在12M也就是一千两百万像素&#xff0c;要用模型对其整体识别的话&#xff0c;需要至少一千两百万权重&#xff0c;那也仅仅是线性模型&#xff0c;若用多层感知机的话&#xff0c;模型的数据…

3D桌面端可视化引擎HOOPS Visualize助力工业制造开发AR/VR功能,实现质量控制与检验可视化!

近年来&#xff0c;工业制造领域正迎来一场数字化和智能化的革命。而增强现实&#xff08;AR&#xff09;和虚拟现实&#xff08;VR&#xff09;技术也在数字革命的浪潮中的有着重要推动力。这两种技术通过融合数字信息与真实或虚拟环境&#xff0c;为用户提供前所未有的交互体…

一手实测【Claude3】 - GPT4啊,你的时代终于要过去了

通过虚拟卡 WildCard 的方式来升级Claude3最快了&#xff0c;大概2分钟就可以开通完成, 而且升级 GPT 4.0 价钱也不贵&#xff0c;虚拟卡一年10美元&#xff0c;Claude3 每个月也才 20美元。如果你觉得Claude3对你可能有帮助&#xff0c;那就赶快来升级吧&#xff01; Claude3…

rclone源码解析

rclone会将数据分成3类&#xff1a;srcOnly&#xff0c;dstOnly&#xff0c;match 分类的方法是&#xff0c;先读取当前目录下的源数据&#xff0c;然后以同样的名字去对端查看&#xff0c;如果有的话就放到match&#xff0c;如果只有当前目录有的话就放入srcOnly 然后srcOnl…

Baumer工业相机堡盟工业相机如何联合GAPI SDK和OpenCV实现相机图像将图像转换为Mat格式再转为Bitmap图像进行显示(C++)

Baumer工业相机堡盟工业相机如何联合GAPI SDK和OpenCV实现相机图像将图像转换为Mat图像格式再转为Bitmap图像进行显示&#xff08;C&#xff09; Baumer工业相机Baumer工业相机的图像转换为OpenCV的Mat图像的技术背景代码分析第一步&#xff1a;先引用对应的OpenCV的DLL文件第二…

在 Spring Boot 中整合、使用 WebSocket

在 Spring Boot 中整合、使用 WebSocket 文章目录 在 Spring Boot 中整合、使用 WebSocket在 Spring Boot 中整合 WebSocket添加依赖开发 ServerEndpoint 端点OnMessageOnOpenOnCloseOnError 配置 ServerEndpointExporter 测试在端点中注入 Bean WebSocket 是一种基于 TCP 协议…

VueUse 安装使用

VueUse 是一个集成了常用的 Vue Composition API 的函数库&#xff0c;它提供了一系列的 hooks 和工具函数&#xff0c;帮助我们更方便地使用 Vue Composition API。在本文中&#xff0c;我将为你介绍如何安装和使用 VueUse。 安装 首先&#xff0c;你需要打开一个终端窗口&a…

eclipse搭建java web项目

准备条件 eclipsejdk1.8 &#xff08;配置jdk环境&#xff09;apache-tomcat-8.5.97&#xff08;记住安装位置&#xff09; 一 点击完成 开始创建javaweb项目 import java.io.IOException; import java.io.PrintWriter;import javax.servlet.ServletException; import javax.s…