目录
compose的图片查看器不少,telophoto功能完善,文档详细,支持image外的控件缩放.
它支持多平台.
打算从它的源码入手,然后作一个自定义的view.
项目模块:
include(":benchmark:runner")
include(":flick")
include(":zoomable")
include(":zoomable-image:core")
include(":zoomable-image:coil")
include(":zoomable-image:coil3")
include(":zoomable-image:glide")
include(":zoomable-image:sub-sampling-image")
include(":zoomable-peek-overlay")
include(":sample")
include(":test-util")
主要依赖
include(":zoomable")
include(":zoomable-image:core")
include(":zoomable-image:sub-sampling-image")
这三个是图片查看器的核心,第一个包含了缩放,不只支持image.
第二个包含对image的缩放.
第三个是处理超大图的分块解码功能.
按依赖顺序是zoomable为基础库.
sub-sampling-image依赖zoomable
core依赖它们两个,它即支持普通的放大缩小,又支持分块加载.
在docs里面有对应的模块的文档.index.md是安装使用.recipes.md是教程,涉及一些具体特性
zoomable
commonMain这是多平台共享代码,下面有两个包.一个是根目录,另一个是internal
ZoomableContentTransformation 这个类定义了平移,转换,旋转,缩放等属性,它是一个接口.具体实现是internal/RealZoomableContentTransformation
ZoomableState,它是state,的接口,具体实现是RealZoomableState,提供了rememberZoomableState方法.
sealed interface ZoomableState {
val contentTransformation: ZoomableContentTransformation
var autoApplyTransformations: Boolean
var contentScale: ContentScale
var contentAlignment: Alignment
val transformedContentBounds: Rect
val zoomFraction: Float?
/** The zoom spec passed to [rememberZoomableState]. */
val zoomSpec: ZoomSpec
/** Whether any zoom, pan (or both) animation is in progress. */
val isAnimationRunning: Boolean
/** See [ZoomableContentLocation]. */
fun setContentLocation(location: ZoomableContentLocation)
suspend fun resetZoom(animationSpec: AnimationSpec<Float> = DefaultZoomAnimationSpec)
suspend fun zoomBy(
zoomFactor: Float,
centroid: Offset = Offset.Unspecified,
animationSpec: AnimationSpec<Float> = DefaultZoomAnimationSpec,
)
suspend fun zoomTo(
zoomFactor: Float,
centroid: Offset = Offset.Unspecified,
animationSpec: AnimationSpec<Float> = DefaultZoomAnimationSpec,
)
suspend fun panBy(
offset: Offset,
animationSpec: AnimationSpec<Offset> = DefaultPanAnimationSpec,
)
@Deprecated(message = "Use resetZoom(AnimationSpec) instead")
suspend fun resetZoom(withAnimation: Boolean) {
if (withAnimation) {
resetZoom()
} else {
resetZoom(animationSpec = SnapSpec())
}
}
/** See [ZoomableContentLocation]. */
@Deprecated(
message = "Use setContentLocation() instead",
replaceWith = ReplaceWith("setContentLocation"),
level = DeprecationLevel.HIDDEN,
)
@Suppress("INAPPLICABLE_JVM_NAME") // https://youtrack.jetbrains.com/issue/KT-31420
@JvmName("setContentLocation")
suspend fun setContentLocationSuspending(location: ZoomableContentLocation) {
setContentLocation(location)
}
companion object {
val DefaultZoomAnimationSpec: AnimationSpec<Float> get() = spring(stiffness = Spring.StiffnessMediumLow)
val DefaultPanAnimationSpec: AnimationSpec<Offset> get() = spring(stiffness = Spring.StiffnessMediumLow)
val DefaultSettleAnimationSpec: AnimationSpec<Float> get() = spring()
}
}
ZoomableState包含的内容:
转换参数,动画规格,内容缩放类型,可见区域.定义了zoomTo,zoomBy,panBy等方法来处理转换.
ZoomableState最终作用于外部的view,把这些参数应用后对于view的graphicsLayer.所以它不局限于image,可以用于作用compose view.
zoomable
是一个自定义的 Modifier
扩展函数,用于为 Compose UI 提供缩放和手势处理的功能。
- 捏合缩放(Pinch to Zoom):允许用户通过双指捏合操作来放大或缩小内容。
- 双击缩放(Double Click to Zoom):通过双击屏幕实现快速缩放。
- 单指缩放(Single Finger Zoom):支持双击后按住并拖动进行更精细的缩放。
- 触觉反馈(Haptic Feedback):当缩放超出范围时,提供触觉反馈以提醒用户。
- 兼容嵌套滚动(Nested Scrolling Compatibility):确保与其他滚动组件协同工作。
- 点击事件监听(Click Listeners):支持点击、长按等交互事件。
ZoomableElement
是一个自定义的 ModifierNodeElement
,用于封装和管理缩放手势相关的逻辑。它的主要作用是:
- 创建手势节点 :通过
create()
方法生成一个ZoomableNode
实例,负责处理捏合缩放、双击缩放、点击等交互事件。 - 更新状态 :通过
update()
方法动态更新手势节点的状态,确保手势逻辑能够根据外部参数的变化实时调整。
ZoomableNode,由ZoomableElement创建.ZoomableElement
是一个关键的中间层,连接了 Modifier.zoomable()
和实际的手势处理逻辑。它通过封装 ZoomableNode
,实现了对捏合缩放、双击缩放、点击等交互事件的统一管理。
ZoomableElement的参数从外部的Zoomable传来,单击,双击,还有一个state.
ZoomableNode
是一个自定义的 DelegatingNode
,负责处理缩放手势的核心逻辑。它的主要作用是:
- 管理手势事件:捕获和处理捏合缩放、双击缩放、点击、长按等交互事件。
- 状态更新:根据用户操作实时更新缩放状态(如缩放比例、平移位置等)。
- 触觉反馈与动画:在特定情况下(如缩放超出范围)提供触觉反馈,并触发相应的动画效果。
单击,双击这些是往外传.
state
:缩放状态管理对象,存储当前的缩放、平移等信息
它的主要方法是update(),里面有一个TransformableElement对象
-
手势节点的组合
- TappableAndQuickZoomableNode:处理点击、长按、双击和快速缩放事件。
- TransformableNode:处理捏合缩放和平移操作。
- 这两个节点通过
delegate()
方法组合在一起,形成完整的手势处理链。
ZoomableNode
是 ZoomableElement
创建的核心手势节点,封装了所有与缩放相关的手势逻辑。它通过组合多个子节点(TappableAndQuickZoomableNode
和 TransformableElement),
Compose 中的节点委托模式,使得手势逻辑既模块化又易于扩展,一路委托过来真正提供这些平移,转换的是在TransformableElement**,它处理了手势,**RealZoomableState包含了状态变化.
于是,总结一下,zoomable是Modifiter的扩展,要实现手势管理,需要ModifierNodeElement的子类注册上.于是有ZoomableElement.而具体要处理手势,它又是通过ZoomableNode来管理.最终由
TransformableElement来处理手势,而RealZoomableState包含了这些状态的变化内容.它应用于view上.