Skip to content

Latest commit

 

History

History
558 lines (413 loc) · 21.1 KB

getting_started_zh.md

File metadata and controls

558 lines (413 loc) · 21.1 KB

开始使用

翻译:English

加载图片

Sketch 加载图片非常简单,如下:

Compose Multiplatform:

// val imageUri = "/Users/my/Downloads/image.jpg"
// val imageUri = file:///compose_resource/composeResources/com.github.panpf.sketch.sample.resources/files/sample.png
val imageUri = "https://example.com/image.jpg"

AsyncImage(
    uri = imageUri,
    contentDescription = "photo"
)

AsyncImage(
    uri = imageUri,
    state = rememberAsyncImageState(ComposableImageOptions {
        placeholder(Res.drawable.placeholder)
        error(Res.drawable.error)
        crossfade()
        // There is a lot more...
    }),
    contentDescription = "photo"
)

AsyncImage(
    rqeuest = ComposableImageRequest(imageUri) {
        placeholder(Res.drawable.placeholder)
        error(Res.drawable.error)
        crossfade()
        // There is a lot more...
    },
    contentDescription = "photo"
)

Image(
    painter = rememberAsyncImagePainter(
        request = ComposableImageRequest(imageUri) {
            placeholder(Res.drawable.placeholder)
            error(Res.drawable.error)
            crossfade()
            // There is a lot more...
        }
    ),
    contentDescription = "photo"
)

Tip

  1. 在 Compose Multiplatform 上你既可以直接使用 AsyncImage 组件也可以使用 Image + AsyncImagePainter 来加载图片。
  2. 但更推荐使用 AsyncImage 组件,因为 AsyncImage 会略快一些。
  3. 这是由于 Sketch 依赖组件的确切大小才会开始加载图片,AsyncImage 在布局阶段就可以获取到组件的大小,而 Image + AsyncImagePainter 则是要等到绘制阶段才能获取到组件大小。
  4. placeholder(Res.drawable.placeholder) 需要导入 sketch-compose-resources 模块

Android View:

// val imageUri = "/sdcard/download/image.jpg"
// val imageUri = "file:///android_asset/image.jpg"
// val imageUri = "content://media/external/images/media/88484"
val imageUri = "https://example.com/image.jpg"

imageView.loadImage(imageUri)

imageView.loadImage(imageUri) {
    placeholder(R.drawable.placeholder)
    error(R.drawable.error)
    crossfade()
    // There is a lot more...
}

val request = ImageRequest(context, imageUri) {
    placeholder(R.drawable.placeholder)
    error(R.drawable.error)
    crossfade()
    target(imageView)
    // There is a lot more...
}
context.sketch.enqueue(request)

Sketch 是智能的,它会自动根据组件的大小来调整图片的尺寸,防止加载到内存的图片的尺寸超出组件自身的大小造成内存浪费,还会在组件销毁时自动取消请求

支持的图片类型

Sketch 支持多种静态图片和动态图片类型,如下:

类型 依赖模块
jpeg _
png _
bmp _
webp _
heif _
avif _
svg sketch-svg
gif sketch-animated-gif
sketch-animated-gif-koral
webp 动图 sketch-animated-webp
heif 动图 sketch-animated-heif
视频帧 sketch-video
sketch-video-ffmpeg
Apk 图标 sketch-extensions-apkicon

每一种图片类型都有对应的 Decoder 对其提供支持,详细了解 Decoder

支持的 URI

Sketch 支持从网络、本机、资源等不同的数据源加载图片,如下:

URI 描述 创建函数 依赖模块
http://, https:// File in network _ sketch-http-hurl
sketch-http-okhttp
sketch-http-ktor2
sketch-http-ktor3
file://, / File in SDCard newFileUri() _
content:// Android Content Resolver _ _
file:///android_asset/ Android Asset newAssetUri() _
android.resource:// Android Resource newResourceUri() _
data:image/, data:img/ Base64 newBase64Uri() _
file:///compose_resource/ Compose Resource newComposeResourceUri() sketch-compose-resources
file:///kotlin_resource/ Kotlin Resource newKotlinResourceUri() _
app.icon:// Android App Icon newAppIconUri() sketch-extensions-appicon

