From 815f7338921efa2c0775937ec510a60ec8b15cef Mon Sep 17 00:00:00 2001 From: Dimitry Ivanov Date: Mon, 16 Mar 2020 10:30:44 +0300 Subject: [PATCH] Sample, anchor links --- .../basicplugins/BasicPluginsActivity.java | 103 +++++++++++++++- .../markwon/sample/html/HtmlActivity.java | 112 +++++++++++++----- .../main/res/layout/activity_text_view.xml | 1 + sample/src/main/res/values/strings.xml | 12 ++ 4 files changed, 197 insertions(+), 31 deletions(-) diff --git a/sample/src/main/java/io/noties/markwon/sample/basicplugins/BasicPluginsActivity.java b/sample/src/main/java/io/noties/markwon/sample/basicplugins/BasicPluginsActivity.java index 632c94ec..ce73a7f8 100644 --- a/sample/src/main/java/io/noties/markwon/sample/basicplugins/BasicPluginsActivity.java +++ b/sample/src/main/java/io/noties/markwon/sample/basicplugins/BasicPluginsActivity.java @@ -3,8 +3,12 @@ package io.noties.markwon.sample.basicplugins; import android.graphics.Color; import android.net.Uri; import android.os.Bundle; +import android.text.Spannable; +import android.text.Spanned; import android.text.TextUtils; import android.text.style.ForegroundColorSpan; +import android.view.View; +import android.widget.ScrollView; import android.widget.TextView; import androidx.annotation.NonNull; @@ -19,6 +23,7 @@ import java.util.Collections; import io.noties.markwon.AbstractMarkwonPlugin; import io.noties.markwon.BlockHandlerDef; +import io.noties.markwon.LinkResolverDef; import io.noties.markwon.Markwon; import io.noties.markwon.MarkwonConfiguration; import io.noties.markwon.MarkwonSpansFactory; @@ -26,6 +31,7 @@ import io.noties.markwon.MarkwonVisitor; import io.noties.markwon.SoftBreakAddsNewLinePlugin; import io.noties.markwon.core.CoreProps; import io.noties.markwon.core.MarkwonTheme; +import io.noties.markwon.core.spans.HeadingSpan; import io.noties.markwon.core.spans.LastLineSpacingSpan; import io.noties.markwon.image.ImageItem; import io.noties.markwon.image.ImagesPlugin; @@ -39,6 +45,7 @@ import io.noties.markwon.sample.R; public class BasicPluginsActivity extends ActivityWithMenuOptions { private TextView textView; + private ScrollView scrollView; @NonNull @Override @@ -54,7 +61,8 @@ public class BasicPluginsActivity extends ActivityWithMenuOptions { .add("additionalSpacing", this::additionalSpacing) .add("headingNoSpace", this::headingNoSpace) .add("headingNoSpaceBlockHandler", this::headingNoSpaceBlockHandler) - .add("allBlocksNoForcedLine", this::allBlocksNoForcedLine); + .add("allBlocksNoForcedLine", this::allBlocksNoForcedLine) + .add("anchor", this::anchor); } @Override @@ -63,6 +71,7 @@ public class BasicPluginsActivity extends ActivityWithMenuOptions { setContentView(R.layout.activity_text_view); textView = findViewById(R.id.text_view); + scrollView = findViewById(R.id.scroll_view); paragraphSpan(); // @@ -403,4 +412,96 @@ public class BasicPluginsActivity extends ActivityWithMenuOptions { // rendering lifecycle (before/after) // renderProps // process + + private static class AnchorSpan { + final String anchor; + + AnchorSpan(@NonNull String anchor) { + this.anchor = anchor; + } + } + + @NonNull + private String createAnchor(@NonNull CharSequence content) { + return String.valueOf(content) + .replaceAll("[^\\w]", "") + .toLowerCase(); + } + + private static class AnchorLinkResolver extends LinkResolverDef { + + interface ScrollTo { + void scrollTo(@NonNull View view, int top); + } + + private final ScrollTo scrollTo; + + AnchorLinkResolver(@NonNull ScrollTo scrollTo) { + this.scrollTo = scrollTo; + } + + @Override + public void resolve(@NonNull View view, @NonNull String link) { + if (link.startsWith("#")) { + final TextView textView = (TextView) view; + final Spanned spanned = (Spannable) textView.getText(); + final AnchorSpan[] spans = spanned.getSpans(0, spanned.length(), AnchorSpan.class); + if (spans != null) { + final String anchor = link.substring(1); + for (AnchorSpan span: spans) { + if (anchor.equals(span.anchor)) { + final int start = spanned.getSpanStart(span); + final int line = textView.getLayout().getLineForOffset(start); + final int top = textView.getLayout().getLineTop(line); + scrollTo.scrollTo(textView, top); + return; + } + } + } + } + super.resolve(view, link); + } + } + + private void anchor() { + final String lorem = getString(R.string.lorem); + final String md = "" + + "Hello [there](#there)!\n\n\n" + + lorem + "\n\n" + + "# There!\n\n" + + lorem; + + final Markwon markwon = Markwon.builder(this) + .usePlugin(new AbstractMarkwonPlugin() { + @Override + public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) { + builder.linkResolver(new AnchorLinkResolver((view, top) -> { + scrollView.smoothScrollTo(0, top); + })); + } + + @Override + public void afterSetText(@NonNull TextView textView) { + final Spannable spannable = (Spannable) textView.getText(); + // obtain heading spans + final HeadingSpan[] spans = spannable.getSpans(0, spannable.length(), HeadingSpan.class); + if (spans != null) { + for (HeadingSpan span : spans) { + final int start = spannable.getSpanStart(span); + final int end = spannable.getSpanEnd(span); + final int flags = spannable.getSpanFlags(span); + spannable.setSpan( + new AnchorSpan(createAnchor(spannable.subSequence(start, end))), + start, + end, + flags + ); + } + } + } + }) + .build(); + + markwon.setMarkdown(textView, md); + } } diff --git a/sample/src/main/java/io/noties/markwon/sample/html/HtmlActivity.java b/sample/src/main/java/io/noties/markwon/sample/html/HtmlActivity.java index 21613985..eecd1c3b 100644 --- a/sample/src/main/java/io/noties/markwon/sample/html/HtmlActivity.java +++ b/sample/src/main/java/io/noties/markwon/sample/html/HtmlActivity.java @@ -1,6 +1,5 @@ package io.noties.markwon.sample.html; -import android.app.Activity; import android.os.Bundle; import android.text.Layout; import android.text.TextUtils; @@ -27,9 +26,22 @@ 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.ActivityWithMenuOptions; +import io.noties.markwon.sample.MenuOptions; import io.noties.markwon.sample.R; -public class HtmlActivity extends Activity { +public class HtmlActivity extends ActivityWithMenuOptions { + + @NonNull + @Override + public MenuOptions menuOptions() { + return MenuOptions.create() + .add("align", this::align) + .add("randomCharSize", this::randomCharSize) + .add("enhance", this::enhance); + } + + private TextView textView; @Override public void onCreate(@Nullable Bundle savedInstanceState) { @@ -39,35 +51,9 @@ public class HtmlActivity extends Activity { // let's define some custom tag-handlers - final TextView textView = findViewById(R.id.text_view); + 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" + - "We are centered\n" + - "\n" + - "We are at the end\n" + - "\n" + - "We should be at the start\n" + - "\n" + - "\n" + - "This message should have a jumpy feeling because of different sizes of characters\n" + - "\n\n" + - "This is text that must be enhanced, at least a part of it"; - - markwon.setMarkdown(textView, markdown); + align(); } // we can use `SimpleTagHandler` for _simple_ cases (when the whole tag content @@ -105,6 +91,31 @@ public class HtmlActivity extends Activity { } } + private void align() { + + final String md = "" + + "We are centered\n" + + "\n" + + "We are at the end\n" + + "\n" + + "We should be at the start\n" + + "\n"; + + + 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(); + + markwon.setMarkdown(textView, md); + } + // each character will have random size private static class RandomCharSize extends TagHandler { @@ -139,6 +150,27 @@ public class HtmlActivity extends Activity { } } + private void randomCharSize() { + + final String md = "" + + "\n" + + "This message should have a jumpy feeling because of different sizes of characters\n" + + "\n\n"; + + 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 RandomCharSize(new Random(42L), textView.getTextSize()))); + } + }) + .build(); + + markwon.setMarkdown(textView, md); + } + private static class EnhanceTagHandler extends TagHandler { private final int enhanceTextSize; @@ -187,4 +219,24 @@ public class HtmlActivity extends Activity { return position; } } + + private void enhance() { + + final String md = "" + + "This is text that must be enhanced, at least a part of it"; + + + 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 EnhanceTagHandler((int) (textView.getTextSize() * 2 + .05F)))); + } + }) + .build(); + + markwon.setMarkdown(textView, md); + } } diff --git a/sample/src/main/res/layout/activity_text_view.xml b/sample/src/main/res/layout/activity_text_view.xml index 9828f257..c3904e70 100644 --- a/sample/src/main/res/layout/activity_text_view.xml +++ b/sample/src/main/res/layout/activity_text_view.xml @@ -1,5 +1,6 @@ diff --git a/sample/src/main/res/values/strings.xml b/sample/src/main/res/values/strings.xml index 5505eb0c..36d27f2a 100644 --- a/sample/src/main/res/values/strings.xml +++ b/sample/src/main/res/values/strings.xml @@ -12,4 +12,16 @@ Sentiment Satisfied 64 red: @ic-sentiment_satisfied-red-64 ]]> + +