Working on documentation. Table rendering sample
This commit is contained in:
		
							parent
							
								
									cc75a92c7f
								
							
						
					
					
						commit
						96ca96fa70
					
				| @ -1 +1,125 @@ | ||||
| # Images | ||||
| 
 | ||||
| Starting with <Badge text="3.0.0" /> `Markwon` comes with `ImagesPlugin` | ||||
| which supports `http(s)`, `file` and `data` schemes and default media | ||||
| decoder (for simple images, no [SVG](/docs/image/svg.md) or [GIF](/docs/image/gif.md) which | ||||
| are defined in standalone modules). | ||||
| 
 | ||||
| ## ImagesPlugin | ||||
| 
 | ||||
| `ImagePlugin` takes care of _obtaining_ image resource, decoding it and displaying it in a `TextView`. | ||||
| 
 | ||||
| :::warning | ||||
| Although `core` artifact contains `ImagesPlugin` one must  | ||||
| still **explicitly** register the `ImagesPlugin` on resulting `Markwon` | ||||
| instance. | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(ImagesPlugin.create()) | ||||
| ``` | ||||
| ::: | ||||
| 
 | ||||
| There are 2 factory methods to obtain `ImagesPlugin`: | ||||
| * `ImagesPlugin#create(Context)` | ||||
| * `ImagesPlugin#createWithAssets(Context)` | ||||
| 
 | ||||
| The first one `#create(Context)` configures: | ||||
| * `FileSchemeHandler` that allows obtaining images from `file://` uris | ||||
| * `DataUriSchemeHandler` that allows _inlining_ images with `data:`  | ||||
|   scheme (`data:image/svg+xml;base64,MTIz`) | ||||
| * `NetworkSchemeHandler` that allows obtaining images from `http://` and `https://` uris | ||||
|   (internally it uses `HttpURLConnection`) | ||||
| * `ImageMediaDecoder` which _tries_ to decode all encountered images as regular ones (png, jpg, etc) | ||||
| 
 | ||||
| The second one `#createWithAssets(Context)` does the same but also adds support | ||||
| for images that reside in `assets` folder of your application and | ||||
| referenced by `file:///android_asset/{path}` uri. | ||||
| 
 | ||||
| `ImagesPlugin` also _prepares_ a TextView to display images. Due to asynchronous | ||||
| nature of image loading, there must be a way to invalidate resulting Spanned  | ||||
| content after an image is loaded. | ||||
| 
 | ||||
| :::warning | ||||
| Images come with few limitations. For of all, they work with a **TextView only**. | ||||
| This is due to the fact that there is no way to invalidate a `Spanned` content | ||||
| by itself (without context in which it is displayed). So, if `Markwon` is used, | ||||
| for example, to display a `Toast` with an image: | ||||
| 
 | ||||
| ```java | ||||
| final Spanned spanned = markwon.toMarkdown("Hello "); | ||||
| Toast.makeText(context, spanned, Toast.LENGTH_LONG).show(); | ||||
| ``` | ||||
| 
 | ||||
| Image _probably_ won't be displayed. As a workaround for `Toast` a custom `View` | ||||
| can be used: | ||||
| 
 | ||||
| ```java | ||||
| final Spanned spanned = markwon.toMarkdown("Hello "); | ||||
| 
 | ||||
| final View view = createToastView(); | ||||
| final TextView textView = view.findViewById(R.id.text_view); | ||||
| markwon.setParsedMarkdown(textView, spanned); | ||||
| 
 | ||||
| final Toast toast = new Toast(context); | ||||
| toast.setView(view); | ||||
| // other Toast configurations | ||||
| toast.show(); | ||||
| ``` | ||||
| ::: | ||||
| 
 | ||||
| ## SchemeHandler | ||||
| 
 | ||||
