offset.zh.md
August 8, 2025 · View on GitHub
偏移
翻译:English
Tip
- 以下示例优先用 Compose 版本的组件来演示
- ZoomState.zoomable 等价于 ZoomImageView.zoomable
- ZoomState.subsampling 等价于 ZoomImageView.subsampling
ZoomImage 支持单指拖动、惯性滑动、键盘拖动,以及 offset() 方法来移动图像。
单指拖动
ZoomImage 默认开启单指拖动手势,但你可以关闭它,如下:
val zoomState: ZoomState by rememberSketchZoomState()
zoomState.zoomable.setDisabledGestureTypes(
zoomState.zoomable.disabledGestureTypes or GestureType.ONE_FINGER_DRAG
)
SketchZoomAsyncImage(
uri = "https://sample.com/sample.jpeg",
contentDescription = "view image",
modifier = Modifier.fillMaxSize(),
zoomState = zoomState,
)
键盘拖动
ZoomImage 支持通过键盘拖动图像,支持短按和长按两种操作。默认注册了以下按键:
- move up: Key.DirectionUp
- move down: Key.DirectionDown
- move left: Key.DirectionLeft
- move right: Key.DirectionRight
由于键盘拖动功能必须依赖焦点,而焦点管理又非常复杂,所以默认没有开启它,需要你主动配置并请求焦点,如下:
val focusRequester = remember { FocusRequester() }
val zoomState = rememberSketchZoomState()
SketchZoomAsyncImage(
uri = "https://sample.com/sample.jpeg",
contentDescription = "view image",
zoomState = zoomState,
modifier = Modifier.fillMaxSize()
.focusRequester(focusRequester)
.focusable()
.keyZoom(zoomState.zoomable),
)
LaunchedEffect(Unit) {
focusRequester.requestFocus()
}
Tip
在 HorizontalPager 中请求焦点时需要注意只能为当前页请求焦点,否则会导致意想不到的意外
你还可以通过手势控制动态关闭它,如下:
val zoomState: ZoomState by rememberSketchZoomState()
zoomState.zoomable.setDisabledGestureTypes(
zoomState.zoomable.disabledGestureTypes or GestureType.KEYBOARD_DRAG
)
SketchZoomAsyncImage(
uri = "https://sample.com/sample.jpeg",
contentDescription = "view image",
modifier = Modifier.fillMaxSize(),
zoomState = zoomState,
)
offset()
ZoomImage 提供了 offset() 方法用来移动图像到指定位置,它有两个参数:
targetOffset: Offset: 目标偏移位置,offset 原点是组件的左上角animated: Boolean = false: 是否使用动画,默认为 false
示例:
val zoomState: ZoomState by rememberSketchZoomState()
SketchZoomAsyncImage(
uri = "https://sample.com/sample.jpeg",
contentDescription = "view image",
modifier = Modifier.fillMaxSize(),
zoomState = zoomState,
)
val coroutineScope = rememberCoroutineScope()
Button(
onClick = {
coroutineScope.launch {
val targetOffset = zoomState.zoomable.transform.offset + Offset(x = 100, y = 200)
zoomState.zoomable.offset(targetOffset = targetOffset, animated = true)
}
}
) {
Text(text = "offset + Offset(100, 200)")
}
Button(
onClick = {
coroutineScope.launch {
val targetOffset = zoomState.zoomable.transform.offset - Offset(x = 100, y = 200)
zoomState.zoomable.offset(targetOffset = targetOffset, animated = true)
}
}
) {
Text(text = "offset - Offset(100, 200)")
}
限制偏移边界
ZoomImage 默认不管你设置的是什么 ContentScale 都可以拖动查看图像的全部内容,例如你设置了 ContentScale 为 Crop,Alignment 为 Center,那么默认只显示图像中间的部分,然后你还可以单指或双指拖动来查看图像的全部内容
如果你希望图像只能在 ContentScale 和 Alignment 所限制的区域内移动,不能查看全部内容,这时你可以修改
limitOffsetWithinBaseVisibleRect 参数为 true 来达到此目的
示例:
val zoomState: ZoomState by rememberSketchZoomState()
zoomState.zoomable.setLimitOffsetWithinBaseVisibleRect(true)
SketchZoomAsyncImage(
uri = "https://sample.com/sample.jpeg",
contentDescription = "view image",
modifier = Modifier.fillMaxSize(),
zoomState = zoomState,
)
容器空白区域
ZoomImage 默认在拖动图像时图像的边缘始终和容器的边缘对齐,它们中间不会存在空白区域(图像初始状态时除外),当你需要在图像和容器之间留有空白区域时,你可以通过设置
containerWhitespace 或 containerWhitespaceMultiple 参数为来达到此目的
示例:
val zoomState: ZoomState by rememberSketchZoomState()
// 通过 containerWhitespace 属性设置具体的大小
zoomState.zoomable.setContainerWhitespace(
ContainerWhitespace(left = 4f, top = 3f, right = 2f, bottom = 1f)
)
// 或
zoomState.zoomable.setContainerWhitespace(ContainerWhitespace(horizontal = 2f, vertical = 1f))
// 或
zoomState.zoomable.setContainerWhitespace(ContainerWhitespace(size = 1f))
// 在图像边缘和容器边缘之间留有 50% 容器大小的空白区域
zoomState.zoomable.setContainerWhitespaceMultiple(0.5f)
SketchZoomAsyncImage(
uri = "https://sample.com/sample.jpeg",
contentDescription = "view image",
modifier = Modifier.fillMaxSize(),
zoomState = zoomState,
)
可访问属性
// compose
val zoomState: ZoomState by rememberSketchZoomState()
SketchZoomAsyncImage(zoomState = zoomState)
val zoomable: ZoomableState = zoomState.zoomable
// view
val sketchZoomImageView = SketchZoomImageView(context)
val zoomable: ZoomableEngine = sketchZoomImageView.zoomable
Tip
- 注意:view 版本的相关属性用 StateFlow 包装,所以其名字相比 compose 版本都以 State 为后缀
只读属性:
zoomable.alignment: Alignment: content 在 container 中的对齐方式,默认是 Alignment.TopStartzoomable.limitOffsetWithinBaseVisibleRect: Boolean: 是否将偏移限制在 contentBaseVisibleRect 内,默认是 falsezoomable.containerWhitespaceMultiple: Float: 基于容器尺寸的倍数为容器四周添加空白区域,默认是 0fzoomable.containerWhitespace: ContainerWhitespace: 容器四周空白区域的配置,优先级高于 containerWhitespaceMultiple,默认是 ContainerWhitespace.Zerozoomable.disabledGestureTypes: Int: 配置禁用的手势类型,默认是 0(不禁用任何手势),可以使用 GestureType 的位或操作来组合多个手势类型zoomable.transform.offset: Offset: 当前偏移量(baseTransform.offset + userTransform.offset)zoomable.baseTransform.offset: Offset: 当前基础偏移量,受 alignment 参数和 rotate 方法影响zoomable.userTransform.offset: Offset: 当前用户偏移量,受 offset()、locate() 以及用户手势拖动影响zoomable.contentBaseDisplayRectF: Rect: content 经过 baseTransform 变换后在 container 中的区域zoomable.contentBaseDisplayRect: IntRect: content 经过 baseTransform 变换后在 container 中的区域zoomable.contentBaseVisibleRectF: Rect: content 经过 baseTransform 变换后自身对用户可见的区域zoomable.contentBaseVisibleRect: IntRect: content 经过 baseTransform 变换后自身对用户可见的区域zoomable.contentDisplayRectF: Rect: content 经过 transform 变换后在 container 中的区域zoomable.contentDisplayRect: IntRect: content 经过 transform 变换后在 container 中的区域zoomable.contentVisibleRectF: Rect: content 经过 transform 变换后自身对用户可见的区域zoomable.contentVisibleRect: IntRect: content 经过 transform 变换后自身对用户可见的区域zoomable.sourceVisibleRectF: Rect: contentVisibleRect 映射到原图上的区域zoomable.sourceVisibleRect: IntRect: contentVisibleRect 映射到原图上的区域zoomable.scrollEdge: ScrollEdge: 当前偏移的边界状态
交互方法:
zoomable.setLimitOffsetWithinBaseVisibleRect(Boolean): 设置是否将偏移限制在 contentBaseVisibleRect 内zoomable.setContainerWhitespaceMultiple(Float): 设置基于容器尺寸的倍数为容器四周添加空白区域zoomable.setContainerWhitespace(ContainerWhitespace): 设置容器四周空白区域的配置,优先级高于 containerWhitespaceMultiplezoomable.setDisabledGestureTypes(Int): 设置禁用的手势类型,可以使用 GestureType 的位或操作来组合多个手势类型zoomable.offset(): 偏移 content 到指定的位置zoomable.offsetBy(): 以增量的方式偏移 content 指定的偏移量zoomable.touchPointToContentPoint(): IntOffset: 将触摸点转换为 content 上的点,原点是 content 的左上角zoomable.touchPointToContentPointF(): Offset: 将触摸点转换为 content 上的点,原点是 content 的左上角zoomable.sourceToDraw(Offset): Offset: 将原图上的点转换为绘制时的点,原点是 container 的左上角zoomable.sourceToDraw(Rect): Rect: 将原图上的矩形转换为绘制时的矩形,原点是 container 的左上角zoomable.canScroll(): Boolean: 判断当前 content 在指定方向上是否可以滚动
监听属性变化
- compose 版本的相关属性是用 State 包装的,在 Composable 函数中直接读取它即可实现监听
- view 的相关属性是用 StateFlow 包装,调用其 collect 函数即可实现监听