offset.md
August 8, 2025 · View on GitHub
Offset
Translations: 简体中文
Tip
- The following example takes precedence over the Compose version component for demonstration
- ZoomState.zoomable is equivalent to ZoomImageView.zoomable
- ZoomState.subsampling is equivalent to ZoomImageView.subsampling
ZoomImage supports one-finger drag, inertial swipe, keyboard drag, and the offset() method to
move the image.
One Finger Drag
ZoomImage enables one finger drag gestures by default, but you can turn it off as follows:
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,
)
Keyboard drag
ZoomImage supports drag images through the keyboard, supports both short press and long press operations. And the following keys are registered by default:
- move up: Key.DirectionUp
- move down: Key.DirectionDown
- move left: Key.DirectionLeft
- move right: Key.DirectionRight
Since the keyboard drag function must rely on focus, and focus management is very complex, it is not enabled by default. You need to actively configure and request focus, as follows:
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
When requesting focus in HorizontalPager, you need to note that you can only request focus for the current page, otherwise it will cause unexpected accidents.
You can also turn it off dynamically via gesture control, as follows:
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 provides a modified offset() method to move the image to a specified position, which has
two parameters:
targetOffset: Offset: The target offset, with the offset origin being the upper-left corner of the componentanimated: Boolean = false: Whether to use animation, the default is false
Example:
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)")
}
Limit the bounds of offset
By default, zoomImage can drag to view the entire content of the image regardless of what you set ContentScale, for example, if you set ContentScale to Crop and Alignment to Center, then only the middle part of the image is displayed by default, and then you can also drag with one or two fingers to view the entire content of the image
If you want the image to be moved only within the area restricted
by ContentScale and Alignment, and not the entire content, you can modify the
limitOffsetWithinBaseVisibleRect parameter to true to achieve this
Example:
val zoomState: ZoomState by rememberSketchZoomState()
zoomState.zoomable.setLimitOffsetWithinBaseVisibleRect(true)
SketchZoomAsyncImage(
uri = "https://sample.com/sample.jpeg",
contentDescription = "view image",
modifier = Modifier.fillMaxSize(),
zoomState = zoomState,
)
Container Whitespace
By default, ZoomImage always aligns the edge of the image with the edge of the container when
dragging the image, and there will be no white space between them (except in the initial state of
the image). When you need to leave a white space between the image and the container, you can pass
To achieve this, set the containerWhitespace or containerWhitespaceMultiple parameter to
Example:
val zoomState: ZoomState by rememberSketchZoomState()
// Set the specific size through the containerWhitespace property
zoomState.zoomable.setContainerWhitespace(
ContainerWhitespace(left = 4f, top = 3f, right = 2f, bottom = 1f)
)
// or
zoomState.zoomable.setContainerWhitespace(ContainerWhitespace(horizontal = 2f, vertical = 1f))
// or
zoomState.zoomable.setContainerWhitespace(ContainerWhitespace(size = 1f))
// Leave 50% of the container size white space between the edge of the image and the edge of the container
zoomState.zoomable.setContainerWhitespaceMultiple(0.5f)
SketchZoomAsyncImage(
uri = "https://sample.com/sample.jpeg",
contentDescription = "view image",
modifier = Modifier.fillMaxSize(),
zoomState = zoomState,
)
Public Properties
// 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
Note: The relevant properties of the view version are wrapped in StateFlow, so its name is suffixed with State compared to the compose version
Readable properties:
zoomable.alignment: Alignment: The alignment of content in container is Alignment.TopStart by defaultzoomable.limitOffsetWithinBaseVisibleRect: Boolean: Whether to limit the offset to contentBaseVisibleRect, the default is falsezoomable.containerWhitespaceMultiple: Float: Add blank space around the container based on multiples of the container size, the default is 0fzoomable.containerWhitespace: ContainerWhitespace: The configuration of blank areas around the container has higher priority than containerWhitespaceMultiple, and the default is ContainerWhitespace.Zerozoomable.disabledGestureTypes: Int: Configure the disabled gesture type, the default is 0 (no gesture is disabled), and multiple gesture types can be combined using the bits or actions of GestureTypezoomable.transform.offset: Offset: Current offset (baseTransform.offset + userTransform.offset)zoomable.baseTransform.offset: Offset: The current base offset, affected by the alignment parameter and the rotate methodzoomable.userTransform.offset: Offset: The current user offset, affected by offset(), locate(), and user gesture draggingzoomable.contentBaseDisplayRectF: Rect: The content region in the container after the baseTransform transformationzoomable.contentBaseDisplayRect: IntRect: The content region in the container after the baseTransform transformationzoomable.contentBaseVisibleRectF: Rect: The content is visible region to the user after the baseTransform transformationzoomable.contentBaseVisibleRect: IntRect: The content is visible region to the user after the baseTransform transformationzoomable.contentDisplayRectF: Rect: The content region in the container after the final transform transformationzoomable.contentDisplayRect: IntRect: The content region in the container after the final transform transformationzoomable.contentVisibleRectF: Rect: The content is visible region to the user after the final transform transformationzoomable.contentVisibleRect: IntRect: The content is visible region to the user after the final transform transformationzoomable.sourceVisibleRectF: Rect: contentVisibleRect maps to the area on the original imagezoomable.sourceVisibleRect: IntRect: contentVisibleRect maps to the area on the original imagezoomable.scrollEdge: ScrollEdge: Edge state for the current offset
Interactive methods:
zoomable.setLimitOffsetWithinBaseVisibleRect(Boolean): Set whether to limit offsets to contentBaseVisibleRectzoomable.setContainerWhitespaceMultiple(Float): Set multiples based on container size to add blank areas around the containerzoomable.setContainerWhitespace(ContainerWhitespace): Set the configuration of blank areas around the container, with priority higher than containerWhitespaceMultiplezoomable.setDisabledGestureTypes(Int): Set the disabled gesture type, you can use the bits or actions of GestureType to combine multiple gesture typeszoomable.offset(): Offset content to the specified locationzoomable.offsetBy(): Offset as incremental content specified offsetzoomable.touchPointToContentPoint(): IntOffset: Convert the touch point to a point on the content, the origin is the upper left corner of the contentzoomable.touchPointToContentPointF(): Offset: Convert the touch point to a point on the content, the origin is the upper left corner of the contentzoomable.sourceToDraw(Offset): Offset: Convert the points on the original image to the points at the time of drawing, the origin is the upper left corner of the containerzoomable.sourceToDraw(Rect): Rect: Convert the rectangle on the original image to the rectangle when drawing, the origin is the upper left corner of the containerzoomable.canScroll(): Boolean: Determine whether the current content can scroll in the specified direction
Listen property changed
- The relevant properties of the compose version are wrapped in State and can be read directly in the Composable function to implement listening
- The relevant properties of the view are wrapped in StateFlow, and its collect function can be called to implement the listening