鴻蒙學習記錄

簡介

在自學鴻蒙開發(fā)的過程中柳琢,記錄下自己遇到的問題以及解決辦法,持續(xù)更新W咴健?贩耐!

環(huán)境信息:

  • 系統(tǒng):Mac 13.5 (22G74)
  • IDE信息:
    • DevEco Studio 3.1.1 Release
    • Build Version: 3.1.0.501, built on June 20, 2023

一、學習文檔

1厦取、HarmonyOS 第一課

2潮太、鴻蒙開發(fā)者文檔指南

3、鴻蒙開發(fā)者文檔API參考

4虾攻、華為開發(fā)者論壇

5铡买、鴻蒙開發(fā)基礎認證和高級認證

6、鴻蒙應用發(fā)布


二霎箍、遇到的問題

1奇钞、DevEco Studio自動生成.js唇聘、.js.map文件問題

通過插件(ArkCompilerSupport)搞定

插件地址點我

參考博客

2涯雅、DevEco Studio無法自動監(jiān)測到本地模擬器?

每次點擊DeviceManager啟動模擬器后粱哼,DevEco Studio都不會自動檢測到模擬器問題顶别,可以參考以下代碼片段進行開發(fā)環(huán)境配置谷徙。

參考華為開發(fā)者社區(qū)論壇博客

# 鴻蒙腳本工具集
export PATH=/Users/XXX/HMOSProject/EnvInfo/sdk/openharmony/9/toolchains:$PATH
export PATH=/Users/XXX/HMOSProject/EnvInfo/ohpm/bin:$PATH
# HDC是為開發(fā)者提供HarmonyOS應用/服務的調(diào)試工具,為方便使用HDC工具驯绎,請為HDC端口號設置環(huán)境變量完慧。
export HDC_SERVER_PORT=7035
launchctl setenv HDC_SERVER_PORT $HDC_SERVER_PORT
export HDC_SERVER_PORT

3、網(wǎng)絡請求如何實現(xiàn)剩失?

  • 方法1:第三方組件:axios
  • 方法2:系統(tǒng)API:http屈尼,封裝示例如下:
import http from '@ohos.net.http';


import http from '@ohos.net.http';

// 標準網(wǎng)絡請求返回數(shù)據(jù)結(jié)構(gòu)
export interface  SJRequestConfig extends http.HttpRequestOptions{
  url?:string; // url參數(shù)
}

// 全局配置對象
export interface SJGlobalConfig {
  // baseUrl
  baseUrl?: string;
  // 是否使用緩存:默認為true
  usingCache?: boolean;
  // 全局header册着,默認值為 'content-type': 'application/json'
  header?: Object;
  // 讀取超時時間:默認60000,即60s
  readTimeout?: number; // default is 60s
  // 連接超時時間:默認60000脾歧,即60s
  connectTimeout?: number; // default is 60s.
  // HTTP協(xié)議版本甲捏,默認由系統(tǒng)決定
  usingProtocol?: http.HttpProtocol;
}

// 標準網(wǎng)絡請求返回數(shù)據(jù)結(jié)構(gòu)
export class SJResponse<T> {
  status?: number;
  message?: string;
  data?: T;
}

// 網(wǎng)絡庫返回的錯誤對象
export class SJError<R> {
  code:Number; // HTTP錯誤碼
  message?:string; // HTTP錯誤信息
  businessCode?:Number; // 業(yè)務code
  businessMessage?:string; // 業(yè)務message
  data?:R; //原始數(shù)據(jù)
}

