本篇内容 继续学习媒体查询(mediaquery)列表(List)风格(Grid/GridItem)和轮播(Swiper)
一、知识储备
1. 媒体查询(mediaquery)
① 媒体查询作为响应式设计的核心,在移动设备上应用十分广泛。可以根据不同设备类型或同设备不同状态修改应用的样式。常用于根据主题、横竖屏切换、分屏等场景展示相应布局界面、
② 用法
import mediaQuery from '@ohos.mediaquery'; //导入媒体查询模块
- 通过matchMediaSync接口设置媒体查询条件,保存返回的条件监听句柄Listener.例如监听横屏事件:
let listener = mediaQuery.matchMediaSync('(orientation: landscape)');//获取监听横屏事件的句柄
- 给条件监听句柄listener绑定回调函数onPortrait,当listener检测设备状态变化时执行回调函数。在回调函数内,根据不同设备状态更改页面布局或实现业务逻辑。
onPortrait(mediaQueryResult) { //当满足媒体查询条件时,触发
if (mediaQueryResult.matches) { //根据横竖屏状态回调,更改相应页面布局
this.text = '横屏'
this.color = '#aa8888'
} else {
this.text = '竖屏'
this.color = '#00c250'
}
}
aboutToAppear() {
portraitFunc = this.onPortrait.bind(this) //绑定当前应用实例
this.listener.on('change', portraitFunc); //绑定回调函数
}
2. 创建列表(List)
① 列表就是一个可以横向或竖向滑动的容器
② List容器里,可以包含ListItem或ListItemGroup,其中ListItemGroup用于列表数据的分组展示,其子组件也是ListItem。
③
3. 创建网格(Grid/GridItem)
① 风格布局,就是由行和列分割的单元格所组成,
② 特点,具体页面尺寸均分能力、子组件占比排版。
③ 适用场景:日历、计算器、九宫格图片展示等
④ Grid容器里,包含GridItem。有三种布局情况
- 行、列数量与占比同时设置:展示固定行列数,且不可以滚动。推荐使用
- 只设置行、列数与占比中的一个。按照设置的方向进行排布,超出组件可以滚动
- 行列数与占比都不设置,组件在布局方向上排布,其行列数由布局方向、单个网格的宽高等多个属性共同决定。超出行列容纳范围的元素不展示,且不可滚动
4. 创建轮播(Swiper)
① 就是提供了滑动轮播显示的能力
② 自身宽高未设置时,同步子组件的宽高。如果设置了宽高,则以设置宽高值为准
二、效果一览
三、源码剖析
import mediaQuery from '@ohos.mediaquery'; //导入媒体查询模块
import window from '@ohos.window';
let portraitFunc = null; //获取监听横屏事件的句柄
@Entry
@Component
struct MediaQueryPage {
@State color: string = '#00c250'
@State text: string = '竖屏'
listener = mediaQuery.matchMediaSync('(orientation: landscape)'); //获取监听横屏事件的句柄
onPortrait(mediaQueryResult) { //当满足媒体查询条件时,触发
if (mediaQueryResult.matches) { //根据横竖屏状态回调,更改相应页面布局
this.text = '横屏'
this.color = '#aa8888'
} else {
this.text = '竖屏'
this.color = '#00c250'
}
}
aboutToAppear() {
portraitFunc = this.onPortrait.bind(this) //绑定当前应用实例
this.listener.on('change', portraitFunc); //绑定回调函数
}
private changeOrientation(isLandscape: boolean) { //定义改变横竖屏状态函数
let context = getContext(this)
window.getLastWindow(context).then((lastWindow) => { //手动变更横竖屏状态
lastWindow.setPreferredOrientation(isLandscape ? window.Orientation.LANDSCAPE : window.Orientation.PORTRAIT)
})
}
build() {
Column({ space: 50 }) {
Text(this.text).fontSize(55).fontColor(this.color)
Button() {
Text('横屏').fontSize(55).fontColor(this.color).backgroundColor(Color.Blue)
}.type(ButtonType.Capsule)
.onClick(() => {
this.changeOrientation(true)
})
Button() {
Text('竖屏').fontSize(55).fontColor(this.color).backgroundColor(Color.Brown)
}
.onClick(() => {
this.changeOrientation(false)
})
}
}
}
const indexArr = ['热门', '古风', '沿海', '临时'] //定义右侧滚动响应位置
@Entry
@Component
struct ListPage {
private listScroller: Scroller = new Scroller(); //初始化一个Scroller对象
@State selectedIndex: number = 0;
private listData: City[] = [];
private tempData: string[] = ['日本', '韩国'];
aboutToAppear() {
this.listData[0] = (new City('热门城市', ['北京', '上海', '广州', '深圳']));
this.listData[1] = (new City('古风城市', ['西安', '洛阳', '开封', '南京', '太原', '武汉', '荆州']));
this.listData[2] = (new City('沿海城市', ['秦皇岛', '吉林', '金门', '台湾']));
}
@Builder itemHead(title: string) { //定义分组头组件
Text(title)
.fontSize(55)
.fontColor('#1f9278')
.width('100%')
.backgroundColor('#f1f2f5')
.padding(10)
}
@Builder itemEnd(index: number, childIndex: number) {
Button({ type: ButtonType.Circle }) {
Image($r('app.media.icon'))
.width(20)
.height(20)
}
.onClick(() => {
let citys: City = this.listData[index];
console.error(citys.cityArr.length + '')
citys.cityArr.splice(childIndex, 1)
console.error(citys.cityArr.length + '')
})
}
build() {
Stack({ alignContent: Alignment.BottomEnd }) {
List({ space: 20, scroller: this.listScroller }) { //绑定Scroller对象
ForEach(this.listData, (item, index) => { //迭代创建子组件
ListItemGroup({ header: this.itemHead(item.title) }) { //支持分组展示
ForEach(item.cityArr, (city: string, childIndex) => {
ListItem() {
Row() { //封闭单个子组装
Image($r('app.media.app_icon'))
.width(40)
.height(40)
.margin(10)
Text(city)
.fontSize(33)
.fontColor(0x00c250)
}.width('100%')
}.backgroundColor(Color.White)
.swipeAction({ start: this.itemEnd(index, childIndex) }) //设置侧滑属性
})
}
.divider({ //添加分隔线
strokeWidth: 1,
startMargin: 45,
endMargin: 12,
color: 0xf70000
})
})
}
.sticky(StickyStyle.Header) //设置吸顶
.onScrollIndex((firstIndex: number) => {
this.selectedIndex = firstIndex;
})
.scrollBar(BarState.Auto) //添加滚动条样式
.backgroundColor(0xf7f7f7)
.listDirection(Axis.Vertical) //默认垂直方向滑动可以不用设置,Axis.Horizontal是横向滑动
.lanes({
minLength: 300,
maxLength: 500
}) //取值为LengthConstrain类型,表示会根据LengthConstrain与List组件的尺寸自适应决定行或列数、也可以设置为number类型比如3,如果不设置默认为1
.alignListItem(ListItemAlign.Start) //表示列表项居中对齐、默认值是Start,即首部对齐
AlphabetIndexer({ arrayValue: indexArr, selected: 0 }) //字母表索引组件,
.selected(this.selectedIndex)
.height('40%')
Button('返回顶部')
.type(ButtonType.Capsule)
.padding({ top: 8, left: 12, right: 12, bottom: 8 })
.onClick(() => {
this.listScroller.scrollToIndex(0);
})
Button('添加')
.type(ButtonType.Capsule)
.padding({ top: 8, left: 12, right: 12, bottom: 8 })
.onClick(() => {
TextPickerDialog.show({
range: this.tempData,
onAccept: (value: TextPickerResult) => {
this.listData.push(new City("临时", [this.tempData[value.index]]))
}
})
})
.margin({ bottom: 60 })
}
}
}
@Observed
class City {
public title: string;
public cityArr: string[];
constructor(title: string, cityArr: string[]) {
this.title = title;
this.cityArr = cityArr;
}
}
@Entry
@Component
struct GridPage {
build() {
Row() {
// MyComputer()
MyCalendar()
}
}
}
@Component
struct MyComputer {
@State listData: string[] = ['CE', 'C', '/', 'X', '7', '8', '9', '-', '4', '5', '6', '+', '1', '2', '3', '=', '0', '.']
build() {
Grid() {
ForEach(this.listData, key => {
if (key === '0') {
GridItem() {
Text(key)
}.backgroundColor(0xf7f7f7)
.columnStart(1)
.columnEnd(2)
} else if (key === '=') {
GridItem() {
Text(key)
}.backgroundColor(0xf7f7f7)
.rowStart(4)
.rowEnd(5)
} else {
GridItem() {
Text(key)
}.backgroundColor(0xf7f7f7)
}
})
}
.backgroundColor(Color.White)
.rowsTemplate('1fr 1fr 1fr 1fr 1fr') //总共几行,每行占比 rowsTemplate columnTemplate只设置其中一项时,组件超过屏幕时可以滚动
.columnsTemplate('1fr 1fr 1fr 1fr') //总共几列,每列占比
.maxCount(4)
.layoutDirection(GridDirection.Row) //每行最多四个
.columnsGap(10) //列间距
.rowsGap(10) //行间距
}
}
@Component
struct MyCalendar {
private scroller: Scroller = new Scroller();
@State dayArr: string[] = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
'11', '12', '13', '14', '15', '16', '17', '18', '19', '20',
'21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31',
'1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
'11', '12', '13', '14', '15', '16', '17', '18', '19', '20',
'21', '22', '23', '24', '25', '26', '27', '28', '29',
'1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
'11', '12', '13', '14', '15', '16', '17', '18', '19', '20',
'21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31',
'1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
'11', '12', '13', '14', '15', '16', '17', '18', '19', '20',
'21', '22', '23', '24', '25', '26', '27', '28', '29', '30',
'1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
'11', '12', '13', '14', '15', '16', '17', '18', '19', '20',
'21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31',
'1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
'11', '12', '13', '14', '15', '16', '17', '18', '19', '20',
'21', '22', '23', '24', '25', '26', '27', '28', '29', '30',
'1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
'11', '12', '13', '14', '15', '16', '17', '18', '19', '20',
'21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31',
'1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
'11', '12', '13', '14', '15', '16', '17', '18', '19', '20',
'21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31',
'1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
'11', '12', '13', '14', '15', '16', '17', '18', '19', '20',
'21', '22', '23', '24', '25', '26', '27', '28', '29', '30',
'1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
'11', '12', '13', '14', '15', '16', '17', '18', '19', '20',
'21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31',
'1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
'11', '12', '13', '14', '15', '16', '17', '18', '19', '20',
'21', '22', '23', '24', '25', '26', '27', '28', '29', '30',
'1', '2', '3', '4', '5', '6', '7', '8', '9', '10',
'11', '12', '13', '14', '15', '16', '17', '18', '19', '20',
'21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31'
]
build() {
Column({ space: 5 }) {
Grid(this.scroller) {
ForEach(this.dayArr, day => {
GridItem() {
Text(day).fontSize(33)
}
.backgroundColor("#f7f7f7")
})
}
.columnsTemplate('1fr 1fr 1fr 1fr 1fr 1fr 1fr')
// .rowsTemplate('1fr 1fr 1fr 1fr 1fr')
.columnsGap(5)
.height('25%')
.rowsGap(5)
.backgroundColor(Color.White)
Button('上一页')
.type(ButtonType.Capsule)
.onClick(() => {
this.scroller.scrollPage({
next: false
})
})
Button('下一页')
.type(ButtonType.Capsule)
.onClick(() => {
this.scroller.scrollPage({
next: true
})
})
}
}
}
@Component
@Entry
struct SwiperPage {
private swiperController: SwiperController = new SwiperController();
build() {
Stack() {
Swiper(this.swiperController) {
Text('0')
.width('90%')
.height('100%')
.backgroundColor(Color.Red)
.textAlign(TextAlign.Center)
.fontSize(55)
Text('1')
.width('90%')
.height('100%')
.backgroundColor(Color.Blue)
.textAlign(TextAlign.Center)
.fontSize(55)
Text('2')
.width('90%')
.height('100%')
.backgroundColor(Color.Green)
.textAlign(TextAlign.Center)
.fontSize(55)
Text('3')
.width('90%')
.height('100%')
.backgroundColor(Color.Yellow)
.textAlign(TextAlign.Center)
.fontSize(55)
}
.indicatorStyle({
size: 25,
left: 0,
color: Color.Orange
})
.displayCount(2)//可以设置一个页面内展示多个子组件
.vertical(true)//false 水平轮播 true垂直轮播
.indicator(true)
.autoPlay(true)//true为自动轮播,时间间隔2秒
.interval(2000) //true为自动轮播,时间间隔2秒
.loop(true) //true为循环播放
.onChange((index: number) => {
console.error(`现在展示的是第${index}个页面`)
})
Row(){
Button('上一页').onClick(() => {
this.swiperController.showPrevious()
})
Button('下一页').onClick(() => {
this.swiperController.showNext()
})
}
}
}
}