每一种 URI 都有对应的 Fetcher 对其提供支持,详细了解 Fetcher

平台差异

由于受平台特性所限,在不同平台上的功能也有所不同,如下:

功能 Android iOS Desktop Web
jpeg
png
webp
bmp
heif ✅ (API 28)
avif ✅ (API 31)
svg
(不支持 CSS)

(不支持 CSS)

(不支持 CSS)
gif
webp 动图 ✅ (API 28)
heif 动图 ✅ (API 30)
视频帧
http://, https://
file://, /
file:///compose_resource/
data:image/, data:img/
file:///android_asset/
content://
android.resource://
file:///kotlin_resource/
Exif Orientation
内存缓存
结果缓存
下载缓存
默认图片解码器 BitmapFactory Skia Image Skia Image Skia Image
最低 API API 21 - JDK 1.8 -

最低 API 是 '-' 表示和 Compose Multiplatform 同步

Sketch

Sketch 类是整个框架的核心,它用来执行并管理 ImageRequest

单例模式

sketch-composesketch-view 模块依赖了 sketch-singleton 模块,因此直接依赖他们就可以使用单例模式

单例模式下不需要主动创建 Sketch 实例,你可以直接获取共享的 Sketch 实例,如下:

// Android
val sketch = context.sketch
val sketch = SingletonSketch.get(context)

// Non Android
val sketch = SingletonSketch.get()

需要自定义 Sketch 时可以通过以下方式创建 Sketch 并配置它:

// Android
class MyApplication : Application(), SingletonSketch.Factory {

    override fun createSketch(): Sketch {
        return Sketch.Builder(context).apply {
            logger(level = Logger.Level.Debug)
            httpStack(OkHttpStack.Builder().build())
            // There is a lot more...
        }.build()
    }
}

// Non Android
SingletonSketch.setSafe {
    Sketch.Builder(PlatformContext.INSTANCE).apply {
        logger(level = Logger.Level.Debug)
        httpStack(OkHttpStack.Builder().build())
        // There is a lot more...
    }.build()
}

Tip

使用 SingletonSketch.setSafe() 方式自定义 Sketch 时需要尽可能早的调用它,最好是在 App 的入口函数中

非单例模式

非单例模式下需要你自己创建 Sketch 并记住它,然后在需要的时候使用你创建的实例,如下:

val sketch = Sketch.Builder(context).apply {
    logger(level = Logger.Level.Debug)
    httpStack(OkHttpStack.Builder().build())
    // There is a lot more...
}.build()

val imageUri = "https://www.example.com/image.jpg"
val request = ImageRequest(context, imageUri)
GloablScope.launch {
    val imageResult: ImageResult = sketch.execute(request)
}

Tip

关于 Sketch 的更多自定义配置请参考 Sketch.Builder 类

ImageRequest

ImageRequest 用来描述一次图片加载请求,它包含图片的 uri 以及占位图、转换、过渡、新的尺寸、Target 、Listener 等配置

创建 ImageRequest

创建一个简单的 ImageRequest,它限制图片的最大像素数为 300x300

val request = ImageRequest(context, "https://www.example.com/image.jpg") {
    size(300, 300)
    // There is a lot more...
}

Tip

关于 ImageRequest 的更多配置请参考 ImageRequest.Builder 类

配置 Target

要想将结果直接加载到组件上还需要配置 Target

在 Compose 上 TargetAsyncImageAsyncImagePainter 的基石 AsyncImageState 来配置,你只需将 ImageRequest 交给 AsyncImageAsyncImagePainter 即可,如下:

val request = ImageRequest(context, "https://www.example.com/image.jpg") {
    size(300, 300)
    // There is a lot more...
}

AsyncImage(
    request = request,
    contentDescription = "photo"
)

Image(
    painter = rememberAsyncImagePainter(request),
    contentDescription = "photo"
)

Caution

AsyncImageAsyncImagePainter 中你不能调用 target() 函数,这会导致 App 崩溃

在 Android View 系统中则需要你主动调用 target() 函数传入 ImageView,如下:

val request = ImageRequest(context, "https://www.example.com/image.jpg") {
    size(300, 300)
    target(imageView)
    // There is a lot more...
}
context.sketch.enqueue(request)

