2020-01-14 16:33:08 +03:00

355 lines
12 KiB
Markdown

# Image
<MavenBadge4 :artifact="'image'" />
In order to display images in your markdown `ImagesPlugin` can be used.
```java
final Markwon markwon = Markwon.builder(context)
.usePlugin(ImagesPlugin.create())
```
:::tip
There are also modules that add image loading capabilities to markdown
based on image-loading libraries: [image-glide](/docs/v4/image-glide/) and
[image-picasso](/docs/v4/image-picasso/)
:::
`ImagesPlugin` splits the image-loading into 2 parts: scheme-handling and media-decoding.
## SchemeHandler
To add a scheme-handler to `ImagesPlugin`:
```java
final Markwon markwon = Markwon.builder(context)
.usePlugin(ImagesPlugin.create())
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void configure(@NonNull Registry registry) {
registry.require(ImagesPlugin.class, new Action<ImagesPlugin>() {
@Override
public void apply(@NonNull ImagesPlugin imagesPlugin) {
imagesPlugin.addSchemeHandler(DataUriSchemeHandler.create());
}
});
}
})
```
```java
final Markwon markwon = Markwon.builder(context)
.usePlugin(ImagesPlugin.create(new ImagesPlugin.ImagesConfigure() {
@Override
public void configureImages(@NonNull ImagesPlugin plugin) {
plugin.addSchemeHandler(DataUriSchemeHandler.create());
}
}))
```
`ImagesPlugin` comes with a set of predefined scheme-handlers:
* `FileSchemeHandler` - `file://`
* `DataUriSchemeHandler` - `data:`
* `NetworkSchemeHandler` - `http`, `https`
* `OkHttpNetworkSchemeHandler` - `http`, `https`
### FileSchemeHandler
Loads images via `file://` scheme. Allows loading images from `assets` folder.
```java
// default implementation, no assets handling
FileSchemeHandler.create();
// assets loading
FileSchemeHandler.createWithAssets(context);
```
:::warning
Assets loading will work when your URL will include `android_asset` in the path,
for example: `file:///android_asset/image.png` (mind the 3 slashes `///`). If you wish
to _assume_ all images without proper scheme to point to assets folder, then you can use
[UrlProcessorAndroidAssets](/docs/v4/core/configuration.html#urlprocessorandroidassets)
:::
By default `ImagesPlugin` includes _plain_ `FileSchemeHandler` (without assets support),
so if you wish to change that you can explicitly specify it:
```java
final Markwon markwon = Markwon.builder(context)
.usePlugin(ImagesPlugin.create(new ImagesPlugin.ImagesConfigure() {
@Override
public void configureImages(@NonNull ImagesPlugin plugin) {
plugin.addSchemeHandler(FileSchemeHandler.createWithAssets(context));
}
}))
```
### DataUriSchemeHandler
`DataUriSchemeHandler` allows _inlining_ images with `data:` scheme (`data:image/svg+xml;base64,MTIz`).
This scheme-handler is registered by default, so you do not need to add it explicitly.
### NetworkSchemeHandler
`NetworkSchemeHandler` allows obtaining images from `http://` and `https://` uris
(internally it uses `HttpURLConnection`). This scheme-handler is registered by default
### OkHttpNetworkSchemeHandler
`OkHttpNetworkSchemeHandler` allows obtaining images from `http://` and `https://` uris
via [okhttp library](https://github.com/square/okhttp). Please note that in order to use
this scheme-handler you must explicitly add `okhttp` library to your project.
```java
// default instance
OkHttpNetworkSchemeHandler.create();
// specify OkHttpClient to use
OkHttpNetworkSchemeHandler.create(new OkHttpClient());
// @since 4.0.0
OkHttpNetworkSchemeHandler.create(Call.Factory);
```
### Custom SchemeHandler
```java
public abstract class SchemeHandler {
@NonNull
public abstract ImageItem handle(@NonNull String raw, @NonNull Uri uri);
@NonNull
public abstract Collection<String> supportedSchemes();
}
```
Starting with <Badge text="4.0.0" /> `SchemeHandler` can return a result (when no
further decoding is required):
```java
final Markwon markwon = Markwon.builder(context)
.usePlugin(ImagesPlugin.create(new ImagesPlugin.ImagesConfigure() {
@Override
public void configureImages(@NonNull ImagesPlugin plugin) {
// for example to return a drawable resource
plugin.addSchemeHandler(new SchemeHandler() {
@NonNull
@Override
public ImageItem handle(@NonNull String raw, @NonNull Uri uri) {
// will handle URLs like `drawable://ic_account_24dp_white`
final int resourceId = context.getResources().getIdentifier(
raw.substring("drawable://".length()),
"drawable",
context.getPackageName());
// it's fine if it throws, async-image-loader will catch exception
final Drawable drawable = context.getDrawable(resourceId);
return ImageItem.withResult(drawable);
}
@NonNull
@Override
public Collection<String> supportedSchemes() {
return Collections.singleton("drawable");
}
});
}
}))
```
Otherwise `SchemeHandler` must return an `InputStream` with proper `content-type` information
for further processing by a `MediaDecoder`:
```java
imagesPlugin.addSchemeHandler(new SchemeHandler() {
@NonNull
@Override
public ImageItem handle(@NonNull String raw, @NonNull Uri uri) {
return ImageItem.withDecodingNeeded("image/png", load(raw));
}
@NonNull
private InputStream load(@NonNull String raw) {...}
});
```
## MediaDecoder
`ImagesPlugin` comes with predefined media-decoders:
* `GifMediaDecoder` adds support for GIF
* `SvgMediaDecoder` adds support for SVG
* `DefaultMediaDecoder`
:::warning
If you wish to add support for **SVG** or **GIF** you must explicitly add these dependencies
to your project:
* to support `SVG`: [com.caverock:androidsvg](https://github.com/BigBadaboom/androidsvg)
* to support `GIF`: [pl.droidsonroids.gif:android-gif-drawable](https://github.com/koral--/android-gif-drawable)
For [security reasons](https://github.com/noties/Markwon/issues/186) it's advisable to use latest
versions of these libraries. If you notice compilation and/or runtime issues when used with Markwon,
please [create an issue](https://github.com/noties/Markwon/issues/new) specifying library and library version used.
:::
### GifMediaDecoder
Adds support for GIF media in markdown. If `pl.droidsonroids.gif:android-gif-drawable:*` dependency
is found in the classpath, then registration will happen automatically.
```java
final Markwon markwon = Markwon.builder(context)
.usePlugin(ImagesPlugin.create(new ImagesPlugin.ImagesConfigure() {
@Override
public void configureImages(@NonNull ImagesPlugin plugin) {
// autoplayGif controls if GIF should be automatically started
plugin.addMediaDecoder(GifMediaDecoder.create(/*autoplayGif*/false));
}
}))
.build();
```
### SvgMediaDecoder
Adds support for SVG media in markdown. If `com.caverock:androidsvg:*` dependency is found
in the classpath, then registration will happen automatically.
```java
final Markwon markwon = Markwon.builder(context)
.usePlugin(ImagesPlugin.create(new ImagesPlugin.ImagesConfigure() {
@Override
public void configureImages(@NonNull ImagesPlugin plugin) {
// uses supplied Resources
plugin.addMediaDecoder(SvgMediaDecoder.create(context.getResources()));
// uses Resources.getSystem()
plugin.addMediaDecoder(SvgMediaDecoder.create());
}
}))
.build();
```
### DefaultMediaDecoder
`DefaultMediaDecoder` _tries_ to decode supplied InputStream
as Bitmap (via `BitmapFactory.decodeStream(inputStream)`). This decoder is registered automatically.
```java
final Markwon markwon = Markwon.builder(context)
.usePlugin(ImagesPlugin.create(new ImagesPlugin.ImagesConfigure() {
@Override
public void configureImages(@NonNull ImagesPlugin plugin) {
// uses supplied Resources
plugin.defaultMediaDecoder(DefaultMediaDecoder.create(context.getResources()));
// uses Resources.getSystem()
plugin.defaultMediaDecoder(DefaultMediaDecoder.create());
}
}))
.build();
```
## AsyncDrawableScheduler
`AsyncDrawableScheduler` is used in order to give `AsyncDrawable` a way to invalidate `TextView`
that is holding it. A plugin that is dealing with `AsyncDrawable` should always call these methods:
```java
final Markwon markwon = Markwon.builder(context)
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void beforeSetText(@NonNull TextView textView, @NonNull Spanned markdown) {
AsyncDrawableScheduler.unschedule(textView);
}
@Override
public void afterSetText(@NonNull TextView textView) {
AsyncDrawableScheduler.schedule(textView);
}
})
.build();
```
:::tip
Starting with <Badge text="4.0.0" /> multiple plugins can call `AsyncDrawableScheduler#schedule`
method without the penalty to process `AsyncDrawable` callbacks multiple times (internally caches
state which ensures that a `TextView` is processed only once the text has changed).
:::
## ErrorHandler
An `ErrorHandler` can be used to receive an error that has happened during image loading
and (optionally) return an error drawable to be displayed instead
```java
final Markwon markwon = Markwon.builder(context)
.usePlugin(ImagesPlugin.create(new ImagesPlugin.ImagesConfigure() {
@Override
public void configureImages(@NonNull ImagesPlugin plugin) {
plugin.errorHandler(new ImagesPlugin.ErrorHandler() {
@Nullable
@Override
public Drawable handleError(@NonNull String url, @NonNull Throwable throwable) {
return null;
}
});
}
}))
.build();
```
## PlaceholderProvider
To display a placeholder during image loading `PlaceholderProvider` can be used:
```java
final Markwon markwon = Markwon.builder(context)
.usePlugin(ImagesPlugin.create(new ImagesPlugin.ImagesConfigure() {
@Override
public void configureImages(@NonNull ImagesPlugin plugin) {
plugin.placeholderProvider(new ImagesPlugin.PlaceholderProvider() {
@Nullable
@Override
public Drawable providePlaceholder(@NonNull AsyncDrawable drawable) {
return null;
}
});
}
}))
.build();
```
:::tip
If your placeholder drawable has _specific_ size which is not the same an image that is being loaded,
you can manually assign bounds to the placeholder:
```java
plugin.placeholderProvider(new ImagesPlugin.PlaceholderProvider() {
@Override
public Drawable providePlaceholder(@NonNull AsyncDrawable drawable) {
final ColorDrawable placeholder = new ColorDrawable(Color.BLUE);
// these bounds will be used to display a placeholder,
// so even if loading image has size `width=100%`, placeholder
// bounds won't be affected by it
placeholder.setBounds(0, 0, 48, 48);
return placeholder;
}
});
```
:::
---
:::tip
If you are using [html](/docs/v4/html/) you do not have to additionally setup
images displayed via `<img>` tag, as `HtmlPlugin` automatically uses configured
image loader. But images referenced in HTML come with additional support for
sizes, which is not supported natively by markdown, allowing absolute or relative sizes:
```html
<img src="./assets/my-image" width="100%">
```
:::