[Daily HarmonyOS Next Knowledge] List Controllers, Adding Subcomponents to Full-Screen Videos, List Scroll Monitoring, Floating Window Button Components, Refreshing UI with Hash Dictionaries
1. Regarding the HarmonyOS List controller Scroller?
I want to use the controller in a List to scroll to a specific item, such as this.scrollerForList.scrollToItemInGroup(1, 2, true)
. The current issue is that there are three sub-layouts in a main layout, as shown in the figure. Currently, I'm using an emitter to notify the content layout to scroll, but it throws an error, as shown in the figure. I hope to get some suggestions and alternative methods.
Reference code:
import {ListItemGroupExample} from './ListItemGroupExample'
@Entry
@Component
struct ListScrollerPage {
@State message: string = 'Hello World';
@State groupInfo: number[] = [0, 0];
onClickItem(item: number[]): void {
this.groupInfo = item;
}
build() {
Row() {
Column() {
// Header layout
RecommendDetailTopView();
// Content area
ListItemGroupExample({
groupInfo: this.groupInfo
})
.layoutWeight(1);
// Footer layout
CommnetBootomView({
onClickItem: (item: number[]): void => this.onClickItem(item)
});
}
.width('100%');
}
.height('100%');
}
}
@Component
struct RecommendDetailTopView {
build() {
Row() {
Text('Header Layout');
}
}
}
@Component
struct CommnetBootomView {
@State list: number[][] = [[1, 1], [2, 1], [3, 2]];
private onClickItem = (item: number[]): void => {};
build() {
Row() {
ForEach(this.list, (item: number[]) => {
Row() {
ForEach(item, (num: number) => {
Text(`${num}`)
.margin({ right: 5 });
}, (num: number, index: number) => JSON.stringify(index));
}
.backgroundColor(Color.Pink)
.onClick(() => {
if (this.onClickItem) {
this.onClickItem(item);
}
});
}, (item: number[]) => JSON.stringify(item));
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.padding(10);
}
}
// ListItemGroupExample file
// xxx.ets
@Entry
@Component
export struct ListItemGroupExample {
private scrollerForList: ListScroller = new ListScroller();
private timeTable: TimeTable[] = [
{
title: 'Monday',
projects: ['Chinese', 'Mathematics', 'English']
},
{
title: 'Tuesday',
projects: ['Physics', 'Chemistry', 'Biology']
},
{
title: 'Wednesday',
projects: ['History', 'Geography', 'Politics']
},
{
title: 'Thursday',
projects: ['Art', 'Music', 'Physical Education']
}
];
@Link @Watch('groupInfoChange') groupInfo: number[];
groupInfoChange(): void {
console.log(JSON.stringify(this.groupInfo));
this.scrollerForList.scrollToItemInGroup(this.groupInfo[0], this.groupInfo[1], true);
}
@Builder
itemHead(text: string): void {
Text(text)
.fontSize(20)
.backgroundColor(0xAABBCC)
.width("100%")
.padding(10);
}
@Builder
itemFoot(num: number): void {
Text('Total ' + num + ' classes')
.fontSize(16)
.backgroundColor(0xAABBCC)
.width("100%")
.padding(5);
}
build() {
Column() {
List({ space: 20, scroller: this.scrollerForList }) {
ForEach(this.timeTable, (item: TimeTable) => {
ListItemGroup({ header: this.itemHead(item.title), footer: this.itemFoot(item.projects.length) }) {
ForEach(item.projects, (project: string) => {
ListItem() {
Text(project)
.width("100%")
.height(100)
.fontSize(20)
.textAlign(TextAlign.Center)
.backgroundColor(0xFFFFFF);
}
}, (item: string) => item);
}
.divider({ strokeWidth: 1, color: Color.Blue }); // Divider between rows
});
}
.width('90%')
// .height(300)
.sticky(StickyStyle.Header | StickyStyle.Footer)
.scrollBar(BarState.Off);
}
.width('100%')
.height('100%')
.backgroundColor(0xDCDCDC)
.padding({ top: 5 });
}
}
interface TimeTable {
title: string;
projects: string[];
}
2. How to add subcomponents to a full-screen video in HarmonyOS?
For video functionality developed based on the Video component, after setting full screen with VideoController, the video component appears to be placed on top and fills the entire screen, making all other components disappear. How can I add playback control components on top of the full-screen video?
Use the Stack component with z-order control to stack components, allowing playback control components to be added on top of the Video component. For details, refer to:
https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-universal-attributes-z-order-V5
3. How to monitor the scroll of a HarmonyOS List component?
Implement synchronized scroll effects for the scroll bar. Currently, I cannot obtain the scroll distance of the list on the x-axis or the total scrollable length.
It is recommended to use the Swiper component. Refer to the following documentation:
https://gitee.com/harmonyos-cases/cases/blob/master/CommonAppDevelopment/feature/swipersmoothvariation/README.md#%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E
The effect achieved by synchronizing scroll positions with the List component isδΈε¦ the Swiper. For the List component, you can refer to the onScrollIndex
method to monitor scroll events:
https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-layout-development-create-list-V5#%E5%93%8D%E5%BA%94%E6%BB%9A%E5%8A%A8%E4%BD%8D%E7%BD%AE
Many applications need to monitor and respond to changes in the scroll position of lists. For example, when scrolling a contacts list, if the scroll crosses groups starting with different letters, the alphabetical index bar on the side also needs to update to the corresponding letter position.
In addition to alphabetical indexing, scrollable lists combined with multi-level classification indexes are also common in application development. For example, in the product classification page of a shopping app, multi-level classifications also need to monitor the scroll position of the list.
As shown in the figure above, when the contacts list scrolls from A to B, the index bar on the right also needs to synchronize from the selected A state to the selected B state. This scenario can be implemented by monitoring the onScrollIndex
event of the List component. The right index bar should use the AlphabetIndexer component.
When the list scrolls, recalculate the position selectedIndex
of the corresponding letter in the alphabetical index bar based on the current index value firstIndex
of the list. Since the AlphabetIndexer component sets the selected item index value via the selected
property, when selectedIndex
changes, it triggers the AlphabetIndexer component to re-render, displaying the selected state for the corresponding letter.
const alphabets = ['#', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
@Entry
@Component
struct ContactsList {
@State selectedIndex: number = 0;
private listScroller: Scroller = new Scroller();
build() {
Stack({ alignContent: Alignment.End }) {
List({ scroller: this.listScroller }) {}
.onScrollIndex((firstIndex: number) => {
// Recalculate the position this.selectedIndex of the corresponding contact index bar based on the scrolled index value of the list
});
// Alphabet index component
AlphabetIndexer({ arrayValue: alphabets, selected: 0 })
.selected(this.selectedIndex);
}
}
}
4. How to use the floating window button component in HarmonyOS ArkTS?
For developing floating windows, refer to this documentation:
https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/application-window-stage
5. In HarmonyOS, when an array is placed in a HashMap and the array data changes (e.g., elements are added or deleted), how to trigger UI refresh?
Define a HashMap:
@State allChannelsMaps: HashMap<string, Array<ChannelGroupDataChannelList>> = new HashMap();
// UI layout
List() {
ForEach(this.channelTypeList, (channelData: ChannelTypeEntityData, index: number) => {
ListItem() {
this.channelGrid(this.allChannelsMaps.get(channelData.channelType?.toString()), index);
}
});
}
.width('100%')
.height('100%');
@Builder channelGrid(channelGroupData: Array<ChannelGroupDataChannelList>, index: number) {
Grid() {
ForEach(channelGroupData, (channel: ChannelGroupDataChannelList, index: number) => {
GridItem() {
Text(channel.channelName)
.fontSize((channel.channelName ?? '').length > 5 ? '11fp' : '15fp')
.fontColor($r('app.color.color202022'))
.fontFamily(CommonConstants.SI_YUAN)
.textAlign(TextAlign.Center)
.width(this.itemWidth)
.height(this.itemHeight)
.backgroundColor($r('app.color.colorF9F9F9'))
.borderRadius(4)
.onClick(() => {
const tapChannel: ChannelGroupDataChannelList = channelGroupData[index];
this.addChannelUpdateMap(tapChannel);
});
}
});
}
.columnsTemplate('1fr 1fr 1fr 1fr')
.columnsGap(10)
.rowsGap(10)
.height(this.channelTypeList[index].channelHeight)
.nestedScroll({
scrollForward: NestedScrollMode.PARENT_FIRST,
scrollBackward: NestedScrollMode.SELF_FIRST
});
}
Refer to the following demo:
import HashMap from '@ohos.util.HashMap';
class DataItem {
title: string;
id: number;
constructor(title: string, id: number) {
this.title = title;
this.id = id;
}
}
@Entry
@Component
struct MapExample {
@State hashMap: HashMap<string, Array<DataItem>> = new HashMap();
@State keys: Array<string> = [];
aboutToAppear(): void {
this.hashMap.set("squirrel", [new DataItem("123", 20)]);
this.hashMap.set("sparrow", [new DataItem("human", 45)]);
this.keys = Array.from(this.hashMap.keys());
console.info('valuesaaa', this.hashMap.values());
for (let key of this.keys) {
console.log("key:" + key);
console.log("value:" + this.hashMap.get(key));
}
}
build() {
List() {
ForEach(this.keys, (item: string, index: number) => {
ChildItem({ item: item });
}, (item: string) => item);
}
}
}
@Component
struct ChildItem {
@Prop item: string;
build() {
Text(this.item)
.fontSize(50);
}
}
Top comments (0)