Add html sample
This commit is contained in:
		
							parent
							
								
									ffb5848c3c
								
							
						
					
					
						commit
						fdb0f76e13
					
				| @ -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 — `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`). | ||||
| ::: | ||||
| @ -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 | ||||
|  | ||||
| @ -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; | ||||
|     } | ||||
| } | ||||
| ``` | ||||
| @ -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> | ||||
| 
 | ||||
|  | ||||
| @ -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); | ||||
|         } | ||||
|  | ||||
| @ -15,7 +15,7 @@ public enum Sample { | ||||
| 
 | ||||
|     RECYCLER(R.string.sample_recycler), | ||||
| 
 | ||||
|     ; | ||||
|     HTML(R.string.sample_html); | ||||
| 
 | ||||
|     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. | ||||
|         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> | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Dimitry Ivanov
						Dimitry Ivanov