Translations: 简体中文
Loading images with Sketch is very simple, as follows:
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
- On Compose Multiplatform you can use AsyncImage directly Components can also
use
Image + AsyncImagePainter
to load the image. - But it is more recommended to use the AsyncImage component because AsyncImage is slightly faster.
- This is because Sketch relies on the exact size of the component to start loading images,
AsyncImage The size of the component can be obtained during the layout stage,
while
Image + AsyncImagePainter
cannot obtain the component size until the drawing stage. placeholder(Res.drawable.placeholder)
needs to import thesketch-compose-resources
module
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 is smart. It will automatically adjust the size of the image according to the size of the component to prevent the size of the image loaded into the memory from exceeding the size of the component itself and cause memory waste. It will also automatically cancel the request when the component is destroyed.
Sketch supports a variety of static and dynamic image types, as follows:
Format | Dependent Modules |
---|---|
jpeg | _ |
png | _ |
bmp | _ |
webp | _ |
heif | _ |
avif | _ |
svg | sketch-svg |
gif | sketch-animated-gif sketch-animated-gif-koral |
Animated webp | sketch-animated-webp |
Animated heif | sketch-animated-heif |
Video frames | sketch-video sketch-video-ffmpeg |
Apk icon | sketch-extensions-apkicon |
Each image type has a corresponding Decoder support for it, Learn more about Decoder
Sketch supports loading images from different data sources such as the network, local machine, and resources, as follows:
URI | Describe | Create Function | Dependent Modules |
---|---|---|---|
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 |
Each URI has its own Fetcher to support it, Learn more about Fetcher
Due to limitations of platform characteristics, the functions on different platforms are also different, as follows:
Feature | Android | iOS | Desktop | Web |
---|---|---|---|---|
jpeg png webp bmp |
✅ | ✅ | ✅ | ✅ |
heif | ✅ (API 28) | ❌ | ❌ | ❌ |
avif | ✅ (API 31) | ❌ | ❌ | ❌ |
svg | ✅ | ✅ (Not Support CSS) |
✅ (Not Support CSS) |
✅ (Not Support CSS) |
gif | ✅ | ✅ | ✅ | ✅ |
Animated webp | ✅ (API 28) | ✅ | ✅ | ✅ |
Animated heif | ✅ (API 30) | ❌ | ❌ | ❌ |
Video frames | ✅ | ❌ | ❌ | ❌ |
http:// https:// file://, / file:///compose_resource/ data:image/jpeg;base64 |
✅ | ✅ | ✅ | ✅ |
file:///android_asset/ content:// android.resource:// |
✅ | ❌ | ❌ | ❌ |
file:///kotlin_resource/ | ❌ | ✅ | ✅ | ❌ |
Exif Orientation | ✅ | ✅ | ✅ | ✅ |
Memory Cache | ✅ | ✅ | ✅ | ✅ |
Result Cache | ✅ | ✅ | ✅ | ❌ |
Download Cache | ✅ | ✅ | ✅ | ❌ |
Default image decoder | BitmapFactory | Skia Image | Skia Image | Skia Image |
Minimum API | API 21 | - | JDK 1.8 | - |
The minimum API is '-' to synchronize with Compose Multiplatform
The Sketch class is the core of the entire framework, which is used to execute and manage ImageRequest
The sketch-compose
and sketch-view
modules depend on the sketch-singleton
module, so you can
use the singleton mode by directly relying on them.
In singleton mode, you do not need to actively create a Sketch instance. You can directly obtain the shared Sketch instance, as follows:
// Android
val sketch = context.sketch
val sketch = SingletonSketch.get(context)
// Non Android
val sketch = SingletonSketch.get()
When you need to customize Sketch, you can create Sketch and configure it in the following ways:
// 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
When using SingletonSketch.setSafe() to customize Sketch, you need to call it as early as possible, preferably in the entry function of the App
In non-singleton mode, you need to create Sketch yourself and remember it, and then use the instance you created when needed, as follows:
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)
}
ImageRequest is used to describe an image loading request, which includes the uri of the image and placeholder image, transform, transition, new size, Target, Listener and other configurations
Create a simple ImageRequest that limits the maximum number of pixels of the image to 300x300
val request = ImageRequest(context, "https://www.example.com/image.jpg") {
size(300, 300)
// There is a lot more...
}
Tip
For more configuration of ImageRequest, please refer to the ImageRequest.Builder class
To load the results directly into the component, you also need to configure Target
On Compose Target is configured by AsyncImage and AsyncImagePainter's cornerstone AsyncImageState, you just need to Just pass ImageRequest to AsyncImage or AsyncImagePainter, as follows:
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
You cannot call the target() function in AsyncImage and AsyncImagePainter, which will cause the app to crash
In the Android View system, you need to actively call the target() function and pass in the ImageView, as follows:
val request = ImageRequest(context, "https://www.example.com/image.jpg") {
size(300, 300)
target(imageView)
// There is a lot more...
}
context.sketch.enqueue(request)
You can also use ImageRequest(ImageView, String) or ImageView.loadImage() extension functions, they will call target() for you, as follows:
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...
}
After ImageRequest is created, it is handed over to Sketch for execution. Sketch supports asynchronous and synchronous execution of ImageRequest, as follows:
val request = ImageRequest(context, "https://www.example.com/image.jpg")
// Asynchronous execution of ImageRequest does not block the current thread or suspend the current coroutine.
val disposable: Disposable = sketch.enqueue(request)
// Synchronously execute ImageRequest and suspend the current coroutine until the result is returned.
coroutineScope.launch(Dispatchers.Main) {
val imageResult: ImageResult = sketch.execute(request)
val image: Image = imageResult.image
}
Note
The singleton mode provides ImageRequest.enqueue() and ImageRequest.execute() extension functions for ImageRequest to facilitate sequential writing.
When Target is configured, Sketch will hand over the results to Target for display, but sometimes you need to do something with the results or when Target is not configured, you need to actively obtain the results, as follows:
val request = ImageRequest(context, "https://www.example.com/image.jpg")
// When using the enqueue() method to asynchronously execute a request, you can obtain the result through the returned Disposable.job
val disposable = sketch.enqueue(request)
coroutineScope.launch(Dispatchers.Main) {
val imageResult: ImageResult = disposable.job.await()
}
// You can directly obtain the results when executing a request synchronously using the execute() method.
coroutineScope.launch(Dispatchers.Main) {
val imageResult: ImageResult = sketch.execute(request)
}
ImageResult contains a lot of useful information, as follows:
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
}
When Target is configured, ImageRequest will automatically cancel the request under the following circumstances:
- AsyncImage or AsyncImagePainter component forgotten
- ImageView's onViewDetachedFromWindow() method is executed
- Lifecycle changes to DESTROYED state
When Target is not configured or when active cancellation is required, it can be canceled through Disposable or Job, as follows:
// When using the enqueue() method to asynchronously execute a request, a Disposable will be returned, which can be used to cancel the request when needed.
val request = ImageRequest(context, "https://www.example.com/image.jpg")
val disposable = sketch.enqueue(request)
disposable.dispose()
// When using the execute() method to execute a request synchronously, you can cancel the request through its coroutine's Job when needed.
val job = coroutineScope.launch(Dispatchers.Main) {
val request = ImageRequest(context, "https://www.example.com/image.jpg")
val imageResult: ImageResult = sketch.execute(request)
}
job.cancel()
Sketch provides a series of extensions for ImageView, as follows:
// 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() is only available in singleton mode
Basic functions:
- Register component
- Compose
- Http: Load network images
- AnimatedImage: GIF、WEBP、HEIF
- Resize: Modify the image size
- Transformation: Transformation image
- Transition: Display images in cool transitions
- StateImage: Placeholder and error images
- Listener: Listen for request status and download progress
- DownloadCache: Understand download caching to avoid repeated downloads
- ResultCache: Understand result caching to avoid duplicate conversions
- MemoryCache: Understand memory caching to avoid repeated loading
- Fetcher: Learn about Fetcher and extend new URI types
- Decoder: Understand the decoding process of Sketch
- Target: Apply the load results to the target
- SVG: Decode SVG still images
- VideoFrames: Decode video frames
- ExifOrientation: Correct the image orientation
- ImageOptions: Manage image configurations in a unified manner
- RequestInterceptor: Intercept ImageRequest
- DecodeInterceptor: Intercept the decoding process
- Preload images into memory
- Download images
- Lifecycle
- Log
Featured functions:
- SketchImageView: Configure the request through XML attributes
- Improve the clarity of long images in grid lists
- Displays the download progress
- Displays the image type corner
- Pause image downloads on cellular data to save data
- The list slides to pause the loading of images
- Displays an icon for an apk file or installed app