实现思路
使用绝对定位来控制视图的显示位置,添加拖动手势,在拖动手势对应的事件里去改变视图的位置
封装一个吸边可拖拽的视图,需要考虑以下几点
- 需要使用方来定义拖动视图的初始位置
- 需要使用方来定义拖动视图的内容
- 需要响应视图的拖动事件,并移动视图的位置
- 在拖动事件结束之后,需要根据视图当前的位置,去判断吸附到屏幕的哪个边缘,并且以动画的形式滚动到屏幕边缘
1. 需要使用方来定义拖动视图的初始位置
可以使用鸿蒙状态管理V2里的@Param @Once 来定义视图的初始位置,Param @Once 可以实现仅从外部初始化一次、不接受后续同步变化的能力@Param @Once bottom:number = 20; @Param @Once right:number = 0
2. 需要使用方来定义拖动视图的内容
使用@BuilderParam可以从使用方传过来一个自定义的builder,这样使用方可以自定义视图内容
typescript
@BuilderParam
defaultBuilder: ()=>void
3. 需要响应视图的拖动事件,并移动视图的位置
给视图添加拖动手势,在手势开始时,记录当前的位置,在手势更新时,根据偏移量和开始时的位置,更新视图的位置
4. 在拖动事件结束之后,需要根据视图当前的位置,去判断吸附到屏幕的哪个边缘,并且以动画的形式滚动到屏幕边缘
当视图中心点小于屏幕中心点的时候,需要移动到屏幕左侧,否则移动到屏幕右侧,因此我们需要获取屏幕的宽度以及视图的宽度, 通过display.getDefaultDisplaySync() 获取屏幕信息(获取到的信息是px,需要转换成vp),通过onSizeChange获取视图信息,视图中心点等于屏幕宽度 - right - 视图宽度除以2,最后通过Animator来实现动画
完整代码
kotlin
import { display } from "@kit.ArkUI"
import { AnimatorResult } from '@kit.ArkUI';
@ComponentV2
export struct DragView {
@BuilderParam
defaultBuilder: ()=>void
@Param @Once bottom:number = 20
@Param @Once right:number = 0
private sizeWidth: number = 0
private startBottom: number = 0
private startRight: number = 0
private screenWidth: number = 0
private fadeAnimator: AnimatorResult | undefined = undefined
aboutToAppear(): void {
//获取屏幕宽度,并转换成VP
const displayInfo = display.getDefaultDisplaySync()
this.screenWidth = px2vp(displayInfo.width)
}
aboutToDisappear(): void {
this.fadeAnimator = undefined
}
createFadeAnim(begin: number, end: number) {
// 创建动画,在动画的onFrame里去控制right的值
this.fadeAnimator = this.getUIContext().createAnimator({
duration: 300,
easing:'ease',
delay:0,
fill: "forwards",
direction: "normal",
iterations: 1,
begin,
end
})
this.fadeAnimator.onFrame = (value: number) => {
this.right = value
}
this.fadeAnimator.play()
}
build() {
Row(){
this.defaultBuilder()
}
.position({bottom:this.bottom, right:this.right})
// 获取视图的大小
.onSizeChange((oldValue, newValue)=>{
this.sizeWidth = newValue.width as number
})
//添加手势
.gesture(
PanGesture()
//在手势开始时,取消动画,并记录手势开始时,视图当前位置
.onActionStart((event: GestureEvent) => {
this.fadeAnimator?.cancel()
this.startBottom = this.bottom
this.startRight = this.right
})
// 手势更新时,修改bottom和right的值
// offsetY向上位负,offsetX向左位负数
.onActionUpdate((event: GestureEvent) => {
if (event) {
this.bottom = this.startBottom - event.offsetY
this.right = this.startRight - event.offsetX
}
})
//手势结束回调
.onActionEnd((event: GestureEvent) => {
// 屏幕中心点
const screenCenter = this.screenWidth / 2
// 视图中心点
const selfCenter = this.screenWidth - this.right - this.sizeWidth / 2
//最终right的值,屏幕右侧值为0,屏幕左侧值为屏幕宽度减去视图宽度
let end = selfCenter < screenCenter ? this.screenWidth - this.sizeWidth : 0;
//创建动画,当前right为起始点,end为动画结束点
this.createFadeAnim(this.right, end)
}))
}
}