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.
ZoomImage enables one finger drag gestures by default, but you can turn it off as follows:
val zoomState: ZoomState by rememberZoomState()
LaunchEffect(zoomState.zoomable) {
zoomState.zoomable.disabledGestureTypes =
zoomState.zoomable.disabledGestureTypes or GestureType.ONE_FINGER_DRAG
}
SketchZoomAsyncImage(
imageUri = "https://sample.com/sample.jpeg",
contentDescription = "view image",
modifier = Modifier.fillMaxSize(),
zoomState = zoomState,
)
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 rememberZoomState()
LaunchEffect(zoomState.zoomable) {
zoomState.zoomable.disabledGestureTypes =
zoomState.zoomable.disabledGestureTypes or GestureType.KEYBOARD_DRAG
}
SketchZoomAsyncImage(
imageUri = "https://sample.com/sample.jpeg",
contentDescription = "view image",
modifier = Modifier.fillMaxSize(),
zoomState = zoomState,
)
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 rememberZoomState()
SketchZoomAsyncImage(
imageUri = "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)")
}
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 rememberZoomState()
LaunchEffect(zoomState.zoomable) {
zoomState.zoomable.limitOffsetWithinBaseVisibleRect = true
}
SketchZoomAsyncImage(
imageUri = "https://sample.com/sample.jpeg",
contentDescription = "view image",
modifier = Modifier.fillMaxSize(),
zoomState = zoomState,
)
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 rememberZoomState()
LaunchEffect(zoomState.zoomable) {
// Set the specific size through the containerWhitespace property
zoomState.zoomable.containerWhitespace = ContainerWhitespace(
left = 4f, top = 3f, right = 2f, bottom = 1f
)
// or
zoomState.zoomable.containerWhitespace = ContainerWhitespace(horizontal = 2f, vertical = 1f)
// or
zoomState.zoomable.containerWhitespace = 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.containerWhitespaceMultiple = 0.5f
}
SketchZoomAsyncImage(
imageUri = "https://sample.com/sample.jpeg",
contentDescription = "view image",
modifier = Modifier.fillMaxSize(),
zoomState = zoomState,
)
// compose
val zoomState: ZoomState by rememberZoomState()
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
zoomable.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.scrollEdge: ScrollEdge
: Edge state for the current offset
- 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