一、前言:
笔者在前面第六、七节文章当中,分别指出了音量和屏幕亮度的前置知识,在本节当中,我们将一并实现这两个功能,从而接续第五节内容。本文的逻辑分三大部分,先说用到的变量,再说界面,最后说功能。
其中音量功能,是设置的系统音量,而不是视频音量;其中亮度功能,是设置的应用亮度,而非系统亮度,请注意两者的区别。
亮度功能使用brightness库获取系统亮度值,在初始化亮度值,然后根据触摸情况通过window库设置亮度值
音量功能通过audio库,初始化音量值,根据触摸情况设置音量值
文中代码均是ArkTS代码,使用TS标志,是为了有颜色,好看,嘿嘿!
以下是效果图,即我手指左边垂直向下滑动时,出现亮度调节图标和进度条。
以下是效果图,即我手指右边垂直向下滑动时,出现音量调节图标和进度条。
二、状态变量:
// 以下代码均放在struct yourPageName{}结构体内,但在build(){}函数之外
// 控制图标和进度条是否显示
@State volumeVisible:boolean = false;
@State brightVisible: boolean = false;
// 音量和亮度值
@State volume:number = 0;
@State bright:number = 50;
// 手指触点横坐标位置
@State fingerPosition:number = 0;
// 屏幕一半宽度,我这里是500px
@State halfDeviceWidth:number = 500;
// 根据全局变量获得窗口控制器 ,这里需要提前在EntryAbility中设置全局变量,并把windowStage放进去,代码附下
windowStage: window.WindowStage = AppStorage.get('windowStage') as window.WindowStage;
// 获取主窗口的方式
mainWin: window.Window = this.windowStage.getMainWindowSync();
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import hilog from '@ohos.hilog';
import UIAbility from '@ohos.app.ability.UIAbility';
import Want from '@ohos.app.ability.Want';
import window from '@ohos.window';
import { BusinessError } from '@kit.BasicServicesKit';
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
}
onDestroy(): void {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
}
onWindowStageCreate(windowStage: window.WindowStage): void {
// Main window is created, set main page for this ability
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
console.log('onWindowStageCreate');
// 这里设置全局变量,把我们需要的窗体控制器放进去
AppStorage.setOrCreate('windowStage',windowStage);
windowStage.loadContent('pages/Index', (err) => {
if (err.code) {
hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
return;
}
hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.');
});
}
onWindowStageDestroy(): void {
// Main window is destroyed, release UI related resources
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
}
onForeground(): void {
// Ability has brought to foreground
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
}
onBackground(): void {
// Ability has back to background
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
}
}
三、界面逻辑:**
这里使用了Stack组件,按照栈概念,可以将图片和进度条在同一时刻一起显示。可以看到笔者这里设置了if语句中的三个变量,分别判断触摸点是否移动,触摸点在屏幕左边还是右边,是否显示。
Stack(){
// 亮度调节UI
if (this.fingerPosition != 0 && this.fingerPosition < this.halfDeviceWidth && this.brightVisible){
Image($r('app.media.ic_bright_white'))
.width(50)
.aspectRatio(1.0)
Progress({value:this.bright,total:100,type:ProgressType.Ring})
.color('#ffffffff')
.width(100)
.aspectRatio(1.0)
// 音量调节UI
}else if (this.fingerPosition != 0 && this.fingerPosition > this.halfDeviceWidth && this.volumeVisible){
Image($r('app.media.ic_volume_white'))
.width(50)
.aspectRatio(1.0)
Progress({value:this.volume,total: 20,type:ProgressType.Ring})
.color('#ffffffff')
.width(100)
.aspectRatio(1.0)
}
} // Stack组件可以自行调节,这段代码也可以嵌入进其他代码中,里面的图片随便找一个就好了
.width('100%')
.height('40%')
.backgroundColor(Color.Black)
四、功能逻辑:
我们需要在页面渲染之前完成一些初始化的工作,包括主窗体的获取,音量的初始化,亮度的初始化。
当然,也可以直接在第一部分定义变量的时候直接赋值,但直接赋值的弊端就是,以音量调节为例,进度条每次从设置的音量值开始变化,而不是从系统本来的值开始变化。笔者这里的初始化代码附下
// 此函数是生命周期函数,在struct yourPageName(){}中,build(){}之外
aboutToAppear() {
// 初始化音量
audioManager.getVolume(audio.AudioVolumeType.MEDIA, (err: BusinessError, value: number) => {
if (err) {
console.error(`Failed to obtain the volume. ${err}`);
return;
}
this.volume = value;
console.info('Callback invoked to indicate that the volume is obtained:'+value);
});
//
// 音量初始化,这里代码笔者注释了,选择在定义变量的时候直接赋值0.5
// brightness.getValue({
// success: (data: BrightnessResponse) => {
// console.log('success get brightness value:' + data.value);
// // brightness库中获取的亮度值介于0到255,而window库中设置的亮度值介于0到1,这里在做转换
// this.bright = ((data.value/255)*10)%10
// try {
// this.mainWin.setWindowBrightness(this.bright, (err) => {
// if (err.code) {
// console.error('Failed to set the brightness. Cause: ' + JSON.stringify(err));
// return;
// }
// console.info('Succeeded in setting the brightness.');
// });
// } catch (exception) {
// console.error('Failed to set the brightness. Cause: ' + JSON.stringify(exception));
// }
// },
// fail: (data: string, code: number) => {
// console.error('get brightness fail, code: ' + code + ', data: ' + data);
// }
// });
// 初始化主窗体
this.windowStage = AppStorage.get('windowStage') as window.WindowStage;
// 获取主窗口的方式
this.mainWin = this.windowStage.getMainWindowSync();
// 获取最上层窗口的方式
// window.getLastWindow(getContext(this));
}
为上面的Stack组件添加触摸事件,具体功能见代码注释,这里面的try-catch语句主要方便调试,代码如果可以正常运行的话,直接去掉try-catch语句和日志打印,那么代码可以很简介。里面最核心的函数是if语句。
Stack(){
//代码上面有,此处省略
}
.gesture(
GestureGroup(GestureMode.Exclusive,
// 添加触摸手势,并通过direction控制手势滑动方向为上下滑动
PanGesture({direction:PanDirection.Vertical})
.onActionStart((event: GestureEvent) => {
// 获取屏幕宽度和高度,便于后面的手势操作
if (event.fingerList[0].localX < this.halfDeviceWidth) {
this.brightVisible = true;
} else {
this.volumeVisible = true;
}
})
.onActionEnd(() => {
// 当手指离开屏幕时,清除currentIcon状态和关闭图标/进度条
this.volumeVisible = false;
this.brightVisible = false;
})
.onActionUpdate((event?:GestureEvent| undefined)=>{
// 取得系统亮度,为this.bright赋初值
if (!event) return; // 如果 event 是 undefined,直接返回,不执行后续代码
// 通过event.fingerList[0].localX获取触摸点的横坐标
this.fingerPosition = event.fingerList[0].localX
// 向上滑动
if (event.offsetY < 0){
// 触摸点在屏幕右侧
if (this.volume < 20 && this.fingerPosition > this.halfDeviceWidth){
// 音量值增加
this.volume += 0.2
audioManager.setVolume(audio.AudioVolumeType.MEDIA, this.volume, (err: BusinessError) => {
if (err) {
console.error(`Failed to set the volume. ${err}`);
return;
}
console.info('Callback invoked to indicate a successful volume setting.');
});
}
// 触摸点在屏幕左侧
if (this.bright < 100 && this.fingerPosition < this.halfDeviceWidth){
// 亮度值增加
this.bright += 1
try {
this.mainWin.setWindowBrightness(this.bright / 100 , (err) => {
if (err.code) {
console.error('Failed to set the brightness. Cause: ' + JSON.stringify(err));
return;
}
console.info('Succeeded in setting the brightness.');
});
} catch (exception) {
console.error('Failed to set the brightness. Cause: ' + JSON.stringify(exception));
}
} // 向下滑动
}else{
// 触摸点在屏幕右侧
if (this.volume > 0 && this.fingerPosition > this.halfDeviceWidth){
// 音量值减小
this.volume -= 0.2
audioManager.setVolume(audio.AudioVolumeType.MEDIA, this.volume, (err: BusinessError) => {
if (err) {
console.error(`Failed to set the volume. ${err}`);
return;
}
console.info('Callback invoked to indicate a successful volume setting.');
});
}
// 触摸点在屏幕左侧
if (this.bright > 0 && this.fingerPosition < this.halfDeviceWidth){
// 亮度值减小
this.bright -= 1
try {
this.mainWin.setWindowBrightness(this.bright / 100 , (err) => {
if (err.code) {
console.error('Failed to set the brightness. Cause: ' + JSON.stringify(err));
return;
}
console.info('Succeeded in setting the brightness.');
});
} catch (exception) {
console.error('Failed to set the brightness. Cause: ' + JSON.stringify(exception));
}
}
}
}),
)
)