export class SJHttp {
  request<T,R= string | Object | ArrayBuffer>(config:SJRequestConfig): Promise<T> {
    // 每一個httpRequest對應一個HTTP請求任務,不可復用
    let httpRequest = http.createHttp();
    // 請求參數(shù)配置
    let httpResponse = httpRequest.request(config.url,config)

    return new Promise((resolve,reject)=>{
      httpResponse.then((data: http.HttpResponse) => {
        let httpCode = data.responseCode;
        if (httpCode == 200) {
          // data.result為HTTP響應內(nèi)容涨椒,可根據(jù)業(yè)務需要進行解析
          let resultStr = `${data.result}`
          let response:SJResponse<T> = JSON.parse(resultStr)
          if (response.status == 200) {
            resolve(response.data);
          } else {
            var sjErr:SJError<R> = new SJError();
            sjErr.code = httpCode;
            sjErr.message = "";
            sjErr.businessCode = response.status;
            sjErr.businessMessage = response.message;
            sjErr.data = data.result as R;
            reject(sjErr)
          }
        }
        var sjErr:SJError<R> = new SJError();
        sjErr.code = httpCode;
        sjErr.message = `${data.result}`;
        sjErr.businessCode = null;
        sjErr.businessMessage = null;
        sjErr.data = data.result as R;
        reject(sjErr)
      }).catch((err) => {
        console.info('error:' + JSON.stringify(err));
        // 取消訂閱HTTP響應頭事件
        httpRequest.off('headersReceive');
        // 當該請求使用完畢時摊鸡,調(diào)用destroy方法主動銷毀。
        httpRequest.destroy();

        var sjErr:SJError<R> = new SJError();
        sjErr.code = err.code;
        sjErr.message = err.message;
        sjErr.businessCode = null;
        sjErr.businessMessage = null;
        sjErr.data = null;
        reject(sjErr)
      })
    })
  }
}

export class SJRequest {
  // 單例對象
  static instance: SJRequest = new SJRequest();
  // 全局配置對象
  public globalConfig:SJGlobalConfig;

  constructor() {
    this.globalConfig = {
      usingCache:true,
      readTimeout:60000,
      connectTimeout:60000,
      usingProtocol:http.HttpProtocol.HTTP1_1,
    }
  }

  get<T>(config:SJRequestConfig): Promise<T> {
    config.method = http.RequestMethod.GET;
    return this.request(config);
  }

  post<T>(config:SJRequestConfig): Promise<T> {
    config.method = http.RequestMethod.POST;
    return this.request(config);
  }

  delete<T>(config:SJRequestConfig): Promise<T> {
    config.method = http.RequestMethod.DELETE;
    return this.request(config);
  }

  put<T>(config:SJRequestConfig): Promise<T> {
    config.method = http.RequestMethod.PUT;
    return this.request(config);
  }

  request<T>(config:SJRequestConfig): Promise<T> {
    this.handleGlobalConfig(config);
    let sjReq = new SJHttp();
    return sjReq.request<T>(config)
  }

  private isEmpty(obj:any):boolean{
    if (obj == undefined || obj == null) {
      return true;
    }
    return false;
  }

  private handleGlobalConfig(config:SJRequestConfig):SJRequestConfig{
    let globalConfig:SJGlobalConfig = this.globalConfig;
    // 1.處理url
    let url = config.url;
    let urlBase = globalConfig.baseUrl;

    let urlLower = url.toLowerCase();
    let isHttpUrl = urlLower.startsWith('http') || urlLower.startsWith('https');
    if (!isHttpUrl) {
      config.url = `${urlBase}${url}`
    }

    // 2蚕冬、處理其他參數(shù)
    if (this.isEmpty(config.usingCache)) {
      config.usingCache = globalConfig.usingCache;
    }
    if (this.isEmpty(config.header)) {
      config.header = globalConfig.header;
    }
    if (this.isEmpty(config.readTimeout)) {
      config.readTimeout = globalConfig.readTimeout;
    }
    if (this.isEmpty(config.connectTimeout)) {
      config.connectTimeout = globalConfig.connectTimeout;
    }
    if (this.isEmpty(config.usingProtocol)) {
      config.usingProtocol = globalConfig.usingProtocol;
    }
    return config;
  }
}

export class SJRequestManager {
  // 全局配置
  static configGlobal(callback:(config:SJGlobalConfig)=>void){
    callback(SJRequest.instance.globalConfig);
  }

  static get<T>(config:SJRequestConfig): Promise<T> {
    return SJRequest.instance.get(config);
  }

  static post<T>(config:SJRequestConfig): Promise<T> {
    return SJRequest.instance.post(config);
  }

  static delete<T>(config:SJRequestConfig): Promise<T> {
    return SJRequest.instance.delete(config);
  }

  static put<T>(config:SJRequestConfig): Promise<T> {
    return SJRequest.instance.put(config);
  }