| To add support for different schemes (or customize provided) a `SchemeHandler` must be used. | ||||
| 
 | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(ImagesPlugin.create(context)) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configureImages(@NonNull AsyncDrawableLoader.Builder builder) { | ||||
|                 // example only, Markwon doesn't come with a ftp scheme handler | ||||
|                 builder.addSchemeHandler("ftp", new FtpSchemeHandler()); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| 
 | ||||
| It's a class to _convert_ an URI into an `InputStream`: | ||||
| 
 | ||||
| ```java | ||||
| public abstract class SchemeHandler { | ||||
| 
 | ||||
|     @Nullable | ||||
|     public abstract ImageItem handle(@NonNull String raw, @NonNull Uri uri); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| `ImageItem` is a holder class for resulting `InputStream` and (optional) | ||||
| content type: | ||||
| 
 | ||||
| ```java | ||||
| public class ImageItem { | ||||
| 
 | ||||
|     private final String contentType; | ||||
|     private final InputStream inputStream; | ||||
| 
 | ||||
|     /* rest omitted */ | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| Based on `contentType` returned a corresponding `MediaDecoder` will be matched. | ||||
| If no `MediaDecoder` can handle given `contentType` then a default media decoder will | ||||
| be used. | ||||
| 
 | ||||
| ## MediaDecoder | ||||
| 
 | ||||
| :::tip | ||||
| If you are using [html](/docs/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%"> | ||||
| ``` | ||||
| ::: | ||||
| @ -2,6 +2,23 @@ | ||||
| 
 | ||||
| Here is the list of properties that can be configured via `MarkwonTheme.Builder` class.  | ||||
| 
 | ||||
| :::tip | ||||
| Starting with <Badge text="3.0.0" /> there is no need to manually construct a `MarkwonTheme`. | ||||
| Instead a `Plugin` should be used: | ||||
| ```java | ||||
| final Markwon markwon = Markwon.builder(context) | ||||
|         .usePlugin(new AbstractMarkwonPlugin() { | ||||
|             @Override | ||||
|             public void configureTheme(@NonNull MarkwonTheme.Builder builder) { | ||||
|                 builder | ||||
|                         .codeTextColor(Color.BLACK) | ||||
|                         .codeBackgroundColor(Color.GREEN); | ||||
|             } | ||||
|         }) | ||||
|         .build(); | ||||
| ``` | ||||
| ::: | ||||
| 
 | ||||
| ## Link color | ||||
| 
 | ||||
| Controls the color of a [link](#) | ||||
|  | ||||
| @ -10,7 +10,6 @@ import org.commonmark.ext.gfm.tables.TableHead; | ||||
| import org.commonmark.ext.gfm.tables.TableRow; | ||||
| import org.commonmark.node.AbstractVisitor; | ||||
| import org.commonmark.node.CustomNode; | ||||
| import org.commonmark.node.Node; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| @ -160,20 +159,12 @@ public class Table { | ||||
| 
 | ||||
|                 final TableCell cell = (TableCell) customNode; | ||||
| 
 | ||||
|                 final Node firstChild = cell.getFirstChild(); | ||||
| 
 | ||||
|                 // need to investigate why... (most likely initial node is modified by someone) | ||||
|                 if (firstChild != null) { | ||||
| 
 | ||||
|                 if (pendingRow == null) { | ||||
|                     pendingRow = new ArrayList<>(2); | ||||
|                 } | ||||
| 
 | ||||
|                     // let's TRY to not visit this node but instead try to render its first child | ||||
| 
 | ||||
|                     pendingRow.add(new Table.Column(alignment(cell.getAlignment()), markwon.render(firstChild))); | ||||
|                 pendingRow.add(new Table.Column(alignment(cell.getAlignment()), markwon.render(cell))); | ||||
|                 pendingRowIsHeader = cell.isHeader(); | ||||
|                 } | ||||
| 
 | ||||
|                 return; | ||||
|             } | ||||
| @ -215,6 +206,4 @@ public class Table { | ||||
|             return out; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -67,7 +67,7 @@ public abstract class MarkwonAdapter extends RecyclerView.Adapter<MarkwonAdapter | ||||
|      * @see SimpleEntry | ||||
|      */ | ||||
|     @NonNull | ||||
|     public static MarkwonAdapter create(@NonNull Entry<? extends Holder, ? extends Node> defaultEntry) { | ||||
|     public static MarkwonAdapter create(@NonNull Entry<? extends Node, ? extends Holder> defaultEntry) { | ||||
|         return new MarkwonAdapterImpl.BuilderImpl().defaultEntry(defaultEntry).build(); | ||||
|     } | ||||
| 
 | ||||
| @ -109,7 +109,7 @@ public abstract class MarkwonAdapter extends RecyclerView.Adapter<MarkwonAdapter | ||||
|         @NonNull | ||||
|         <N extends Node> Builder include( | ||||
|                 @NonNull Class<N> node, | ||||
|                 @NonNull Entry<? extends Holder, ? super N> entry); | ||||
|                 @NonNull Entry<? super N, ? extends Holder> entry); | ||||
| 
 | ||||
|         /** | ||||
|          * Specify which {@link Entry} to use for all non-explicitly registered nodes | ||||
| @ -119,7 +119,7 @@ public abstract class MarkwonAdapter extends RecyclerView.Adapter<MarkwonAdapter | ||||
|          * @see SimpleEntry | ||||
|          */ | ||||
|         @NonNull | ||||
|         Builder defaultEntry(@NonNull Entry<? extends Holder, ? extends Node> defaultEntry); | ||||
|         Builder defaultEntry(@NonNull Entry<? extends Node, ? extends Holder> defaultEntry); | ||||
| 
 | ||||
|         /** | ||||
|          * Specify which layout {@link SimpleEntry} will use to render all non-explicitly | ||||
| @ -156,7 +156,7 @@ public abstract class MarkwonAdapter extends RecyclerView.Adapter<MarkwonAdapter | ||||
|     /** | ||||
|      * @see SimpleEntry | ||||
|      */ | ||||
|     public interface Entry<H extends Holder, N extends Node> { | ||||
|     public interface Entry<N extends Node, H extends Holder> { | ||||
| 
 | ||||
|         @NonNull | ||||
|         H createHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent); | ||||
|  | ||||
| @ -15,8 +15,8 @@ import ru.noties.markwon.Markwon; | ||||
| 
 | ||||
| class MarkwonAdapterImpl extends MarkwonAdapter { | ||||
| 
 | ||||
|     private final SparseArray<Entry<Holder, Node>> entries; | ||||
|     private final Entry<Holder, Node> defaultEntry; | ||||
|     private final SparseArray<Entry<Node, Holder>> entries; | ||||
|     private final Entry<Node, Holder> defaultEntry; | ||||
|     private final Reducer reducer; | ||||
| 
 | ||||
|     private LayoutInflater layoutInflater; | ||||
| @ -26,8 +26,8 @@ class MarkwonAdapterImpl extends MarkwonAdapter { | ||||
| 
 | ||||
|     @SuppressWarnings("WeakerAccess") | ||||
|     MarkwonAdapterImpl( | ||||
|             @NonNull SparseArray<Entry<Holder, Node>> entries, | ||||
|             @NonNull Entry<Holder, Node> defaultEntry, | ||||
|             @NonNull SparseArray<Entry<Node, Holder>> entries, | ||||
|             @NonNull Entry<Node, Holder> defaultEntry, | ||||
|             @NonNull Reducer reducer) { | ||||
|         this.entries = entries; | ||||
|         this.defaultEntry = defaultEntry; | ||||
| @ -67,7 +67,7 @@ class MarkwonAdapterImpl extends MarkwonAdapter { | ||||
|             layoutInflater = LayoutInflater.from(parent.getContext()); | ||||
|         } | ||||
| 
 | ||||
|         final Entry<Holder, Node> entry = getEntry(viewType); | ||||
|         final Entry<Node, Holder> entry = getEntry(viewType); | ||||
| 
 | ||||
|         return entry.createHolder(layoutInflater, parent); | ||||
|     } | ||||
| @ -78,7 +78,7 @@ class MarkwonAdapterImpl extends MarkwonAdapter { | ||||
|         final Node node = nodes.get(position); | ||||
|         final int viewType = getNodeViewType(node.getClass()); | ||||
| 
 | ||||
|         final Entry<Holder, Node> entry = getEntry(viewType); | ||||
|         final Entry<Node, Holder> entry = getEntry(viewType); | ||||
| 
 | ||||
|         entry.bindHolder(markwon, holder, node); | ||||
|     } | ||||
| @ -107,7 +107,7 @@ class MarkwonAdapterImpl extends MarkwonAdapter { | ||||
|     public long getItemId(int position) { | ||||
|         final Node node = nodes.get(position); | ||||
|         final int type = getNodeViewType(node.getClass()); | ||||
|         final Entry<Holder, Node> entry = getEntry(type); | ||||
|         final Entry<Node, Holder> entry = getEntry(type); | ||||
|         return entry.id(node); | ||||
|     } | ||||
| 
 | ||||
| @ -122,7 +122,7 @@ class MarkwonAdapterImpl extends MarkwonAdapter { | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     private Entry<Holder, Node> getEntry(int viewType) { | ||||
|     private Entry<Node, Holder> getEntry(int viewType) { | ||||
|         return viewType == 0 | ||||
|                 ? defaultEntry | ||||
|                 : entries.get(viewType); | ||||
| @ -130,26 +130,26 @@ class MarkwonAdapterImpl extends MarkwonAdapter { | ||||
| 
 | ||||
|     static class BuilderImpl implements Builder { | ||||
| 
 | ||||
|         private final SparseArray<Entry<Holder, Node>> entries = new SparseArray<>(3); | ||||
|         private final SparseArray<Entry<Node, Holder>> entries = new SparseArray<>(3); | ||||
| 
 | ||||
|         private Entry<Holder, Node> defaultEntry; | ||||
|         private Entry<Node, Holder> defaultEntry; | ||||
|         private Reducer reducer; | ||||
| 
 | ||||
|         @NonNull | ||||
|         @Override | ||||
|         public <N extends Node> Builder include( | ||||
|                 @NonNull Class<N> node, | ||||
|                 @NonNull Entry<? extends Holder, ? super N> entry) { | ||||
|                 @NonNull Entry<? super N, ? extends Holder> entry) { | ||||
|             //noinspection unchecked | ||||
|             entries.append(node.hashCode(), (Entry<Holder, Node>) entry); | ||||
|             entries.append(node.hashCode(), (Entry<Node, Holder>) entry); | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         @NonNull | ||||
|         @Override | ||||
|         public Builder defaultEntry(@NonNull Entry<? extends Holder, ? extends Node> defaultEntry) { | ||||
|         public Builder defaultEntry(@NonNull Entry<? extends Node, ? extends Holder> defaultEntry) { | ||||
|             //noinspection unchecked | ||||
|             this.defaultEntry = (Entry<Holder, Node>) defaultEntry; | ||||
|             this.defaultEntry = (Entry<Node, Holder>) defaultEntry; | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
| @ -157,7 +157,7 @@ class MarkwonAdapterImpl extends MarkwonAdapter { | ||||
|         @Override | ||||
|         public Builder defaultEntry(int layoutResId) { | ||||
|             //noinspection unchecked | ||||
|             this.defaultEntry = (Entry<Holder, Node>) (Entry) new SimpleEntry(layoutResId); | ||||
|             this.defaultEntry = (Entry<Node, Holder>) (Entry) new SimpleEntry(layoutResId); | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
| @ -174,7 +174,7 @@ class MarkwonAdapterImpl extends MarkwonAdapter { | ||||
| 
 | ||||
|             if (defaultEntry == null) { | ||||
|                 //noinspection unchecked | ||||
|                 defaultEntry = (Entry<Holder, Node>) (Entry) new SimpleEntry(); | ||||
|                 defaultEntry = (Entry<Node, Holder>) (Entry) new SimpleEntry(); | ||||
|             } | ||||
| 
 | ||||
|             if (reducer == null) { | ||||
|  | ||||
| @ -21,7 +21,7 @@ import ru.noties.markwon.Markwon; | ||||
|  * @since 3.0.0 | ||||
|  */ | ||||
| @SuppressWarnings("WeakerAccess") | ||||
| public class SimpleEntry implements MarkwonAdapter.Entry<SimpleEntry.Holder, Node> { | ||||
| public class SimpleEntry implements MarkwonAdapter.Entry<Node, SimpleEntry.Holder> { | ||||
| 
 | ||||
|     public static final Spannable.Factory NO_COPY_SPANNABLE_FACTORY = new NoCopySpannableFactory(); | ||||
| 
 | ||||
|  | ||||
| @ -8,8 +8,8 @@ | ||||
|     <application | ||||
|         android:allowBackup="true" | ||||
|         android:icon="@mipmap/ic_launcher" | ||||
|         android:roundIcon="@mipmap/ic_launcher" | ||||
|         android:label="@string/app_name" | ||||
|         android:roundIcon="@mipmap/ic_launcher" | ||||
|         android:theme="@style/AppTheme" | ||||
|         tools:ignore="AllowBackup,GoogleAppIndexingWarning"> | ||||
| 
 | ||||
|  | ||||
| @ -11,27 +11,29 @@ import android.support.v7.widget.RecyclerView; | ||||
| import android.text.TextUtils; | ||||
| 
 | ||||
| import org.commonmark.ext.gfm.tables.TableBlock; | ||||
| import org.commonmark.ext.gfm.tables.TablesExtension; | ||||
| import org.commonmark.node.FencedCodeBlock; | ||||
| import org.commonmark.parser.Parser; | ||||
| 
 | ||||
| import java.io.BufferedReader; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.io.InputStreamReader; | ||||
| import java.util.Collections; | ||||
| 
 | ||||
| import ru.noties.debug.AndroidLogDebugOutput; | ||||
| import ru.noties.debug.Debug; | ||||
| import ru.noties.markwon.AbstractMarkwonPlugin; | ||||
| import ru.noties.markwon.Markwon; | ||||
| import ru.noties.markwon.MarkwonConfiguration; | ||||
| import ru.noties.markwon.MarkwonVisitor; | ||||
| import ru.noties.markwon.core.CorePlugin; | ||||
| import ru.noties.markwon.ext.tables.TablePlugin; | ||||
| import ru.noties.markwon.html.HtmlPlugin; | ||||
| import ru.noties.markwon.image.ImagesPlugin; | ||||
| import ru.noties.markwon.image.svg.SvgPlugin; | ||||
| import ru.noties.markwon.recycler.MarkwonAdapter; | ||||
| import ru.noties.markwon.recycler.SimpleEntry; | ||||
| import ru.noties.markwon.sample.R; | ||||
| import ru.noties.markwon.syntax.SyntaxHighlightPlugin; | ||||
| import ru.noties.markwon.urlprocessor.UrlProcessor; | ||||
| import ru.noties.markwon.urlprocessor.UrlProcessorRelativeToAbsolute; | ||||
| 
 | ||||
| @ -54,7 +56,7 @@ public class RecyclerActivity extends Activity { | ||||
|                 // with `@+id/text` id | ||||
|                 .include(FencedCodeBlock.class, new SimpleEntry(R.layout.adapter_fenced_code_block)) | ||||
|                 // create own implementation of entry for different rendering | ||||
|                 .include(TableBlock.class, new TableEntry()) | ||||
|                 .include(TableBlock.class, new TableEntry2()) | ||||
|                 // specify default entry (for all other blocks) | ||||
|                 .defaultEntry(new SimpleEntry(R.layout.adapter_default_entry)) | ||||
|                 .build(); | ||||
| @ -81,15 +83,35 @@ public class RecyclerActivity extends Activity { | ||||
|                 .usePlugin(CorePlugin.create()) | ||||
|                 .usePlugin(ImagesPlugin.createWithAssets(context)) | ||||
|                 .usePlugin(SvgPlugin.create(context.getResources())) | ||||
|                 // although we will be rendering table differently we still need | ||||
|                 // to register commonmark-java tables extension (which TablePlugin does) | ||||
|                 .usePlugin(TablePlugin.create(context)) | ||||
|                 .usePlugin(new AbstractMarkwonPlugin() { | ||||
|                     @Override | ||||
|                     public void configureParser(@NonNull Parser.Builder builder) { | ||||
|                         // it's important NOT to use TablePlugin | ||||
|                         // the only thing we want from it is commonmark-java parser extension | ||||
|                         builder.extensions(Collections.singleton(TablesExtension.create())); | ||||
|                     } | ||||
|                 }) | ||||
|                 .usePlugin(HtmlPlugin.create()) | ||||
| //                .usePlugin(SyntaxHighlightPlugin.create()) | ||||
|                 .usePlugin(new AbstractMarkwonPlugin() { | ||||
|                     @Override | ||||
|                     public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) { | ||||
|                         builder.urlProcessor(new UrlProcessorInitialReadme()); | ||||
|                     } | ||||
| 
 | ||||
|                     @Override | ||||
|                     public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) { | ||||
|                         builder.on(FencedCodeBlock.class, (visitor, fencedCodeBlock) -> { | ||||
|                             // we actually won't be applying code spans here, as our custom view will | ||||
|                             // draw background and apply mono typeface | ||||
|                             // | ||||
|                             // NB the `trim` operation on literal (as code will have a new line at the end) | ||||
|                             final CharSequence code = visitor.configuration() | ||||
|                                     .syntaxHighlight() | ||||
|                                     .highlight(fencedCodeBlock.getInfo(), fencedCodeBlock.getLiteral().trim()); | ||||
|                             visitor.builder().append(code); | ||||
|                         }); | ||||
|                     } | ||||
|                 }) | ||||
|                 .build(); | ||||
|     } | ||||
|  | ||||
| @ -17,7 +17,7 @@ import ru.noties.markwon.recycler.MarkwonAdapter; | ||||
| import ru.noties.markwon.sample.R; | ||||
| 
 | ||||
| // do not use in real applications, this is just a showcase | ||||
| public class TableEntry implements MarkwonAdapter.Entry<TableEntry.TableNodeHolder, TableBlock> { | ||||
| public class TableEntry implements MarkwonAdapter.Entry<TableBlock, TableEntry.TableNodeHolder> { | ||||
| 
 | ||||
|     private final Map<TableBlock, Table> cache = new HashMap<>(2); | ||||
| 
 | ||||
|  | ||||
| @ -0,0 +1,121 @@ | ||||
| package ru.noties.markwon.sample.recycler; | ||||
| 
 | ||||
| import android.annotation.SuppressLint; | ||||
| import android.content.Context; | ||||
| import android.support.annotation.NonNull; | ||||
| import android.view.Gravity; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.TableLayout; | ||||
| import android.widget.TableRow; | ||||
| import android.widget.TextView; | ||||
| 
 | ||||
| import org.commonmark.ext.gfm.tables.TableBlock; | ||||
| 
 | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
| 
 | ||||
| import ru.noties.markwon.Markwon; | ||||
| import ru.noties.markwon.ext.tables.Table; | ||||
| import ru.noties.markwon.recycler.MarkwonAdapter; | ||||
| import ru.noties.markwon.sample.R; | ||||
| 
 | ||||
| public class TableEntry2 implements MarkwonAdapter.Entry<TableBlock, TableEntry2.TableHolder> { | ||||
| 
 | ||||
|     private final Map<TableBlock, Table> map = new HashMap<>(3); | ||||
| 
 | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public TableHolder createHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) { | ||||
|         return new TableHolder(inflater.inflate(R.layout.adapter_table_block_2, parent, false)); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void bindHolder(@NonNull Markwon markwon, @NonNull TableHolder holder, @NonNull TableBlock node) { | ||||
| 
 | ||||
|         Table table = map.get(node); | ||||
|         if (table == null) { | ||||
|             table = Table.parse(markwon, node); | ||||
|             map.put(node, table); | ||||
|         } | ||||
| 
 | ||||
|         // check if this exact TableBlock was already | ||||
|         final TableLayout layout = holder.layout; | ||||
|         if (table == null | ||||
|                 || table == layout.getTag(R.id.table_layout)) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         layout.setTag(R.id.table_layout, table); | ||||
|         layout.removeAllViews(); | ||||
|         layout.setBackgroundResource(R.drawable.bg_table_cell); | ||||
| 
 | ||||
|         final Context context = layout.getContext(); | ||||
|         final LayoutInflater inflater = LayoutInflater.from(context); | ||||
| 
 | ||||
|         TableRow tableRow; | ||||
|         TextView textView; | ||||
| 
 | ||||
|         for (Table.Row row : table.rows()) { | ||||
|             tableRow = new TableRow(context); | ||||
|             for (Table.Column column : row.columns()) { | ||||
|                 textView = (TextView) inflater.inflate(R.layout.view_table_entry_cell, tableRow, false); | ||||
|                 textView.setGravity(textGravity(column.alignment())); | ||||
|                 markwon.setParsedMarkdown(textView, column.content()); | ||||
|                 textView.getPaint().setFakeBoldText(row.header()); | ||||
|                 textView.setBackgroundResource(R.drawable.bg_table_cell); | ||||
|                 tableRow.addView(textView); | ||||
|             } | ||||
|             layout.addView(tableRow); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public long id(@NonNull TableBlock node) { | ||||
|         return node.hashCode(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void clear() { | ||||
|         map.clear(); | ||||
|     } | ||||
| 
 | ||||
|     static class TableHolder extends MarkwonAdapter.Holder { | ||||
| 
 | ||||
|         final TableLayout layout; | ||||
| 
 | ||||
|         TableHolder(@NonNull View itemView) { | ||||
|             super(itemView); | ||||
| 
 | ||||
|             this.layout = requireView(R.id.table_layout); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // we will use gravity instead of textAlignment because min sdk is 16 (textAlignment starts at 17) | ||||
|     @SuppressLint("RtlHardcoded") | ||||
|     private static int textGravity(@NonNull Table.Alignment alignment) { | ||||
| 
 | ||||
|         final int gravity; | ||||
| 
 | ||||
|         switch (alignment) { | ||||
| 
 | ||||
|             case LEFT: | ||||
|                 gravity = Gravity.LEFT; | ||||
|                 break; | ||||
| 
 | ||||
|             case CENTER: | ||||
|                 gravity = Gravity.CENTER_HORIZONTAL; | ||||
|                 break; | ||||
| 
 | ||||
|             case RIGHT: | ||||
|                 gravity = Gravity.RIGHT; | ||||
|                 break; | ||||
| 
 | ||||
|             default: | ||||
|                 throw new IllegalStateException("Unknown table alignment: " + alignment); | ||||
|         } | ||||
| 
 | ||||
|         return gravity | Gravity.CENTER_VERTICAL; | ||||
|     } | ||||
| } | ||||
| @ -54,7 +54,6 @@ public class TableEntryView extends LinearLayout { | ||||
| 
 | ||||
|                 rowEvenBackgroundColor = array.getColor(R.styleable.TableEntryView_tev_rowEvenBackgroundColor, 0); | ||||
| 
 | ||||
| 
 | ||||
|                 final int stroke = array.getDimensionPixelSize(R.styleable.TableEntryView_tev_borderWidth, 0); | ||||
| 
 | ||||
|                 // half of requested | ||||
| @ -124,6 +123,8 @@ public class TableEntryView extends LinearLayout { | ||||
|             textView.setText(column.content()); | ||||
|             textView.getPaint().setFakeBoldText(row.header()); | ||||
|         } | ||||
| 
 | ||||
|         group.requestLayout(); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <TextView xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     android:id="@+id/text" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="wrap_content" | ||||
| @ -10,4 +11,5 @@ | ||||
|     android:paddingBottom="8dip" | ||||
|     android:textAppearance="?android:attr/textAppearanceMedium" | ||||
|     android:textColor="#000" | ||||
|     android:textSize="16sp" /> | ||||
|     android:textSize="16sp" | ||||
|     tools:text="Hello" /> | ||||
| @ -14,11 +14,15 @@ | ||||
|         android:id="@+id/text" | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:background="#0f000000" | ||||
|         android:fontFamily="monospace" | ||||
|         android:lineSpacingExtra="2dip" | ||||
|         android:paddingLeft="16dip" | ||||
|         android:paddingTop="8dip" | ||||
|         android:paddingRight="16dip" | ||||
|         android:paddingBottom="8dip" | ||||
|         android:textAppearance="?android:attr/textAppearanceMedium" | ||||
|         android:textSize="16sp" | ||||
|         tools:text="# Hello there! and taskjs" /> | ||||
|         android:textSize="14sp" | ||||
|         tools:text="# Hello there! and tasks" /> | ||||
| 
 | ||||
| </HorizontalScrollView> | ||||
| @ -5,7 +5,6 @@ | ||||
|     android:layout_height="wrap_content" | ||||
|     android:clipChildren="false" | ||||
|     android:clipToPadding="false" | ||||
|     android:fillViewport="true" | ||||
|     android:paddingLeft="16dip" | ||||
|     android:paddingTop="8dip" | ||||
|     android:paddingRight="16dip" | ||||
|  | ||||
| @ -0,0 +1,18 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="wrap_content" | ||||
|     android:clipChildren="false" | ||||
|     android:clipToPadding="false" | ||||
|     android:paddingLeft="16dip" | ||||
|     android:paddingTop="8dip" | ||||
|     android:paddingRight="16dip" | ||||
|     android:paddingBottom="8dip" | ||||
|     android:scrollbarStyle="outsideInset"> | ||||
| 
 | ||||
|     <TableLayout | ||||
|         android:id="@+id/table_layout" | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="wrap_content" /> | ||||
| 
 | ||||
| </HorizontalScrollView> | ||||
| @ -1,10 +1,10 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <TextView xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     android:layout_width="0px" | ||||
|     android:layout_width="wrap_content" | ||||
|     android:layout_height="match_parent" | ||||
|     android:layout_weight="1" | ||||
|     android:padding="4dip" | ||||
|     android:gravity="center_vertical" | ||||
|     android:padding="8dip" | ||||
|     android:textAppearance="?android:attr/textAppearanceMedium" | ||||
|     android:textColor="#000" | ||||
|     android:textSize="16sp" | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_width="wrap_content" | ||||
|     android:layout_height="wrap_content" | ||||
|     android:orientation="horizontal" /> | ||||
							
								
								
									
										6
									
								
								sample/src/main/res/drawable/bg_table_cell.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								sample/src/main/res/drawable/bg_table_cell.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <shape xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
|     <stroke | ||||
|         android:width="0.5dip" | ||||
|         android:color="#000" /> | ||||
| </shape> | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Dimitry Ivanov
						Dimitry Ivanov