ナビゲーションスタックの挙動とモード設定
アプリケーション内でのユーザー体験を向上させるためには、ページ間を滑らかに移動できることが不可欠です。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,
});
}
ページ間でのデータ引数の送受信
異なるコンポーネント間でデータを共有するには、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 })
}
}