  static request<T>(config:SJRequestConfig): Promise<T> {
    return SJRequest.instance.request(config);
  }
}

4免猾、List上拉加載,下拉刷新如何實現(xiàn)囤热?

參考第三方組件: PullToRefresh

5猎提、頁面導航欄如何實現(xiàn)?

通過系統(tǒng)的Navigation組件即可實現(xiàn)

@Builder content(){
    Navigation(){
      Column() {
        this.appGridView()
        // LayoutGridDratEditPage()
      }
    }
    .mode(NavigationMode.Auto)
    .titleMode(NavigationTitleMode.Mini)
    .title('編輯應用')
    .menus(this.rightBtns())
  }

6旁蔼、grid拖動編輯如何實現(xiàn)锨苏?有什么要注意的?

1)設置編輯模式屬性為true

//設置Grid是否進入編輯模式棺聊,進入編輯模式可以拖拽Grid組件內(nèi)部GridItem
.editMode(this.isEditable)

2)剛開始拖拽的時候伞租,設置拖拽懸浮展示的UI

//第一次拖拽此事件綁定的組件時,觸發(fā)回調(diào)
.onItemDragStart((event: ItemDragInfo, itemIndex: number) => {
   //設置拖拽過程中顯示的圖片
   return this.pixelMapBuilder()
})
//拖拽過程中展示的樣式
@Builder pixelMapBuilder() {
   Column() {
     Text(this.text).gridItemStyle()
   }
}

3)拖拽到目標gridItem時限佩,根據(jù)條件交換數(shù)組中的元素

//綁定此事件的組件可作為拖拽釋放目標葵诈,當在本組件范圍內(nèi)停止拖拽行為時,觸發(fā)回調(diào)
//itemIndex為拖拽起始位置祟同,insertIndex為拖拽插入位置
.onItemDrop((event: ItemDragInfo, itemIndex: number, insertIndex: number, isSuccess: boolean) => {
  let isValidInserIndex = insertIndex < this.numbers.length;
  if (isSuccess && insertIndex < this.numbers.length) {
  // if (isSuccess && isValidInserIndex) {
    //不支持拖拽到已有內(nèi)容以外的位置
    this.changeIndex(itemIndex, insertIndex)
  } else {
    Logger.warn('griditem交換失敗');
  }
})

//交換數(shù)組中元素位置
changeIndex(index1: number, index2: number) {
  Logger.warn('交換前:' + this.numbers.toString());
  [this.numbers[index1], this.numbers[index2]] = [this.numbers[index2], this.numbers[index1]];
  Logger.warn('交換后:' + this.numbers.toString());
}

4)完整代碼如下:

@Extend(Grid) function gridStyle() {
  .columnsTemplate('1fr 1fr 1fr')
  .columnsGap(10)
  .rowsGap(10)
  .width('100%')
  .backgroundColor(0xFAEEE0)
  .height('100%')
}

@Extend(Text) function gridItemStyle() {
  .fontSize(16)
  .backgroundColor(0xF9CF93)
  .width(80)
  .height(80)
  .textAlign(TextAlign.Center)
}

@Entry
@Component
struct LayoutGridDratEditPage {
  @State numbers: String[] = ['1','2','3','4','5','6','7','8','9','10']
  scroller: Scroller = new Scroller()
  @State text: string = 'drag'
  // 是否是編輯模式
  @State isEditable: boolean = true

  //拖拽過程中展示的樣式
  @Builder pixelMapBuilder() {
    Column() {
      Text(this.text).gridItemStyle()
    }
  }

  aboutToAppear() {
    // for (let i = 1;i <= 15; i++) {
    //   this.numbers.push(i.toString())
    // }
    Logger.warn('初始化后:' + this.numbers.toString());
  }

  //交換數(shù)組中元素位置
  changeIndex(index1: number, index2: number) {
    Logger.warn('交換前:' + this.numbers.toString());
    [this.numbers[index1], this.numbers[index2]] = [this.numbers[index2], this.numbers[index1]];
    Logger.warn('交換后:' + this.numbers.toString());
  }

  build() {
      Column() {
        this.dragDemo()
        // this.animalTest()
      }
  }

