Add html sample

This commit is contained in:
Dimitry Ivanov 2019-06-24 13:47:44 +03:00
parent ffb5848c3c
commit fdb0f76e13
8 changed files with 341 additions and 18 deletions

View File

@ -4,7 +4,7 @@
These are _configurable_ properties:
* `AsyncDrawableLoader` (back here since <Badge text="4.0.0" />)
* `SyntaxHighlight`
* `LinkSpan.Resolver`
* `LinkResolver` (since <Badge text="4.0.0" />, before &mdash; `LinkSpan.Resolver`)
* `UrlProcessor`
* `ImageSizeResolver`
@ -37,9 +37,9 @@ final Markwon markwon = Markwon.builder(context)
```
Currently `Markwon` provides 3 implementations for loading images:
* [own implementation](/docs/v4/image.md) with SVG, GIF, data uri and android_assets support
* [based on Picasso](/docs/v4/image-picasso.md)
* [based on Glide](/docs/v4/image-glide.md)
* [markwon implementation](/docs/v4/image/) with SVG, GIF, data uri and android_assets support
* [based on Picasso](/docs/v4/image-picasso/)
* [based on Glide](/docs/v4/image-glide/)
## SyntaxHighlight
@ -59,7 +59,7 @@ Use [syntax-highlight](/docs/v4/syntax-highlight/) to add syntax highlighting
to your application
:::
## LinkSpan.Resolver
## LinkResolver
React to a link click event. By default `LinkResolverDef` is used,
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() {
@Override
public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) {
builder.linkResolver(new LinkSpan.Resolver() {
builder.linkResolver(new LinkResolver() {
@Override
public void resolve(View view, @NonNull String link) {
public void resolve(@NonNull View view, @NonNull String link) {
// react to link click here
}
});
@ -119,8 +119,7 @@ will be kept as-is.
## ImageSizeResolver
`ImageSizeResolver` controls the size of an image to be displayed. Currently it
handles only HTML images (specified via `img` tag).
`ImageSizeResolver` controls the size of an image to be displayed.
```java
final Markwon markwon = Markwon.builder(this)
@ -130,12 +129,9 @@ final Markwon markwon = Markwon.builder(this)
builder.imageSizeResolver(new ImageSizeResolver() {
@NonNull
@Override
public Rect resolveImageSize(
@Nullable ImageSize imageSize,
@NonNull Rect imageBounds,
int canvasWidth,
float textSize) {
return null;
public Rect resolveImageSize(@NonNull AsyncDrawable drawable) {
final ImageSize imageSize = drawable.getImageSize();
return drawable.getResult().getBounds();
}
});
}
@ -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
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
applies only if image has no absolute sizes (for example width is specified
in pixels).
applies only if image has no sizes specified (`ImageSize == null`).
:::

View File

@ -35,6 +35,8 @@ final Markwon markwon = Markwon.builder(context)
.align(JLatexMathDrawable.ALIGN_CENTER)
.fitCanvas(true)
.padding(paddingPx)
// @since 4.0.0 - horizontal and vertical padding
.padding(paddingHorizontalPx, paddingVerticalPx)
// @since 4.0.0 - change to provider
.backgroundProvider(() -> new MyDrawable()))
// @since 4.0.0 - optional, by default cached-thread-pool will be used

View File

@ -104,4 +104,132 @@ If you wish to exclude some of them `TagHandlerNoOp` can be used:
## 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;
}
}
```

View File

@ -24,6 +24,7 @@
<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.theme.ThemeActivity" />
<activity android:name=".html.HtmlActivity" />
</application>

View File

@ -21,6 +21,7 @@ import io.noties.markwon.Markwon;
import io.noties.markwon.sample.basicplugins.BasicPluginsActivity;
import io.noties.markwon.sample.core.CoreActivity;
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.recycler.RecyclerActivity;
@ -97,6 +98,10 @@ public class MainActivity extends Activity {
activity = RecyclerActivity.class;
break;
case HTML:
activity = HtmlActivity.class;
break;
default:
throw new IllegalStateException("No Activity is associated with sample-item: " + item);
}

View File

@ -15,7 +15,7 @@ public enum Sample {
RECYCLER(R.string.sample_recycler),
;
HTML(R.string.sample_html);
private final int textResId;

View File

@ -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;
}
}
}

View File

@ -15,4 +15,6 @@
<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>
<string name="sample_html"># \# Html\n\nShows how to define own tag handlers</string>
</resources>