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  To ensure that components enlarge appropriately when the system font size is greater than 1x, you need to configure the [configuration tag](../quick-start/app-configuration-file.md#configuration) for implementing an aging-friendly feature.
12
13<!--RP1--><!--RP1End-->
14
15* Aging-friendly operations
16
17  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.
18
19* Aging-friendly objects
20
21  The components that trigger the aging-friendly operation and provide the data.
22
23* Aging-friendly dialog box targets
24
25  The components capable of receiving and processing the aging-friendly data.
26
27* Dialog box restrictions
28
29  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.
30
31* Combination with other capabilities
32
33  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.
34
35## Aging-Friendly Component Adaptation and Activation Methods
36
37| Activation Method            | Component                                                    |
38| -------------------- | ------------------------------------------------------------ |
39| 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)|
40| Default system font enlargement| [PickerDialog](../reference/apis-arkui/arkui-ts/ts-methods-calendarpicker-dialog.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)|
41
42## Example
43
44<!--RP2-->
45This example uses the **SideBarContainer** component to trigger an aging-friendly dialog box through a long press of the control button. Note that the dialog box does not appear if the system font size is at the 1x setting. Instead, it appears only when the system font size is set to greater than 1x.
46
47```ts
48import { abilityManager, Configuration } from '@kit.AbilityKit';
49import { BusinessError } from '@kit.BasicServicesKit';
50
51@Entry
52@Component
53struct SideBarContainerExample {
54  @State currentFontSizeScale: number = 1
55  normalIcon: Resource = $r("app.media.icon")
56  selectedIcon: Resource = $r("app.media.icon")
57  @State arr: number[] = [1, 2, 3]
58  @State current: number = 1
59  @State title: string = 'Index01';
60  // Set the font scale.
61  async setFontScale(scale: number): Promise<void> {
62    let configInit: Configuration = {
63      language: 'zh-Ch',
64      fontSizeScale: scale,
65    };
66    // Update the font size.
67    abilityManager.updateConfiguration(configInit, (err: BusinessError) => {
68      if (err) {
69        console.error(`updateConfiguration fail, err: ${JSON.stringify(err)}`);
70      } else {
71        this.currentFontSizeScale = scale;
72        console.log('updateConfiguration success.');
73      }
74    });
75  }
76
77  build() {
78    SideBarContainer(SideBarContainerType.Embed) {
79      Column() {
80        ForEach(this.arr, (item: number) => {
81          Column({ space: 5 }) {
82            Image(this.current === item ? this.selectedIcon : this.normalIcon).width(64).height(64)
83            Text("0" + item)
84              .fontSize(25)
85              .fontColor(this.current === item ? '#0A59F7' : '#999')
86              .fontFamily('source-sans-pro,cursive,sans-serif')
87          }
88          .onClick(() => {
89            this.current = item;
90            this.title = "Index0" + item;
91          })
92        }, (item: string) => item)
93      }.width('100%')
94      .justifyContent(FlexAlign.SpaceEvenly)
95      .backgroundColor($r('sys.color.mask_fifth'))
96
97      Column() {
98        Text(this.title)
99        Button('1x').onClick(() => {
100          this.setFontScale(1)
101        }).margin(10)
102        Button('1.75x').onClick(() => {
103          this.setFontScale(1.75)
104        }).margin(10)
105        Button('2x').onClick(() => {
106          this.setFontScale(2)
107        }).margin(10)
108        Button('3.2x').onClick(() => {
109          this.setFontScale(3.2)
110        }).margin(10)
111      }
112      .margin({ top: 50, left: 20, right: 30 })
113    }
114    .controlButton({
115      icons: {
116        hidden: $r('sys.media.ohos_ic_public_drawer_open_filled'),
117        shown: $r('sys.media.ohos_ic_public_drawer_close')
118      }
119    })
120    .sideBarWidth(150)
121    .minSideBarWidth(50)
122    .maxSideBarWidth(300)
123    .minContentWidth(0)
124    .onChange((value: boolean) => {
125      console.info('status:' + value)
126    })
127    .divider({ strokeWidth: '1vp', color: Color.Gray, startMargin: '4vp', endMargin: '4vp' })
128  }
129}
130```
131
132Switching system font sizes and long-pressing components with aging-friendly capabilities yields the effects as follows.
133
134| System Font at 1x (Before Aging-Friendly Features Are Enabled)| System Font at 1.75x (After Aging-Friendly Features Are Enabled)|
135| ---------------------------------- | ------------------------------------ |
136| ![](figures/aging_01.png)          | ![](figures/aging_02.png)            |
137
138The [TextPickerDialog](../reference/apis-arkui/arkui-ts/ts-methods-textpicker-dialog.md) 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.
139
140```ts
141import { abilityManager, Configuration } from '@kit.AbilityKit';
142import { BusinessError } from '@kit.BasicServicesKit';
143
144@Entry
145@Component
146struct TextPickerExample {
147  private select: number | number[] = 0;
148  private cascade: TextCascadePickerRangeContent[] = [
149    {
150      text: 'Category 1',
151      children: [{ text: 'Subcategory 1', children: [{ text: 'Subcategory 2' }, { text: 'Subcategory 3' }, { text: 'Subcategory 4' }] },
152        { text: 'Item 1', children: [{ text: 'Item 2' }, { text: 'Item 3' }, { text: 'Item 4' }] }]
153    },
154    {
155      text: 'Category 2',
156      children: [{ text: 'Subcategory 1', children: [{ text: 'Subcategory 2' }, { text: 'Subcategory 3' }, { text: 'Subcategory 4' }] },
157        { text: 'Item 1', children: [{ text: 'Item 2' }, { text: 'Item 3' }, { text: 'Item 4' }] }]
158    },
159    {
160      text: 'Category 3',
161      children: [{ text: 'Subcategory 1', children: [{ text: 'Subcategory 2' }, { text: 'Subcategory 3' }, { text: 'Subcategory 4' }] },
162        { text: 'Item 1', children: [{ text: 'Item 2' }, { text: 'Item 3' }, { text: 'Item 4' }] }]
163    }
164  ]
165  @State v: string = '';
166  @State showTriggered: string = '';
167  private triggered: string = '';
168  private maxLines: number = 3;
169  // Set the font scale.
170  async setFontScale(scale: number): Promise<void> {
171    let configInit: Configuration = {
172      fontSizeScale: scale,
173    };
174
175    abilityManager.updateConfiguration(configInit, (err: BusinessError) => {
176      if (err) {
177        console.error(`updateConfiguration fail, err: ${JSON.stringify(err)}`);
178      } else {
179        console.log('updateConfiguration success.');
180      }
181    });
182  }
183
184  linesNum(max: number): void {
185    let items: string[] = this.triggered.split('\n').filter(item => item != '');
186    if (items.length > max) {
187      this.showTriggered = items.slice(-this.maxLines).join('\n');
188    } else {
189      this.showTriggered = this.triggered;
190    }
191  }
192
193  build() {
194    Column() {
195      Button("TextPickerDialog.show:" + this.v)
196        .onClick(() => {
197          this.getUIContext().showTextPickerDialog({
198            range: this.cascade,
199            selected: this.select,
200            onAccept: (value: TextPickerResult) => {
201              this.select = value.index
202              console.log(this.select + '')
203              this.v = value.value as string
204              console.info("TextPickerDialog:onAccept()" + JSON.stringify(value))
205              if (this.triggered != '') {
206                this.triggered += `\nonAccept(${JSON.stringify(value)})`;
207              } else {
208                this.triggered = `onAccept(${JSON.stringify(value)})`;
209              }
210              this.linesNum(this.maxLines);
211            },
212            onCancel: () => {
213              console.info("TextPickerDialog:onCancel()")
214              if (this.triggered != '') {
215                this.triggered += `\nonCancel()`;
216              } else {
217                this.triggered = `onCancel()`;
218              }
219              this.linesNum(this.maxLines);
220            },
221            onChange: (value: TextPickerResult) => {
222              console.info("TextPickerDialog:onChange()" + JSON.stringify(value))
223              if (this.triggered != '') {
224                this.triggered += `\nonChange(${JSON.stringify(value)})`;
225              } else {
226                this.triggered = `onChange(${JSON.stringify(value)})`;
227              }
228              this.linesNum(this.maxLines);
229            },
230          })
231        })
232        .margin({ top: 60 })
233
234      Row() {
235        Button('1x').onClick(() => {
236          this.setFontScale(1)
237        }).margin(10)
238        Button('1.75x').onClick(() => {
239          this.setFontScale(1.75)
240        }).margin(10)
241
242        Button('2x').onClick(() => {
243          this.setFontScale(2)
244        }).margin(10)
245        Button('3.2x').onClick(() => {
246          this.setFontScale(3.2)
247        }).margin(10)
248      }.margin({ top: 50 })
249    }
250
251  }
252}
253```
254
255| System Font at 1x (Before Aging-Friendly Features Are Enabled)| System Font at 1.75x (After Aging-Friendly Features Are Enabled)|
256| ---------------------------------- | ------------------------------------ |
257| ![](figures/aging_03.png)          | ![](figures/aging_04.png)            |
258<!--RP2End-->
259