  @Builder dragDemo() {
    Column({ space: 5 }) {
      Text(this.isEditable?'編輯模式':'非編輯模式').fontSize(28).fontWeight(FontWeight.Bold)
        .onClick(()=>{
          this.isEditable = !this.isEditable;
        })
      Grid(this.scroller) {
        ForEach(this.numbers, (day: string,index:number) => {
          GridItem() {
            Stack(){
              Text(day).gridItemStyle()
                // .rotate({ angle: this.rotateAngle })
                .onTouch((event: TouchEvent) => {
                  if (event.type === TouchType.Down) {
                    this.text = day
                  }
                })
              if (this.isEditable) {
                Image($r('app.media.btn_Closed')).width(15).height(15).onClick(()=>{
                  // Array
                  Logger.warn('移除前:' + this.numbers.toString());
                  this.numbers.splice(index,1)
                  Logger.warn('移除后:' + this.numbers.toString());
                })
              }
            }.alignContent(Alignment.TopEnd)
          }
        })
      }
      .gridStyle()
      .onScrollIndex((first: number) => {
        console.info(first.toString())
      })
      //設置Grid是否進入編輯模式作喘,進入編輯模式可以拖拽Grid組件內(nèi)部GridItem
      .editMode(this.isEditable)
      //第一次拖拽此事件綁定的組件時,觸發(fā)回調(diào)
      .onItemDragStart((event: ItemDragInfo, itemIndex: number) => {
        //設置拖拽過程中顯示的圖片
        return this.pixelMapBuilder()
      })
      //綁定此事件的組件可作為拖拽釋放目標晕城,當在本組件范圍內(nèi)停止拖拽行為時泞坦,觸發(fā)回調(diào)
      //itemIndex為拖拽起始位置,insertIndex為拖拽插入位置
      .onItemDrop((event: ItemDragInfo, itemIndex: number, insertIndex: number, isSuccess: boolean) => {
        let isValidInserIndex = insertIndex < this.numbers.length;
        if (isSuccess && insertIndex < this.numbers.length) {
        // if (isSuccess && isValidInserIndex) {
          //不支持拖拽到已有內(nèi)容以外的位置
          this.changeIndex(itemIndex, insertIndex)
        } else {
          Logger.warn('griditem交換失敗');
        }
      })
    }
    .width('100%')
  }
}

5)注意事項

  • griditem最好設置寬高砖顷,否則可能拖拽不成功贰锁,具體現(xiàn)象就是onItemDrop方法中的insertIndex為-1,isSuccess為false

7滤蝠、子組件占滿主軸方向剩余區(qū)域如何設置豌熄?

  • 方法1:在row和column的子組件中設置layoutWeight
Column() {
      Row({space:10}){
        Image($r('app.media.logo')).width(90).height(90)
        Column(){
          Text(title).fontSize('14').width('100%')
          Blank()
          Row(){
            Text('我是按鈕前文')
            Blank()
            Button('立即查看',{type:ButtonType.Capsule}).width(80).height(35).onClick(()=>{
              Logger.debug('當前索引值:'+ index.toString() + ',當前item:' + title)
            })
          }
          .width('100%')
          .backgroundColor(Color.Orange)
        }
        .layoutWeight(1) // 占滿主軸方向剩余空間
        .height(80)
        .justifyContent(FlexAlign.SpaceBetween)
        .backgroundColor(Color.Pink)
      }
      .width('100%')
      .padding(10)

      Divider().color('#F2F2F2').strokeWidth(1).margin({left:10,right:10})
    }.width('100%')
    
  • 方法2:使用flext布局,設置flexGrow

      Flex({direction:FlexDirection.Row,alignItems:ItemAlign.Center}){
        Flex(){
          Stack(){
            Image($r('app.media.logo')).width(90).height(90)
          }.width(90).height(90)
          // .backgroundColor(Color.Blue)
        }
        // .flexBasis(120)
        .width(120)
        // .backgroundColor(Color.Green)

        Flex({direction:FlexDirection.Column,justifyContent:FlexAlign.SpaceBetween,alignItems:ItemAlign.Start}){
          Text(title).fontSize('14')
          Flex({direction:FlexDirection.Row,justifyContent:FlexAlign.SpaceBetween,alignItems:ItemAlign.Center}){
            Text('按鈕前文案')
            Button('立即查看',{type:ButtonType.Capsule}).width(80).height(35).onClick(()=>{
              Logger.debug('當前索引值:'+ index.toString() + '几睛,當前item:' + title)
            })
          }
          .backgroundColor(Color.Orange)
        }
        .flexGrow(2) // column占滿剩余空間
        // .flexShrink(1)
        .backgroundColor(Color.Pink)
        .height(80)
        .margin({left:10})
      }
      .width('100%')
      .padding(10)
      // .backgroundColor(Color.Green)
      // 2房轿、最下面的橫線
      Divider().color('#F2F2F2').strokeWidth(1).margin({left:10,right:10})
    }.width('100%')


