Add html sample
This commit is contained in:
parent
ffb5848c3c
commit
fdb0f76e13
@ -4,7 +4,7 @@
|
|||||||
These are _configurable_ properties:
|
These are _configurable_ properties:
|
||||||
* `AsyncDrawableLoader` (back here since <Badge text="4.0.0" />)
|
* `AsyncDrawableLoader` (back here since <Badge text="4.0.0" />)
|
||||||
* `SyntaxHighlight`
|
* `SyntaxHighlight`
|
||||||
* `LinkSpan.Resolver`
|
* `LinkResolver` (since <Badge text="4.0.0" />, before — `LinkSpan.Resolver`)
|
||||||
* `UrlProcessor`
|
* `UrlProcessor`
|
||||||
* `ImageSizeResolver`
|
* `ImageSizeResolver`
|
||||||
|
|
||||||
@ -37,9 +37,9 @@ final Markwon markwon = Markwon.builder(context)
|
|||||||
```
|
```
|
||||||
|
|
||||||
Currently `Markwon` provides 3 implementations for loading images:
|
Currently `Markwon` provides 3 implementations for loading images:
|
||||||
* [own implementation](/docs/v4/image.md) with SVG, GIF, data uri and android_assets support
|
* [markwon implementation](/docs/v4/image/) with SVG, GIF, data uri and android_assets support
|
||||||
* [based on Picasso](/docs/v4/image-picasso.md)
|
* [based on Picasso](/docs/v4/image-picasso/)
|
||||||
* [based on Glide](/docs/v4/image-glide.md)
|
* [based on Glide](/docs/v4/image-glide/)
|
||||||
|
|
||||||
## SyntaxHighlight
|
## SyntaxHighlight
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ Use [syntax-highlight](/docs/v4/syntax-highlight/) to add syntax highlighting
|
|||||||
to your application
|
to your application
|
||||||
:::
|
:::
|
||||||
|
|
||||||
## LinkSpan.Resolver
|
## LinkResolver
|
||||||
|
|
||||||
React to a link click event. By default `LinkResolverDef` is used,
|
React to a link click event. By default `LinkResolverDef` is used,
|
||||||
which tries to start an Activity given the `link` argument. If no
|
which tries to start an Activity given the `link` argument. If no
|
||||||
@ -70,9 +70,9 @@ final Markwon markwon = Markwon.builder(this)
|
|||||||
.usePlugin(new AbstractMarkwonPlugin() {
|
.usePlugin(new AbstractMarkwonPlugin() {
|
||||||
@Override
|
@Override
|
||||||
public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) {
|
public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) {
|
||||||
builder.linkResolver(new LinkSpan.Resolver() {
|
builder.linkResolver(new LinkResolver() {
|
||||||
@Override
|
@Override
|
||||||
public void resolve(View view, @NonNull String link) {
|
public void resolve(@NonNull View view, @NonNull String link) {
|
||||||
// react to link click here
|
// react to link click here
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -119,8 +119,7 @@ will be kept as-is.
|
|||||||
|
|
||||||
## ImageSizeResolver
|
## ImageSizeResolver
|
||||||
|
|
||||||
`ImageSizeResolver` controls the size of an image to be displayed. Currently it
|
`ImageSizeResolver` controls the size of an image to be displayed.
|
||||||
handles only HTML images (specified via `img` tag).
|
|
||||||
|
|
||||||
```java
|
```java
|
||||||
final Markwon markwon = Markwon.builder(this)
|
final Markwon markwon = Markwon.builder(this)
|
||||||
@ -130,12 +129,9 @@ final Markwon markwon = Markwon.builder(this)
|
|||||||
builder.imageSizeResolver(new ImageSizeResolver() {
|
builder.imageSizeResolver(new ImageSizeResolver() {
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Rect resolveImageSize(
|
public Rect resolveImageSize(@NonNull AsyncDrawable drawable) {
|
||||||
@Nullable ImageSize imageSize,
|
final ImageSize imageSize = drawable.getImageSize();
|
||||||
@NonNull Rect imageBounds,
|
return drawable.getResult().getBounds();
|
||||||
int canvasWidth,
|
|
||||||
float textSize) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -168,6 +164,5 @@ so we will have no point-of-reference from which to _calculate_ image height.
|
|||||||
`ImageSizeResolverDef` also takes care for an image to **not** exceed
|
`ImageSizeResolverDef` also takes care for an image to **not** exceed
|
||||||
canvas width. If an image has greater width than a TextView Canvas, then
|
canvas width. If an image has greater width than a TextView Canvas, then
|
||||||
image will be _scaled-down_ to fit the canvas. Please note that this rule
|
image will be _scaled-down_ to fit the canvas. Please note that this rule
|
||||||
applies only if image has no absolute sizes (for example width is specified
|
applies only if image has no sizes specified (`ImageSize == null`).
|
||||||
in pixels).
|
|
||||||
:::
|
:::
|
@ -35,6 +35,8 @@ final Markwon markwon = Markwon.builder(context)
|
|||||||
.align(JLatexMathDrawable.ALIGN_CENTER)
|
.align(JLatexMathDrawable.ALIGN_CENTER)
|
||||||
.fitCanvas(true)
|
.fitCanvas(true)
|
||||||
.padding(paddingPx)
|
.padding(paddingPx)
|
||||||
|
// @since 4.0.0 - horizontal and vertical padding
|
||||||
|
.padding(paddingHorizontalPx, paddingVerticalPx)
|
||||||
// @since 4.0.0 - change to provider
|
// @since 4.0.0 - change to provider
|
||||||
.backgroundProvider(() -> new MyDrawable()))
|
.backgroundProvider(() -> new MyDrawable()))
|
||||||
// @since 4.0.0 - optional, by default cached-thread-pool will be used
|
// @since 4.0.0 - optional, by default cached-thread-pool will be used
|
||||||
|
@ -104,4 +104,132 @@ If you wish to exclude some of them `TagHandlerNoOp` can be used:
|
|||||||
|
|
||||||
## TagHandler
|
## TagHandler
|
||||||
|
|
||||||
|
To define a tag-handler that applies style for the whole tag content (from start to end),
|
||||||
|
a `SimpleTagHandler` can be used. For example, let's define `<align>` tag, which can be used
|
||||||
|
like this:
|
||||||
|
|
||||||
|
* `<align center>centered text</align>`
|
||||||
|
* `<align end>this should be aligned at the end (right for LTR locales)</align>`
|
||||||
|
* `<align>regular alignment</align>`
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class AlignTagHandler extends SimpleTagHandler {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Object getSpans(
|
||||||
|
@NonNull MarkwonConfiguration configuration,
|
||||||
|
@NonNull RenderProps renderProps,
|
||||||
|
@NonNull HtmlTag tag) {
|
||||||
|
|
||||||
|
final Layout.Alignment alignment;
|
||||||
|
|
||||||
|
// html attribute without value, <align center></align>
|
||||||
|
if (tag.attributes().containsKey("center")) {
|
||||||
|
alignment = Layout.Alignment.ALIGN_CENTER;
|
||||||
|
} else if (tag.attributes().containsKey("end")) {
|
||||||
|
alignment = Layout.Alignment.ALIGN_OPPOSITE;
|
||||||
|
} else {
|
||||||
|
// empty value or any other will make regular alignment
|
||||||
|
alignment = Layout.Alignment.ALIGN_NORMAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AlignmentSpan.Standard(alignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Collection<String> supportedTags() {
|
||||||
|
return Collections.singleton("align");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
`SimpleTagHandler` can return an array of spans from `getSpans` method
|
||||||
|
:::
|
||||||
|
|
||||||
|
Then register `AlignTagHandler`:
|
||||||
|
|
||||||
|
```java
|
||||||
|
final Markwon markwon = Markwon.builder(this)
|
||||||
|
.usePlugin(HtmlPlugin.create())
|
||||||
|
.usePlugin(new AbstractMarkwonPlugin() {
|
||||||
|
@Override
|
||||||
|
public void configure(@NonNull Registry registry) {
|
||||||
|
registry.require(HtmlPlugin.class, htmlPlugin -> htmlPlugin
|
||||||
|
.addHandler(new AlignTagHandler());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
```
|
||||||
|
|
||||||
|
or directly on `HtmlPlugin`:
|
||||||
|
|
||||||
|
```java
|
||||||
|
final Markwon markwon = Markwon.builder(this)
|
||||||
|
.usePlugin(HtmlPlugin.create(plugin -> plugin.addHandler(new AlignTagHandler())))
|
||||||
|
.build();
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
If a tag requires special handling `TagHandler` can be used directly. For example
|
||||||
|
let's define an `<enhance>` tag with `start` and `end` arguments, that will mark
|
||||||
|
start and end positions of the text that needs to be enlarged:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<enhance start="5" end="12">This is text that must be enhanced, at least a part of it</enhance>
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class EnhanceTagHandler extends TagHandler {
|
||||||
|
|
||||||
|
private final int enhanceTextSize;
|
||||||
|
|
||||||
|
EnhanceTagHandler(@Px int enhanceTextSize) {
|
||||||
|
this.enhanceTextSize = enhanceTextSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(
|
||||||
|
@NonNull MarkwonVisitor visitor,
|
||||||
|
@NonNull MarkwonHtmlRenderer renderer,
|
||||||
|
@NonNull HtmlTag tag) {
|
||||||
|
|
||||||
|
// we require start and end to be present
|
||||||
|
final int start = parsePosition(tag.attributes().get("start"));
|
||||||
|
final int end = parsePosition(tag.attributes().get("end"));
|
||||||
|
|
||||||
|
if (start > -1 && end > -1) {
|
||||||
|
visitor.builder().setSpan(
|
||||||
|
new AbsoluteSizeSpan(enhanceTextSize),
|
||||||
|
tag.start() + start,
|
||||||
|
tag.start() + end
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Collection<String> supportedTags() {
|
||||||
|
return Collections.singleton("enhance");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int parsePosition(@Nullable String value) {
|
||||||
|
int position;
|
||||||
|
if (!TextUtils.isEmpty(value)) {
|
||||||
|
try {
|
||||||
|
position = Integer.parseInt(value);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
position = -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
position = -1;
|
||||||
|
}
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
@ -24,6 +24,7 @@
|
|||||||
<activity android:name="io.noties.markwon.sample.basicplugins.BasicPluginsActivity" />
|
<activity android:name="io.noties.markwon.sample.basicplugins.BasicPluginsActivity" />
|
||||||
<activity android:name="io.noties.markwon.sample.recycler.RecyclerActivity" />
|
<activity android:name="io.noties.markwon.sample.recycler.RecyclerActivity" />
|
||||||
<activity android:name="io.noties.markwon.sample.theme.ThemeActivity" />
|
<activity android:name="io.noties.markwon.sample.theme.ThemeActivity" />
|
||||||
|
<activity android:name=".html.HtmlActivity" />
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ import io.noties.markwon.Markwon;
|
|||||||
import io.noties.markwon.sample.basicplugins.BasicPluginsActivity;
|
import io.noties.markwon.sample.basicplugins.BasicPluginsActivity;
|
||||||
import io.noties.markwon.sample.core.CoreActivity;
|
import io.noties.markwon.sample.core.CoreActivity;
|
||||||
import io.noties.markwon.sample.customextension.CustomExtensionActivity;
|
import io.noties.markwon.sample.customextension.CustomExtensionActivity;
|
||||||
|
import io.noties.markwon.sample.html.HtmlActivity;
|
||||||
import io.noties.markwon.sample.latex.LatexActivity;
|
import io.noties.markwon.sample.latex.LatexActivity;
|
||||||
import io.noties.markwon.sample.recycler.RecyclerActivity;
|
import io.noties.markwon.sample.recycler.RecyclerActivity;
|
||||||
|
|
||||||
@ -97,6 +98,10 @@ public class MainActivity extends Activity {
|
|||||||
activity = RecyclerActivity.class;
|
activity = RecyclerActivity.class;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case HTML:
|
||||||
|
activity = HtmlActivity.class;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new IllegalStateException("No Activity is associated with sample-item: " + item);
|
throw new IllegalStateException("No Activity is associated with sample-item: " + item);
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ public enum Sample {
|
|||||||
|
|
||||||
RECYCLER(R.string.sample_recycler),
|
RECYCLER(R.string.sample_recycler),
|
||||||
|
|
||||||
;
|
HTML(R.string.sample_html);
|
||||||
|
|
||||||
private final int textResId;
|
private final int textResId;
|
||||||
|
|
||||||
|
@ -0,0 +1,190 @@
|
|||||||
|
package io.noties.markwon.sample.html;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.Layout;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.text.style.AbsoluteSizeSpan;
|
||||||
|
import android.text.style.AlignmentSpan;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.Px;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import io.noties.markwon.AbstractMarkwonPlugin;
|
||||||
|
import io.noties.markwon.Markwon;
|
||||||
|
import io.noties.markwon.MarkwonConfiguration;
|
||||||
|
import io.noties.markwon.MarkwonVisitor;
|
||||||
|
import io.noties.markwon.RenderProps;
|
||||||
|
import io.noties.markwon.SpannableBuilder;
|
||||||
|
import io.noties.markwon.html.HtmlPlugin;
|
||||||
|
import io.noties.markwon.html.HtmlTag;
|
||||||
|
import io.noties.markwon.html.MarkwonHtmlRenderer;
|
||||||
|
import io.noties.markwon.html.TagHandler;
|
||||||
|
import io.noties.markwon.html.tag.SimpleTagHandler;
|
||||||
|
import io.noties.markwon.sample.R;
|
||||||
|
|
||||||
|
public class HtmlActivity extends Activity {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
setContentView(R.layout.activity_text_view);
|
||||||
|
|
||||||
|
// let's define some custom tag-handlers
|
||||||
|
|
||||||
|
final TextView textView = findViewById(R.id.text_view);
|
||||||
|
|
||||||
|
final Markwon markwon = Markwon.builder(this)
|
||||||
|
.usePlugin(HtmlPlugin.create())
|
||||||
|
.usePlugin(new AbstractMarkwonPlugin() {
|
||||||
|
@Override
|
||||||
|
public void configure(@NonNull Registry registry) {
|
||||||
|
registry.require(HtmlPlugin.class, htmlPlugin -> htmlPlugin
|
||||||
|
.addHandler(new AlignTagHandler())
|
||||||
|
.addHandler(new RandomCharSize(new Random(42L), textView.getTextSize()))
|
||||||
|
.addHandler(new EnhanceTagHandler((int) (textView.getTextSize() * 2 + .05F))));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final String markdown = "# Hello, HTML\n" +
|
||||||
|
"\n" +
|
||||||
|
"<align center>We are centered</align>\n" +
|
||||||
|
"\n" +
|
||||||
|
"<align end>We are at the end</align>\n" +
|
||||||
|
"\n" +
|
||||||
|
"<align>We should be at the start</align>\n" +
|
||||||
|
"\n" +
|
||||||
|
"<random-char-size>\n" +
|
||||||
|
"This message should have a jumpy feeling because of different sizes of characters\n" +
|
||||||
|
"</random-char-size>\n\n" +
|
||||||
|
"<enhance start=\"5\" end=\"12\">This is text that must be enhanced, at least a part of it</enhance>";
|
||||||
|
|
||||||
|
markwon.setMarkdown(textView, markdown);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we can use `SimpleTagHandler` for _simple_ cases (when the whole tag content
|
||||||
|
// will have spans from start to end)
|
||||||
|
//
|
||||||
|
// we can use any tag name, even not defined in HTML spec
|
||||||
|
private static class AlignTagHandler extends SimpleTagHandler {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Object getSpans(
|
||||||
|
@NonNull MarkwonConfiguration configuration,
|
||||||
|
@NonNull RenderProps renderProps,
|
||||||
|
@NonNull HtmlTag tag) {
|
||||||
|
|
||||||
|
final Layout.Alignment alignment;
|
||||||
|
|
||||||
|
// html attribute without value, <align center></align>
|
||||||
|
if (tag.attributes().containsKey("center")) {
|
||||||
|
alignment = Layout.Alignment.ALIGN_CENTER;
|
||||||
|
} else if (tag.attributes().containsKey("end")) {
|
||||||
|
alignment = Layout.Alignment.ALIGN_OPPOSITE;
|
||||||
|
} else {
|
||||||
|
// empty value or any other will make regular alignment
|
||||||
|
alignment = Layout.Alignment.ALIGN_NORMAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AlignmentSpan.Standard(alignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Collection<String> supportedTags() {
|
||||||
|
return Collections.singleton("align");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// each character will have random size
|
||||||
|
private static class RandomCharSize extends TagHandler {
|
||||||
|
|
||||||
|
private final Random random;
|
||||||
|
private final float base;
|
||||||
|
|
||||||
|
RandomCharSize(@NonNull Random random, float base) {
|
||||||
|
this.random = random;
|
||||||
|
this.base = base;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(
|
||||||
|
@NonNull MarkwonVisitor visitor,
|
||||||
|
@NonNull MarkwonHtmlRenderer renderer,
|
||||||
|
@NonNull HtmlTag tag) {
|
||||||
|
|
||||||
|
final SpannableBuilder builder = visitor.builder();
|
||||||
|
|
||||||
|
// text content is already added, we should only apply spans
|
||||||
|
|
||||||
|
for (int i = tag.start(), end = tag.end(); i < end; i++) {
|
||||||
|
final int size = (int) (base * (random.nextFloat() + 0.5F) + 0.5F);
|
||||||
|
builder.setSpan(new AbsoluteSizeSpan(size, false), i, i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Collection<String> supportedTags() {
|
||||||
|
return Collections.singleton("random-char-size");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class EnhanceTagHandler extends TagHandler {
|
||||||
|
|
||||||
|
private final int enhanceTextSize;
|
||||||
|
|
||||||
|
EnhanceTagHandler(@Px int enhanceTextSize) {
|
||||||
|
this.enhanceTextSize = enhanceTextSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(
|
||||||
|
@NonNull MarkwonVisitor visitor,
|
||||||
|
@NonNull MarkwonHtmlRenderer renderer,
|
||||||
|
@NonNull HtmlTag tag) {
|
||||||
|
|
||||||
|
// we require start and end to be present
|
||||||
|
final int start = parsePosition(tag.attributes().get("start"));
|
||||||
|
final int end = parsePosition(tag.attributes().get("end"));
|
||||||
|
|
||||||
|
if (start > -1 && end > -1) {
|
||||||
|
visitor.builder().setSpan(
|
||||||
|
new AbsoluteSizeSpan(enhanceTextSize),
|
||||||
|
tag.start() + start,
|
||||||
|
tag.start() + end
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Collection<String> supportedTags() {
|
||||||
|
return Collections.singleton("enhance");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int parsePosition(@Nullable String value) {
|
||||||
|
int position;
|
||||||
|
if (!TextUtils.isEmpty(value)) {
|
||||||
|
try {
|
||||||
|
position = Integer.parseInt(value);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
position = -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
position = -1;
|
||||||
|
}
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,4 +15,6 @@
|
|||||||
<string name="sample_recycler"># \# Recycler\n\nShow how to render markdown in a RecyclerView.
|
<string name="sample_recycler"># \# Recycler\n\nShow how to render markdown in a RecyclerView.
|
||||||
Renders code blocks wrapped in a HorizontalScrollView. Renders tables in a custom view.</string>
|
Renders code blocks wrapped in a HorizontalScrollView. Renders tables in a custom view.</string>
|
||||||
|
|
||||||
|
<string name="sample_html"># \# Html\n\nShows how to define own tag handlers</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
Loading…
x
Reference in New Issue
Block a user