HarmonyOS アプリ開発:ArkUI を用いたページ遷移とパラメータ管理の実装

ナビゲーションスタックの挙動とモード設定

アプリケーション内でのユーザー体験を向上させるためには、ページ間を滑らかに移動できることが不可欠です。ArkUI のルーティング機構では、現在のページ状態を維持するかどうかによって、複数の操作モードが提供されています。主に「スタンダードモード」と「シングルモード」が利用可能です。

例えば、ログイン後のプロフィールページへの移動や、設定値の変更を行う際のテーマ画面など、戻ってきた際に元の状態を保っておきたいケースでは、バックグラウンドで保持されることがあります。一方、検索結果から詳細へ進む際など、前のリストページに戻っても同じ検索条件を表示する必要がない場合などは、スタックから除外する設計が可能です。

スタックへの追加と置換の違い

router.pushUrl は通常、スタックに新しいページを追加します。これにより、「戻る」ボタンを押したときに直前のページが表示されます。一方、router.replaceUrl は現在のページスタックをターゲットのページで置き換えます。このため、ユーザーが戻るボタンを押した場合、現在の画面ではなく、その前に存在していたページへ遷移することになります。


// 設定画面遷移例:標準的なプッシュ操作
function navigateToSetting(): void {
  router.pushUrl({
    url: '/pages/Settings',
    mode: router.RouterMode.Standard,
    callback: (err) => {
      if (err) {
        console.error(`Navigation failed: ${err.message}`);
        return;
      }
      console.info('Settings page opened successfully.');
    }
  });
}

// シングルモードでのテーマ画面開示
function openThemePanel(): void {
  router.pushUrl({
    url: '/pages/ThemeSelection',
    mode: router.RouterMode.Single,
  });
}
ナビゲーションスタックの概念図:Standard と Single モードの違いを示す

ページ間でのデータ引数の送受信

異なるコンポーネント間でデータを共有するには、URL パラメータを使用する手法が一般的です。これは情報の一貫性を保ちつつ、簡易的な状態伝達を実現するために有効です。

前方へのパラメータ送信

メイン画面から詳細画面へアクセスする際、対象となる ID や関連データを含むオブジェクトを params プロパティに指定して渡します。


// メイン画面(Home)から詳細画面へ遷移
class UserProfile {
  userName: string;
  userAge: number;
}

class AppContext {
  userData: UserProfile = { userName: "Guest", userAge: 0 };
}

function goToDetail(): void {
  const payload: UserProfile = {
    userName: "Developer",
    userAge: 28
  };

  router.pushUrl({
    url: '/pages/UserDetails',
    params: payload,
    callback: (err) => {
      if (err) console.warn(err.message);
    }
  });
}

引数の取得と解析

遷移先側のコンポーネントでは、router.getParams() を呼び出すことで受け取ったデータを入手できます。取得したオブジェクトから必要な属性を読み取り、表示ロジックに使用します。


@Entry
@Component
struct UserDetails {
  @State userInfo: string = '';

  build() {
    Column() {
      Text(this.userInfo)
        .fontSize(18)
        .fontColor(Color.Blue)
      
      Button('閉じる')
        .onClick(() => {
          router.back();
        })
    }
    .width('100%').height('100%').justifyContent(FlexAlign.Center)
  }

  onPageShow() {
    // 親ページから受け渡されたパラメータを取得
    const routeParams = router.getParams() as UserProfile;
    
    if (routeParams) {
      this.userInfo = `Name: ${routeParams.userName}, Age: ${routeParams.userAge}`;
    }
  }
}

後方からの通信について

ページを閉じて戻る際も同様にパラメータを返すことが可能です。router.back() メソッドはオプション引数を受け付け、戻り値としてデータを送信する設定もできます。


// 戻る際に結果を返す処理
function confirmExitAndSendResult(): void {
  router.back({
    url: '/pages/MainMenu',
    params: { status: 'processed', timestamp: Date.now() }
  });
}

終了前の安全確認ダイアログ

特に重要な変更を加えた直後のページや、未保存データが存在する状況では、意図しない離脱を防ぐためにポップアップ確認が必要です。これにより、ユーザーは「取消」を選択して作業を継続するか、「確認」を選んで移動するかを選べます。

標準の確認ダイアログ

システム標準の promptAction を活用することで、カスタムスタイルを確認メッセージとして表示できます。


import promptAction from '@ohos.promptAction';

function handleBackWithConfirmation(): void {
  promptAction.showDialog({
    title: '確認',
    message: '変更内容は保存されていません。本当に退座しますか?',
    buttons: [
      { text: '中止', color: '#AAAAAA' },
      { text: 'はい', color: '#007DFF' }
    ]
  }).then((result) => {
    if (result.index === 1) {
      // ユーザーが確認ボタンをクリックした場合のみ実行
      router.back();
    } else {
      console.log('Operation cancelled by user.');
    }
  }).catch((err) => {
    console.error(`Prompt error: ${err.message}`);
  });
}

@Entry
@Component
struct SafeExitPage {
  @State isActive: boolean = true;

  build() {
    Column() {
      // サンプル画像のプレースホルダー
      // [ここでは実際のアセットまたはプレビュー画像が表示される想定]
      
      Button('編集画面を開く')
        .width('80%')
        .marginTop(50)
        .onClick(() => {
           // 他の画面遷移ロジック
        })
        
      Row() {
        Button('キャンセル')
        Button('保存して終了')
      }
      .padding(20)
    }
    .onBackPressed(() => {
      handleBackWithConfirmation();
      return true; // ダブルバック防止などのロジック制御
    })
  }
}

UI コントロールの動作検証

サイズ調整やスライダー操作を実装している場合、状態管理を通じて反映を行います。以下のコード例では、画像表示サイズの動的変更と、それを制御するためのインターフェースを示しています。


@Entry
@Component
struct ResizePreview {
  @State currentSize: number = 150;
  
  build() {
    Column() {
      Text('プレビューサイズ調整')
        .fontSize(20).fontWeight(FontWeight.Bold)
        .marginBottom(20)

      Image($r('app.media.preview'))
        .width(this.currentSize.toString() + 'px')
        .height(this.currentSize.toString() + 'px')
        .borderRadius(10)

      Slider({
        min: 100,
        max: 300,
        value: this.currentSize,
        step: 10
      })
        .width('90%')
        .onChange(val => {
          this.currentSize = val;
        })

      Divider().margin({ top: 20, bottom: 20 })

      Row() {
        Button('-')
          .width(60).height(40)
          .onClick(() => {
            if (this.currentSize > 100) this.currentSize -= 10;
          })
          
        Button('+')
          .width(60).height(40)
          .onClick(() => {
            if (this.currentSize < 300) this.currentSize += 10;
          })
      }
      .justifyContent(FlexAlign.SpaceEvenly)

      Button('リターン')
        .width('100%')
        .padding(15)
        .backgroundColor('#EEE')
        .onClick(handleBackWithConfirmation)
    }
    .width('100%').height('100%').justifyContent(FlexAlign.Start)
    .padding({ left: 20, right: 20 })
  }
}

タグ: HarmonyOS ArkUI navigation router-api param-passing

6月23日 16:57 投稿