二粤攒、常用快捷鍵

以下快捷鍵是能提效的所森,部分快捷鍵是我自定義的囱持,大家自行參考。

1焕济、com+shift+j【自定義成Xcode一致】

快速打開當前文件左側(cè)文件樹位置


image.png

2纷妆、com+7

打開structure

3、OPT+F7

提供Find Usages代碼引用查找功能晴弃,幫助開發(fā)者快速查看某個對象(變量掩幢、函數(shù)或者類等)被引用的地方,用于后續(xù)的代碼重構(gòu)上鞠,可以極大的提升開發(fā)者的開發(fā)效率际邻。

4、com+[ 和 com+]

代碼后退和代碼前進

5芍阎、兩次shift

全局搜索

6世曾、com+/

代碼注釋

7、com+左右箭頭

某行代碼谴咸,光標移到行首和行尾

8轮听、com+shift+上下方向鍵

某行代碼上移和下移

9、com+點擊(某個屬性或者方法)

快速跳轉(zhuǎn)到定義的位置岭佳,一般可以配合com+[com+]使用

10血巍、com+刪除按鈕

快速刪除某行

11、com+D

復制某行或者某段代碼

12珊随、com+上下箭頭【自定義的述寡,默認沒有】

快速滾動到文件頂部和底部<BR>
需要自定義快捷鍵,在自定義快捷鍵界面中的Scroll To TopScroll To Bottom自定義快捷鍵


三玫恳、待解決問題辨赐?

1、網(wǎng)絡庫繞過https自簽名證書如何實現(xiàn)京办?

2掀序、網(wǎng)絡庫封裝之code status問題?

3惭婿、網(wǎng)絡庫封裝之全局配置無效的問題不恭?

4、網(wǎng)絡庫封裝之異常問題财饥?

5换吧、web組件繞過https自簽名證書如何實現(xiàn)?

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末钥星,一起剝皮案震驚了整個濱河市沾瓦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖贯莺,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件风喇,死亡現(xiàn)場離奇詭異,居然都是意外死亡缕探,警方通過查閱死者的電腦和手機魂莫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來爹耗,“玉大人耙考,你說我怎么就攤上這事√妒蓿” “怎么了倦始?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長山卦。 經(jīng)常有香客問我楣号,道長,這世上最難降的妖魔是什么怒坯? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任炫狱,我火速辦了婚禮,結(jié)果婚禮上剔猿,老公的妹妹穿的比我還像新娘视译。我一直安慰自己,他們只是感情好归敬,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布酷含。 她就那樣靜靜地躺著,像睡著了一般汪茧。 火紅的嫁衣襯著肌膚如雪椅亚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天舱污,我揣著相機與錄音呀舔,去河邊找鬼。 笑死扩灯,一個胖子當著我的面吹牛媚赖,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播珠插,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼惧磺,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了捻撑?” 一聲冷哼從身側(cè)響起磨隘,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤缤底,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后番捂,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體训堆,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年白嘁,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片膘流。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡絮缅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出呼股,到底是詐尸還是另有隱情耕魄,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布彭谁,位于F島的核電站吸奴,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏缠局。R本人自食惡果不足惜则奥,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望狭园。 院中可真熱鬧读处,春花似錦、人聲如沸唱矛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽绎谦。三九已至管闷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間窃肠,已是汗流浹背包个。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留冤留,地道東北人赃蛛。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像搀菩,于是被迫代替她去往敵國和親呕臂。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容