你还可以使用 ImageRequest(ImageView, String)ImageView.loadImage() 扩展函数,它们会帮你调用 target(),如下:

val request = ImageRequest(imageView, "https://www.example.com/image.jpg") {
    size(300, 300)
    // There is a lot more...
}
context.sketch.enqueue(request)

imageView.loadImage() {
    size(300, 300)
    // There is a lot more...
}

执行 ImageRequest

ImageRequest 创建好后要交由 Sketch 去执行,Sketch 支持异步和同步两种方式执行 ImageRequest,如下:

val request = ImageRequest(context, "https://www.example.com/image.jpg")

// 异步执行 ImageRequest 不阻塞当前线程,也不会挂起当前协程
val disposable: Disposable = sketch.enqueue(request)

// 同步执行 ImageRequest 挂起当前协程直到返回结果
coroutineScope.launch(Dispatchers.Main) {
    val imageResult: ImageResult = sketch.execute(request)
    val image: Image = imageResult.image
}

Note

单例模式为 ImageRequest 提供了 ImageRequest.enqueue() 和 ImageRequest.execute() 扩展函数,方便顺序书写

获取结果

配置了 TargetSketch 会将结果交给 Target 去显示,但有时候需要通过结果做一些事情或者没有配置 Target 时就需要主动获取结果,如下:

val request = ImageRequest(context, "https://www.example.com/image.jpg")

// 使用 enqueue() 方法异步执行请求时可以通过返回的 Disposable.job 获取结果
val disposable = sketch.enqueue(request)
coroutineScope.launch(Dispatchers.Main) {
    val imageResult: ImageResult = disposable.job.await()
}

// 使用 execute() 方法同步执行请求时可以直接获取结果
coroutineScope.launch(Dispatchers.Main) {
    val imageResult: ImageResult = sketch.execute(request)
}

ImageResult 包含了很多有用的信息,如下:

val imageResult: ImageResult = ...
val request: ImageRequest = imageResult.request
val image: Image = imageResult.image
when (image) {
    is BitmapImage -> {
        val bitmap: Bitmap = image.bitmap
    }
    is DrawableImage -> {
        val drawable: Drawable = image.drawable
    }
    is PainterImage -> {
        val painter: Painter = image.painter
    }
    is AnimatedImage -> {
        val codec: Codec = image.codec
    }
}
if (imageResult is ImageResult.Success) {
    val cacheKey: String = imageResult.cacheKey
    val imageInfo: ImageInfo = imageResult.imageInfo
    val dataFrom: DataFrom = imageResult.dataFrom
    val resize: Resize = imageResult.resize
    val transformeds: List<String>? = imageResult.transformeds
    val extras: Map<String, String>? = imageResult.extras
} else if (imageResult is ImageResult.Error) {
    val throwable: Throwable = imageResult.throwable
}

取消请求

配置了 TargetImageRequest 会在下列情况下自动取消请求:

  • AsyncImageAsyncImagePainter 组件被忘记
  • ImageView 的 onViewDetachedFromWindow() 方法被执行
  • Lifecycle 变为 DESTROYED 状态

未配置 Target 或需要主动取消时可以通过 Disposable 或 Job 来取消,如下:

// 使用 enqueue() 方法异步执行请求时会返回一个 Disposable, 可以用来它在需要的时候取消请求
val request = ImageRequest(context, "https://www.example.com/image.jpg")
val disposable = sketch.enqueue(request)
disposable.dispose()

// 使用 execute() 方法同步执行请求时可以通过其协程的 Job 在需要的时候取消请求
val job = coroutineScope.launch(Dispatchers.Main) {
    val request = ImageRequest(context, "https://www.example.com/image.jpg")
    val imageResult: ImageResult = sketch.execute(request)
}
job.cancel()

ImageView 扩展

Sketch 为 ImageView 提供了一系列的扩展,如下:

// load
imageView.loadImage("https://www.example.com/image.jpg") {
    placeholder(R.drawable.placeholder)
    error(R.drawable.error)
    crossfade(true)
}

// cancel
imageView.disposeLoad()

// result
val imageResult: ImageResult? = imageView.imageResult

loadImage() 仅单例模式下可用

文档

基础功能:

特色功能: