• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Supporting Aging-Friendly Design
2
3## Basic Concepts
4
5Aging-friendly design offers a method to enlarge selected areas or components through a long-press action with a mouse or finger. Specifically, when the system font is larger than 1x, this action on a component with aging-friendly features extracts data from the component within the selected area and presents it in a dialog box. This way, both the component and its internal data (child components) are enlarged, and the entire component is centered on the screen for better visibility.
6
7## Constraints
8
9* Aging-friendly rules
10
11  Since components do not automatically enlarge when the system font size is greater than 1x, the aging-friendly feature is necessary to magnify the components.
12
13* Aging-friendly operations
14
15  Long-pressing a component that supports aging-friendly capabilities triggers a dialog box. The aging-friendly operation ends when the user releases the press. When the system font size is set to be greater than 1x, the component automatically enlarges, and when the system font size returns to 1x, the component returns to its normal state.
16
17* Aging-friendly objects
18
19  The components that trigger the aging-friendly operation and provide the data.
20
21* Aging-friendly dialog box targets
22
23  The components capable of receiving and processing the aging-friendly data.
24
25* Dialog box restrictions
26
27  When users set the system font to more than 2x, the dialog box content, including icons and text, is magnified at a fixed 2x scale.
28
29* Combination with other capabilities
30
31  Aging-friendly capabilities can be integrated with other features (such as swipe and drag). When the bottom tab bar (**tabBar**) component is activated for aging-friendly features, users can swipe their fingers or use a mouse to trigger aging-friendly features for other child components within the tab bar.
32
33## Aging-Friendly Component Adaptation and Activation Methods
34
35| Activation Method            | Component                                                    |
36| -------------------- | ------------------------------------------------------------ |
37| Long press on the component        | [SideBarContainer](../reference/apis-arkui/arkui-ts/ts-container-sidebarcontainer.md), [Bottom Tab Bar (tabBar)](../reference/apis-arkui/arkui-ts/ts-container-tabcontent.md#tabbar9), [Navigation](../reference/apis-arkui/arkui-ts/ts-basic-components-navigation.md), [NavDestination](../reference/apis-arkui/arkui-ts/ts-basic-components-navigation.md#navdestination10), [Tabs](../reference/apis-arkui/arkui-ts/ts-container-tabs.md)|
38| Default system font enlargement| [Picker](../reference/apis-arkui/arkui-ts/ts-basic-components-datepicker.md), [Button](../reference/apis-arkui/arkui-ts/ts-basic-components-button.md), [Menu](../reference/apis-arkui/arkui-ts/ts-basic-components-menu.md), [Stepper](../reference/apis-arkui/arkui-ts/ts-basic-components-stepper.md), [BindSheet](../reference/apis-arkui/arkui-ts/ts-universal-attributes-sheet-transition.md#bindsheet), [TextInput](../reference/apis-arkui/arkui-ts/ts-basic-components-textinput.md)/[TextArea](../reference/apis-arkui/arkui-ts/ts-basic-components-textarea.md)/[Search](../reference/apis-arkui/arkui-ts/ts-basic-components-search.md)/[SelectionMenu](../reference/apis-arkui/arkui-ts/ohos-arkui-advanced-SelectionMenu.md), [Chip](../reference/apis-arkui/arkui-ts/ohos-arkui-advanced-Chip.md#chip), [Dialog](../reference/apis-arkui/arkui-ts/ohos-arkui-advanced-Dialog.md), [Slider](../reference/apis-arkui/arkui-ts/ts-basic-components-slider.md), [Progress](../reference/apis-arkui/arkui-ts/ts-basic-components-progress.md), [Badge](../reference/apis-arkui/arkui-ts/ts-container-badge.md)|
39
40## Example
41
42This example involves the **SideBarContainer** component, which triggers an aging-friendly dialog box when the control button is long-pressed. The dialog box does not appear if the system font size is at the default 1x setting; it appears only when the system font size is set to greater than 1x.
43
44```ts
45import { abilityManager, Configuration } from '@kit.AbilityKit';
46import { BusinessError } from '@kit.BasicServicesKit';
47
48@Entry
49@Component
50struct SideBarContainerExample {
51  @State currentFontSizeScale: number = 1
52  normalIcon: Resource = $r("app.media.icon")
53  selectedIcon: Resource = $r("app.media.icon")
54  @State arr: number[] = [1, 2, 3]
55  @State current: number = 1
56  @State title: string = 'Index01';
57  // Set the font scale.
58  async setFontScale(scale: number): Promise<void> {
59    let configInit: Configuration = {
60      language: 'zh-Ch',
61      fontSizeScale: scale,
62    };
63    // Update the font size.
64    abilityManager.updateConfiguration(configInit, (err: BusinessError) => {
65      if (err) {
66        console.error(`updateConfiguration fail, err: ${JSON.stringify(err)}`);
67      } else {
68        this.currentFontSizeScale = scale;
69        console.log('updateConfiguration success.');
70      }
71    });
72  }
73
74  build() {
75    SideBarContainer(SideBarContainerType.Embed) {
76      Column() {
77        ForEach(this.arr, (item: number) => {
78          Column({ space: 5 }) {
79            Image(this.current === item ? this.selectedIcon : this.normalIcon).width(64).height(64)
80            Text("0" + item)
81              .fontSize(25)
82              .fontColor(this.current === item ? '#0A59F7' : '#999')
83              .fontFamily('source-sans-pro,cursive,sans-serif')
84          }
85          .onClick(() => {
86            this.current = item;
87            this.title = "Index0" + item;
88          })
89        }, (item: string) => item)
90      }.width('100%')
91      .justifyContent(FlexAlign.SpaceEvenly)
92      .backgroundColor($r('sys.color.mask_fifth'))
93
94      Column() {
95        Text(this.title)
96        Button('1x').onClick(() => {
97          this.setFontScale(1)
98        }).margin(10)
99        Button('1.75x').onClick(() => {
100          this.setFontScale(1.75)
101        }).margin(10)
102        Button('2x').onClick(() => {
103          this.setFontScale(2)
104        }).margin(10)
105        Button('3.2x').onClick(() => {
106          this.setFontScale(3.2)
107        }).margin(10)
108      }
109      .margin({ top: 50, left: 20, right: 30 })
110    }
111    .controlButton({
112      icons: {
113        hidden: $r('sys.media.ohos_ic_public_drawer_open_filled'),
114        shown: $r('sys.media.ohos_ic_public_drawer_close')
115      }
116    })
117    .sideBarWidth(150)
118    .minSideBarWidth(50)
119    .maxSideBarWidth(300)
120    .minContentWidth(0)
121    .onChange((value: boolean) => {
122      console.info('status:' + value)
123    })
124    .divider({ strokeWidth: '1vp', color: Color.Gray, startMargin: '4vp', endMargin: '4vp' })
125  }
126}
127```
128
129Switching system font sizes and long-pressing components with aging-friendly capabilities yields the effects as follows.
130
131| System Font at 1x (Before Aging-Friendly Features Are Enabled)| System Font at 1.75x (After Aging-Friendly Features Are Enabled)|
132| ---------------------------------- | ------------------------------------ |
133| ![](figures/aging_01.png)          | ![](figures/aging_02.png)            |
134
135The **TextPickerDialog** component triggers an aging-friendly dialog box when the system font is set to greater than 1x, which does not occur at the default 1x setting.
136
137```ts
138import { abilityManager, Configuration } from '@kit.AbilityKit';
139import { BusinessError } from '@kit.BasicServicesKit';
140
141@Entry
142@Component
143struct TextPickerExample {
144  private select: number | number[] = 0;
145  private cascade: TextCascadePickerRangeContent[] = [
146    {
147      text: 'Category 1',
148      children: [{ text: 'Subcategory 1', children: [{ text: 'Subcategory 2' }, { text: 'Subcategory 3' }, { text: 'Subcategory 4' }] },
149        { text: 'Item 1', children: [{ text: 'Item 2' }, { text: 'Item 3' }, { text: 'Item 4' }] }]
150    },
151    {
152      text: 'Category 2',
153      children: [{ text: 'Subcategory 1', children: [{ text: 'Subcategory 2' }, { text: 'Subcategory 3' }, { text: 'Subcategory 4' }] },
154        { text: 'Item 1', children: [{ text: 'Item 2' }, { text: 'Item 3' }, { text: 'Item 4' }] }]
155    },
156    {
157      text: 'Category 3',
158      children: [{ text: 'Subcategory 1', children: [{ text: 'Subcategory 2' }, { text: 'Subcategory 3' }, { text: 'Subcategory 4' }] },
159        { text: 'Item 1', children: [{ text: 'Item 2' }, { text: 'Item 3' }, { text: 'Item 4' }] }]
160    }
161  ]
162  @State v: string = '';
163  @State showTriggered: string = '';
164  private triggered: string = '';
165  private maxLines: number = 3;
166  // Set the font scale.
167  async setFontScale(scale: number): Promise<void> {
168    let configInit: Configuration = {
169      fontSizeScale: scale,
170    };
171
172    abilityManager.updateConfiguration(configInit, (err: BusinessError) => {
173      if (err) {
174        console.error(`updateConfiguration fail, err: ${JSON.stringify(err)}`);
175      } else {
176        console.log('updateConfiguration success.');
177      }
178    });
179  }
180
181  linesNum(max: number): void {
182    let items: string[] = this.triggered.split('\n').filter(item => item != '');
183    if (items.length > max) {
184      this.showTriggered = items.slice(-this.maxLines).join('\n');
185    } else {
186      this.showTriggered = this.triggered;
187    }
188  }
189
190  build() {
191    Column() {
192      Button("TextPickerDialog.show:" + this.v)
193        .onClick(() => {
194          this.getUIContext().showTextPickerDialog({
195            range: this.cascade,
196            selected: this.select,
197            onAccept: (value: TextPickerResult) => {
198              this.select = value.index
199              console.log(this.select + '')
200              this.v = value.value as string
201              console.info("TextPickerDialog:onAccept()" + JSON.stringify(value))
202              if (this.triggered != '') {
203                this.triggered += `\nonAccept(${JSON.stringify(value)})`;
204              } else {
205                this.triggered = `onAccept(${JSON.stringify(value)})`;
206              }
207              this.linesNum(this.maxLines);
208            },
209            onCancel: () => {
210              console.info("TextPickerDialog:onCancel()")
211              if (this.triggered != '') {
212                this.triggered += `\nonCancel()`;
213              } else {
214                this.triggered = `onCancel()`;
215              }
216              this.linesNum(this.maxLines);
217            },
218            onChange: (value: TextPickerResult) => {
219              console.info("TextPickerDialog:onChange()" + JSON.stringify(value))
220              if (this.triggered != '') {
221                this.triggered += `\nonChange(${JSON.stringify(value)})`;
222              } else {
223                this.triggered = `onChange(${JSON.stringify(value)})`;
224              }
225              this.linesNum(this.maxLines);
226            },
227          })
228        })
229        .margin({ top: 60 })
230
231      Row() {
232        Button('1x').onClick(() => {
233          this.setFontScale(1)
234        }).margin(10)
235        Button('1.75x').onClick(() => {
236          this.setFontScale(1.75)
237        }).margin(10)
238
239        Button('2x').onClick(() => {
240          this.setFontScale(2)
241        }).margin(10)
242        Button('3.2x').onClick(() => {
243          this.setFontScale(3.2)
244        }).margin(10)
245      }.margin({ top: 50 })
246    }
247
248  }
249}
250```
251
252| System Font at 1x (Before Aging-Friendly Features Are Enabled)| System Font at 1.75x (After Aging-Friendly Features Are Enabled)|
253| ---------------------------------- | ------------------------------------ |
254| ![](figures/aging_03.png)          | ![](figures/aging_04.png)            |
255