Working with sample application
This commit is contained in:
		
							parent
							
								
									e01787982f
								
							
						
					
					
						commit
						6f8c1dfaee
					
				| @ -17,11 +17,15 @@ import java.util.Map; | ||||
| 
 | ||||
| import ru.noties.markwon.Markwon; | ||||
| 
 | ||||
| /** | ||||
|  * @since 3.0.0 | ||||
|  */ | ||||
| @SuppressWarnings("WeakerAccess") | ||||
| public class SimpleEntry implements MarkwonAdapter.Entry<SimpleEntry.Holder, Node> { | ||||
| 
 | ||||
|     private static final NoCopySpannableFactory FACTORY = new NoCopySpannableFactory(); | ||||
|     public static final Spannable.Factory NO_COPY_SPANNABLE_FACTORY = new NoCopySpannableFactory(); | ||||
| 
 | ||||
|     // small cache, maybe add pre-compute of text, also spannableFactory (so no copying of spans) | ||||
|     // small cache for already rendered nodes | ||||
|     private final Map<Node, Spanned> cache = new HashMap<>(); | ||||
| 
 | ||||
|     private final int layoutResId; | ||||
| @ -60,15 +64,15 @@ public class SimpleEntry implements MarkwonAdapter.Entry<SimpleEntry.Holder, Nod | ||||
|         cache.clear(); | ||||
|     } | ||||
| 
 | ||||
|     static class Holder extends MarkwonAdapter.Holder { | ||||
|     public static class Holder extends MarkwonAdapter.Holder { | ||||
| 
 | ||||
|         final TextView textView; | ||||
| 
 | ||||
|         Holder(@NonNull View itemView) { | ||||
|         protected Holder(@NonNull View itemView) { | ||||
|             super(itemView); | ||||
| 
 | ||||
|             this.textView = requireView(R.id.text); | ||||
|             this.textView.setSpannableFactory(FACTORY); | ||||
|             this.textView.setSpannableFactory(NO_COPY_SPANNABLE_FACTORY); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -170,6 +170,8 @@ class MarkwonBuilderImpl implements Markwon.Builder { | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // important thing here is to check if corePlugin is added | ||||
|         // add it _only_ if it's not present | ||||
|         if (hasCoreDependents && !hasCore) { | ||||
|             final List<MarkwonPlugin> out = new ArrayList<>(plugins.size() + 1); | ||||
|             // add default instance of CorePlugin | ||||
|  | ||||
| @ -0,0 +1,41 @@ | ||||
| package ru.noties.markwon; | ||||
| 
 | ||||
| import android.support.annotation.NonNull; | ||||
| import android.text.Spanned; | ||||
| import android.text.method.LinkMovementMethod; | ||||
| import android.text.method.MovementMethod; | ||||
| import android.widget.TextView; | ||||
| 
 | ||||
| /** | ||||
|  * @since 3.0.0 | ||||
|  */ | ||||
| public class MovementMethodPlugin extends AbstractMarkwonPlugin { | ||||
| 
 | ||||
|     /** | ||||
|      * Creates plugin that will ensure that there is movement method registered on a TextView. | ||||
|      * Uses Android system LinkMovementMethod as default | ||||
|      * | ||||
|      * @see #create(MovementMethod) | ||||
|      */ | ||||
|     @NonNull | ||||
|     public static MovementMethodPlugin create() { | ||||
|         return create(LinkMovementMethod.getInstance()); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     public static MovementMethodPlugin create(@NonNull MovementMethod movementMethod) { | ||||
|         return new MovementMethodPlugin(movementMethod); | ||||
|     } | ||||
| 
 | ||||
|     private final MovementMethod movementMethod; | ||||
| 
 | ||||
|     @SuppressWarnings("WeakerAccess") | ||||
|     MovementMethodPlugin(@NonNull MovementMethod movementMethod) { | ||||
|         this.movementMethod = movementMethod; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void beforeSetText(@NonNull TextView textView, @NonNull Spanned markdown) { | ||||
|         textView.setMovementMethod(movementMethod); | ||||
|     } | ||||
| } | ||||
| @ -31,6 +31,11 @@ public class ImagesPlugin extends AbstractMarkwonPlugin { | ||||
|         return new ImagesPlugin(context, false); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Special scheme that is used {@code file:///android_asset/} | ||||
|      * @param context | ||||
|      * @return | ||||
|      */ | ||||
|     @NonNull | ||||
|     public static ImagesPlugin createWithAssets(@NonNull Context context) { | ||||
|         return new ImagesPlugin(context, true); | ||||
|  | ||||
| @ -2,11 +2,16 @@ package ru.noties.markwon.sample.extension.recycler; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.content.res.TypedArray; | ||||
| import android.graphics.Canvas; | ||||
| import android.graphics.Color; | ||||
| import android.graphics.Paint; | ||||
| import android.graphics.Rect; | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.text.SpannedString; | ||||
| import android.util.AttributeSet; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.LinearLayout; | ||||
| import android.widget.TextView; | ||||
| @ -19,6 +24,10 @@ import ru.noties.markwon.sample.extension.R; | ||||
| 
 | ||||
| public class TableEntryView extends LinearLayout { | ||||
| 
 | ||||
|     // paint and rect to draw borders | ||||
|     private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); | ||||
|     private final Rect rect = new Rect(); | ||||
| 
 | ||||
|     private LayoutInflater inflater; | ||||
| 
 | ||||
|     private int rowEvenBackgroundColor; | ||||
| @ -43,6 +52,18 @@ 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 | ||||
|                 final float strokeWidth = stroke > 0 | ||||
|                         ? stroke / 2.F | ||||
|                         : context.getResources().getDisplayMetrics().density / 2.F; | ||||
| 
 | ||||
|                 paint.setStyle(Paint.Style.STROKE); | ||||
|                 paint.setStrokeWidth(strokeWidth); | ||||
|                 paint.setColor(array.getColor(R.styleable.TableEntryView_tev_borderColor, Color.BLACK)); | ||||
| 
 | ||||
|                 if (isInEditMode()) { | ||||
|                     final String data = array.getString(R.styleable.TableEntryView_tev_debugData); | ||||
|                     if (data != null) { | ||||
| @ -67,6 +88,8 @@ public class TableEntryView extends LinearLayout { | ||||
|                 array.recycle(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         setWillNotDraw(false); | ||||
|     } | ||||
| 
 | ||||
|     public void setTable(@NonNull Table table) { | ||||
| @ -133,6 +156,35 @@ public class TableEntryView extends LinearLayout { | ||||
|         return (TextView) group.getChildAt(index); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void onDraw(Canvas canvas) { | ||||
|         super.onDraw(canvas); | ||||
| 
 | ||||
|         final int rows = getChildCount(); | ||||
|         if (rows == 0) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         // first draw the whole border | ||||
|         rect.set(0, 0, getWidth(), getHeight()); | ||||
|         canvas.drawRect(rect, paint); | ||||
| 
 | ||||
|         ViewGroup group; | ||||
|         View view; | ||||
| 
 | ||||
|         int top; | ||||
| 
 | ||||
|         for (int row = 0; row < rows; row++) { | ||||
|             group = (ViewGroup) getChildAt(row); | ||||
|             top = group.getTop(); | ||||
|             for (int col = 0, cols = group.getChildCount(); col < cols; col++) { | ||||
|                 view = group.getChildAt(col); | ||||
|                 rect.set(view.getLeft(), top + view.getTop(), view.getRight(), top + view.getBottom()); | ||||
|                 canvas.drawRect(rect, paint); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static int textAlignment(@NonNull Table.Alignment alignment) { | ||||
|         final int out; | ||||
|         switch (alignment) { | ||||
|  | ||||
| @ -1,21 +1,23 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="wrap_content" | ||||
|     android:clipChildren="false" | ||||
|     android:clipToPadding="false" | ||||
|     android:fillViewport="true" | ||||
|     android:paddingLeft="16dip" | ||||
|     android:paddingTop="8dip" | ||||
|     android:paddingRight="16dip" | ||||
|     android:paddingBottom="8dip"> | ||||
| <!--<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"--> | ||||
|     <!--xmlns:app="http://schemas.android.com/apk/res-auto"--> | ||||
|     <!--android:layout_width="match_parent"--> | ||||
|     <!--android:layout_height="wrap_content"--> | ||||
|     <!--android:clipChildren="false"--> | ||||
|     <!--android:clipToPadding="false"--> | ||||
|     <!--android:fillViewport="true"--> | ||||
|     <!--android:paddingLeft="16dip"--> | ||||
|     <!--android:paddingTop="8dip"--> | ||||
|     <!--android:paddingRight="16dip"--> | ||||
|     <!--android:paddingBottom="8dip">--> | ||||
| 
 | ||||
|     <ru.noties.markwon.sample.extension.recycler.TableEntryView | ||||
|         xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|         xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|         android:id="@+id/table_entry" | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content" | ||||
|         app:tev_debugData="head1,head2,head3|col1,col2,col3|col1,col2,col3|col1,col2,col3" | ||||
|         app:tev_rowEvenBackgroundColor="#40ff0000" /> | ||||
| 
 | ||||
| </HorizontalScrollView> | ||||
| <!--</HorizontalScrollView>--> | ||||
| @ -3,6 +3,8 @@ | ||||
| 
 | ||||
|     <declare-styleable name="TableEntryView"> | ||||
|         <attr name="tev_rowEvenBackgroundColor" format="color" /> | ||||
|         <attr name="tev_borderColor" format="color" /> | ||||
|         <attr name="tev_borderWidth" format="dimension" /> | ||||
|         <attr name="tev_debugData" format="string" /> | ||||
|     </declare-styleable> | ||||
| </resources> | ||||
| @ -29,6 +29,14 @@ android { | ||||
|         targetCompatibility JavaVersion.VERSION_1_8 | ||||
|         sourceCompatibility JavaVersion.VERSION_1_8 | ||||
|     } | ||||
| 
 | ||||
|     sourceSets { | ||||
|         main { | ||||
|             // let's use different res directory so sample will have _isolated_ resources from others | ||||
|             res.srcDirs += [ './src/main/recycler/res' ] | ||||
|             assets.srcDirs += ['./src/main/recycler/assets'] | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| dependencies { | ||||
| @ -41,6 +49,7 @@ dependencies { | ||||
|     implementation project(':markwon-image-gif') | ||||
|     implementation project(':markwon-image-svg') | ||||
|     implementation project(':markwon-syntax-highlight') | ||||
|     implementation project(':markwon-recycler') | ||||
| 
 | ||||
|     deps.with { | ||||
|         implementation it['support-recycler-view'] | ||||
|  | ||||
| @ -3,6 +3,8 @@ | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     package="ru.noties.markwon.sample"> | ||||
| 
 | ||||
|     <uses-permission android:name="android.permission.INTERNET" /> | ||||
| 
 | ||||
|     <application | ||||
|         android:allowBackup="true" | ||||
|         android:icon="@mipmap/ic_launcher" | ||||
| @ -21,6 +23,8 @@ | ||||
|         <activity android:name=".core.CoreActivity" /> | ||||
|         <activity android:name=".latex.LatexActivity" /> | ||||
|         <activity android:name=".customextension.CustomExtensionActivity" /> | ||||
|         <activity android:name=".basicplugins.BasicPluginsActivity" /> | ||||
|         <activity android:name=".recycler.RecyclerActivity" /> | ||||
| 
 | ||||
|     </application> | ||||
| 
 | ||||
|  | ||||
| @ -17,9 +17,11 @@ import ru.noties.adapt.OnClickViewProcessor; | ||||
| import ru.noties.debug.AndroidLogDebugOutput; | ||||
| import ru.noties.debug.Debug; | ||||
| import ru.noties.markwon.Markwon; | ||||
| import ru.noties.markwon.sample.basicplugins.BasicPluginsActivity; | ||||
| import ru.noties.markwon.sample.core.CoreActivity; | ||||
| import ru.noties.markwon.sample.customextension.CustomExtensionActivity; | ||||
| import ru.noties.markwon.sample.latex.LatexActivity; | ||||
| import ru.noties.markwon.sample.recycler.RecyclerActivity; | ||||
| 
 | ||||
| public class MainActivity extends Activity { | ||||
| 
 | ||||
| @ -79,6 +81,10 @@ public class MainActivity extends Activity { | ||||
|                 activity = CoreActivity.class; | ||||
|                 break; | ||||
| 
 | ||||
|             case BASIC_PLUGINS: | ||||
|                 activity = BasicPluginsActivity.class; | ||||
|                 break; | ||||
| 
 | ||||
|             case LATEX: | ||||
|                 activity = LatexActivity.class; | ||||
|                 break; | ||||
| @ -87,6 +93,10 @@ public class MainActivity extends Activity { | ||||
|                 activity = CustomExtensionActivity.class; | ||||
|                 break; | ||||
| 
 | ||||
|             case RECYCLER: | ||||
|                 activity = RecyclerActivity.class; | ||||
|                 break; | ||||
| 
 | ||||
|             default: | ||||
|                 throw new IllegalStateException("No Activity is associated with sample-item: " + item); | ||||
|         } | ||||
|  | ||||
| @ -7,10 +7,14 @@ public enum SampleItem { | ||||
|     // all usages of markwon without plugins (parse, render, setMarkwon, etc) | ||||
|     CORE(R.string.sample_core), | ||||
| 
 | ||||
|     BASIC_PLUGINS(R.string.sample_basic_plugins), | ||||
| 
 | ||||
|     LATEX(R.string.sample_latex), | ||||
| 
 | ||||
|     CUSTOM_EXTENSION(R.string.sample_custom_extension), | ||||
| 
 | ||||
|     RECYCLER(R.string.sample_recycler), | ||||
| 
 | ||||
|     ; | ||||
| 
 | ||||
|     private final int textResId; | ||||
|  | ||||
| @ -0,0 +1,204 @@ | ||||
| package ru.noties.markwon.sample.basicplugins; | ||||
| 
 | ||||
| import android.app.Activity; | ||||
| import android.graphics.Color; | ||||
| import android.net.Uri; | ||||
| import android.os.Bundle; | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.text.TextUtils; | ||||
| import android.text.style.ForegroundColorSpan; | ||||
| import android.widget.TextView; | ||||
| 
 | ||||
| import org.commonmark.node.Heading; | ||||
| import org.commonmark.node.Paragraph; | ||||
| 
 | ||||
| import ru.noties.markwon.AbstractMarkwonPlugin; | ||||
| import ru.noties.markwon.Markwon; | ||||
| import ru.noties.markwon.MarkwonConfiguration; | ||||
| import ru.noties.markwon.MarkwonPlugin; | ||||
| import ru.noties.markwon.MarkwonSpansFactory; | ||||
| import ru.noties.markwon.MarkwonVisitor; | ||||
| import ru.noties.markwon.MovementMethodPlugin; | ||||
| import ru.noties.markwon.core.MarkwonTheme; | ||||
| import ru.noties.markwon.image.AsyncDrawableLoader; | ||||
| import ru.noties.markwon.image.ImageItem; | ||||
| import ru.noties.markwon.image.ImagesPlugin; | ||||
| import ru.noties.markwon.image.SchemeHandler; | ||||
| import ru.noties.markwon.image.network.NetworkSchemeHandler; | ||||
| 
 | ||||
| public class BasicPluginsActivity extends Activity { | ||||
| 
 | ||||
|     private TextView textView; | ||||
| 
 | ||||
|     @Override | ||||
|     public void onCreate(@Nullable Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
| 
 | ||||
|         textView = new TextView(this); | ||||
|         setContentView(textView); | ||||
| 
 | ||||
|         step_1(); | ||||
| 
 | ||||
|         step_2(); | ||||
| 
 | ||||
|         step_3(); | ||||
| 
 | ||||
|         step_4(); | ||||
| 
 | ||||
|         step_5(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * In order to apply paragraph spans a custom plugin should be created (CorePlugin will take care | ||||
|      * of everything else). | ||||
|      * <p> | ||||
|      * Please note that when a plugin is registered and it <em>depends</em> on CorePlugin, there is no | ||||
|      * need to explicitly specify it. By default all plugins that extend AbstractMarkwonPlugin do declare | ||||
|      * it\'s dependency on CorePlugin ({@link MarkwonPlugin#priority()}). | ||||
|      * <p> | ||||
|      * Order in which plugins are specified to the builder is of little importance as long as each | ||||
|      * plugin clearly states what dependencies it has | ||||
|      */ | ||||
|     private void step_1() { | ||||
| 
 | ||||
|         final String markdown = "# Hello!\n\nA paragraph?\n\nIt should be!"; | ||||
| 
 | ||||
|         final Markwon markwon = Markwon.builder(this) | ||||
|                 .usePlugin(new AbstractMarkwonPlugin() { | ||||
|                     @Override | ||||
|                     public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) { | ||||
|                         builder.setFactory(Paragraph.class, (configuration, props) -> | ||||
|                                 new ForegroundColorSpan(Color.GREEN)); | ||||
|                     } | ||||
|                 }) | ||||
|                 .build(); | ||||
| 
 | ||||
|         markwon.setMarkdown(textView, markdown); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * To disable some nodes from rendering another custom plugin can be used | ||||
|      */ | ||||
|     private void step_2() { | ||||
| 
 | ||||
|         final String markdown = "# Heading 1\n\n## Heading 2\n\n**other** content [here](#)"; | ||||
| 
 | ||||
|         final Markwon markwon = Markwon.builder(this) | ||||
|                 .usePlugin(new AbstractMarkwonPlugin() { | ||||
|                     @Override | ||||
|                     public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) { | ||||
| 
 | ||||
|                         // for example to disable rendering of heading: | ||||
|                         // try commenting this out to see that otherwise headings will be rendered | ||||
|                         builder.on(Heading.class, null); | ||||
| 
 | ||||
|                         // same method can be used to override existing visitor by specifying | ||||
|                         // a new NodeVisitor instance | ||||
|                     } | ||||
|                 }) | ||||
|                 .build(); | ||||
| 
 | ||||
|         markwon.setMarkdown(textView, markdown); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * To customize core theme plugin can be used again | ||||
|      */ | ||||
|     private void step_3() { | ||||
| 
 | ||||
|         final String markdown = "`A code` that is rendered differently\n\n```\nHello!\n```"; | ||||
| 
 | ||||
|         final Markwon markwon = Markwon.builder(this) | ||||
|                 .usePlugin(new AbstractMarkwonPlugin() { | ||||
|                     @Override | ||||
|                     public void configureTheme(@NonNull MarkwonTheme.Builder builder) { | ||||
|                         builder | ||||
|                                 .codeBackgroundColor(Color.BLACK) | ||||
|                                 .codeTextColor(Color.RED); | ||||
|                     } | ||||
|                 }) | ||||
|                 .build(); | ||||
| 
 | ||||
|         markwon.setMarkdown(textView, markdown); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * MarkwonConfiguration contains these <em>utilities</em>: | ||||
|      * <ul> | ||||
|      * <li>SyntaxHighlight</li> | ||||
|      * <li>LinkSpan.Resolver</li> | ||||
|      * <li>UrlProcessor</li> | ||||
|      * <li>ImageSizeResolver</li> | ||||
|      * </ul> | ||||
|      * <p> | ||||
|      * In order to customize them a custom plugin should be used | ||||
|      */ | ||||
|     private void step_4() { | ||||
| 
 | ||||
|         final String markdown = "[a link without scheme](github.com)"; | ||||
| 
 | ||||
|         final Markwon markwon = Markwon.builder(this) | ||||
|                 // please note that Markwon does not handle MovementMethod, | ||||
|                 // so if your markdown has links your should apply MovementMethod manually | ||||
|                 // or use MovementMethodPlugin (which uses system LinkMovementMethod by default) | ||||
|                 .usePlugin(MovementMethodPlugin.create()) | ||||
|                 .usePlugin(new AbstractMarkwonPlugin() { | ||||
|                     @Override | ||||
|                     public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) { | ||||
|                         // for example if specified destination has no scheme info, we will | ||||
|                         // _assume_ that it's network request and append HTTPS scheme | ||||
|                         builder.urlProcessor(destination -> { | ||||
|                             final Uri uri = Uri.parse(destination); | ||||
|                             if (TextUtils.isEmpty(uri.getScheme())) { | ||||
|                                 return "https://" + destination; | ||||
|                             } | ||||
|                             return destination; | ||||
|                         }); | ||||
|                     } | ||||
|                 }) | ||||
|                 .build(); | ||||
| 
 | ||||
|         markwon.setMarkdown(textView, markdown); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Images configuration. Can be used with (or without) ImagesPlugin, which does some basic | ||||
|      * images handling (parsing markdown containing images, obtain an image from network | ||||
|      * file system or assets). Please note that | ||||
|      */ | ||||
|     private void step_5() { | ||||
| 
 | ||||
|         final String markdown = ""; | ||||
| 
 | ||||
|         final Markwon markwon = Markwon.builder(this) | ||||
|                 .usePlugin(ImagesPlugin.create(this)) | ||||
|                 .usePlugin(new AbstractMarkwonPlugin() { | ||||
|                     @Override | ||||
|                     public void configureImages(@NonNull AsyncDrawableLoader.Builder builder) { | ||||
|                         // we can have a custom SchemeHandler | ||||
|                         // here we will just use networkSchemeHandler to redirect call | ||||
|                         builder.addSchemeHandler("myownscheme", new SchemeHandler() { | ||||
| 
 | ||||
|                             final NetworkSchemeHandler networkSchemeHandler = NetworkSchemeHandler.create(); | ||||
| 
 | ||||
|                             @Nullable | ||||
|                             @Override | ||||
|                             public ImageItem handle(@NonNull String raw, @NonNull Uri uri) { | ||||
|                                 raw = raw.replace("myownscheme", "https"); | ||||
|                                 return networkSchemeHandler.handle(raw, Uri.parse(raw)); | ||||
|                             } | ||||
|                         }); | ||||
|                     } | ||||
|                 }) | ||||
|                 .build(); | ||||
| 
 | ||||
|         markwon.setMarkdown(textView, markdown); | ||||
|     } | ||||
| 
 | ||||
|     // text lifecycle (after/before) | ||||
|     // rendering lifecycle (before/after) | ||||
|     // renderProps | ||||
|     // process | ||||
|     // priority | ||||
| } | ||||
| @ -1,26 +1,16 @@ | ||||
| package ru.noties.markwon.sample.core; | ||||
| 
 | ||||
| import android.app.Activity; | ||||
| import android.graphics.Color; | ||||
| import android.os.Bundle; | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.text.Spanned; | ||||
| import android.text.style.ForegroundColorSpan; | ||||
| import android.widget.TextView; | ||||
| import android.widget.Toast; | ||||
| 
 | ||||
| import org.commonmark.node.Heading; | ||||
| import org.commonmark.node.Node; | ||||
| import org.commonmark.node.Paragraph; | ||||
| 
 | ||||
| import ru.noties.markwon.AbstractMarkwonPlugin; | ||||
| import ru.noties.markwon.Markwon; | ||||
| import ru.noties.markwon.MarkwonPlugin; | ||||
| import ru.noties.markwon.MarkwonSpansFactory; | ||||
| import ru.noties.markwon.MarkwonVisitor; | ||||
| import ru.noties.markwon.core.CorePlugin; | ||||
| import ru.noties.markwon.core.MarkwonTheme; | ||||
| 
 | ||||
| public class CoreActivity extends Activity { | ||||
| 
 | ||||
| @ -40,12 +30,6 @@ public class CoreActivity extends Activity { | ||||
|         step_3(); | ||||
| 
 | ||||
|         step_4(); | ||||
| 
 | ||||
|         step_5(); | ||||
| 
 | ||||
|         step_6(); | ||||
| 
 | ||||
|         step_7(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -135,74 +119,4 @@ public class CoreActivity extends Activity { | ||||
|         // apply parsed markdown | ||||
|         markwon.setParsedMarkdown(textView, spanned); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * In order to apply paragraph spans a custom plugin should be created (CorePlugin will take care | ||||
|      * of everything else). | ||||
|      * <p> | ||||
|      * Please note that when a plugin is registered and it <em>depends</em> on CorePlugin, there is no | ||||
|      * need to explicitly specify it. By default all plugins that extend AbstractMarkwonPlugin do declare | ||||
|      * it\'s dependency on CorePlugin ({@link MarkwonPlugin#priority()}). | ||||
|      * <p> | ||||
|      * Order in which plugins are specified to the builder is of little importance as long as each | ||||
|      * plugin clearly states what dependencies it has | ||||
|      */ | ||||
|     private void step_5() { | ||||
| 
 | ||||
|         final String markdown = "# Hello!\n\nA paragraph?\n\nIt should be!"; | ||||
| 
 | ||||
|         final Markwon markwon = Markwon.builder(this) | ||||
|                 .usePlugin(new AbstractMarkwonPlugin() { | ||||
|                     @Override | ||||
|                     public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) { | ||||
|                         builder.setFactory(Paragraph.class, (configuration, props) -> | ||||
|                                 new ForegroundColorSpan(Color.GREEN)); | ||||
|                     } | ||||
|                 }) | ||||
|                 .build(); | ||||
| 
 | ||||
|         markwon.setMarkdown(textView, markdown); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * To disable some nodes from rendering another custom plugin can be used | ||||
|      */ | ||||
|     private void step_6() { | ||||
| 
 | ||||
|         final String markdown = "# Heading 1\n\n## Heading 2\n\n**other** content [here](#)"; | ||||
| 
 | ||||
|         final Markwon markwon = Markwon.builder(this) | ||||
|                 .usePlugin(new AbstractMarkwonPlugin() { | ||||
|                     @Override | ||||
|                     public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) { | ||||
|                         // for example to disable rendering of heading: | ||||
|                         // try commenting this out to see that otherwise headings will be rendered | ||||
|                         builder.on(Heading.class, null); | ||||
|                     } | ||||
|                 }) | ||||
|                 .build(); | ||||
| 
 | ||||
|         markwon.setMarkdown(textView, markdown); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * To customize core theme plugins can be used again | ||||
|      */ | ||||
|     private void step_7() { | ||||
| 
 | ||||
|         final String markdown = "`A code` that is rendered differently\n\n```\nHello!\n```"; | ||||
| 
 | ||||
|         final Markwon markwon = Markwon.builder(this) | ||||
|                 .usePlugin(new AbstractMarkwonPlugin() { | ||||
|                     @Override | ||||
|                     public void configureTheme(@NonNull MarkwonTheme.Builder builder) { | ||||
|                         builder | ||||
|                                 .codeBackgroundColor(Color.BLACK) | ||||
|                                 .codeTextColor(Color.RED); | ||||
|                     } | ||||
|                 }) | ||||
|                 .build(); | ||||
| 
 | ||||
|         markwon.setMarkdown(textView, markdown); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,162 @@ | ||||
| package ru.noties.markwon.sample.recycler; | ||||
| 
 | ||||
| import android.app.Activity; | ||||
| import android.content.Context; | ||||
| import android.net.Uri; | ||||
| import android.os.Bundle; | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.support.v7.widget.LinearLayoutManager; | ||||
| import android.support.v7.widget.RecyclerView; | ||||
| import android.text.TextUtils; | ||||
| 
 | ||||
| import org.commonmark.ext.gfm.tables.TableBlock; | ||||
| import org.commonmark.node.FencedCodeBlock; | ||||
| 
 | ||||
| import java.io.BufferedReader; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.io.InputStreamReader; | ||||
| 
 | ||||
| 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.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.urlprocessor.UrlProcessor; | ||||
| import ru.noties.markwon.urlprocessor.UrlProcessorRelativeToAbsolute; | ||||
| 
 | ||||
| public class RecyclerActivity extends Activity { | ||||
| 
 | ||||
|     static { | ||||
|         Debug.init(new AndroidLogDebugOutput(true)); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onCreate(@Nullable Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|         setContentView(R.layout.activity_recycler); | ||||
| 
 | ||||
|         // create MarkwonAdapter and register two blocks that will be rendered differently | ||||
|         // * fenced code block (can also specify the same Entry for indended code block) | ||||
|         // * table block | ||||
|         final MarkwonAdapter adapter = MarkwonAdapter.builder() | ||||
|                 // we can simply use bundled SimpleEntry, that will lookup a TextView | ||||
|                 // 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()) | ||||
|                 // specify default entry (for all other blocks) | ||||
|                 .defaultEntry(new SimpleEntry(R.layout.adapter_default_entry)) | ||||
|                 .build(); | ||||
| 
 | ||||
|         final RecyclerView recyclerView = findViewById(R.id.recycler_view); | ||||
|         recyclerView.setLayoutManager(new LinearLayoutManager(this)); | ||||
|         recyclerView.setHasFixedSize(true); | ||||
|         recyclerView.setAdapter(adapter); | ||||
| 
 | ||||
|         final Markwon markwon = markwon(this); | ||||
|         adapter.setMarkdown(markwon, loadReadMe(this)); | ||||
| 
 | ||||
|         // please note that we should notify updates (adapter doesn't do it implicitly) | ||||
|         adapter.notifyDataSetChanged(); | ||||
| 
 | ||||
|         // NB, there is no currently available widget to render tables gracefully | ||||
|         // TableEntryView is here for demonstration purposes only (to show that rendering | ||||
|         // tables | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     private static Markwon markwon(@NonNull Context context) { | ||||
|         return Markwon.builder(context) | ||||
|                 .usePlugin(CorePlugin.create()) | ||||
|                 .usePlugin(ImagesPlugin.createWithAssets(context)) | ||||
|                 .usePlugin(SvgPlugin.create(context.getResources())) | ||||
|                 .usePlugin(TablePlugin.create(context)) | ||||
|                 .usePlugin(HtmlPlugin.create()) | ||||
|                 .usePlugin(new AbstractMarkwonPlugin() { | ||||
|                     @Override | ||||
|                     public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) { | ||||
|                         builder.urlProcessor(new UrlProcessorInitialReadme()); | ||||
|                     } | ||||
|                 }) | ||||
|                 .build(); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     private static String loadReadMe(@NonNull Context context) { | ||||
|         InputStream stream = null; | ||||
|         try { | ||||
|             stream = context.getAssets().open("README.md"); | ||||
|         } catch (IOException e) { | ||||
|             e.printStackTrace(); | ||||
|         } | ||||
|         return readStream(stream); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     private static String readStream(@Nullable InputStream inputStream) { | ||||
| 
 | ||||
|         String out = null; | ||||
| 
 | ||||
|         if (inputStream != null) { | ||||
|             BufferedReader reader = null; | ||||
|             //noinspection TryFinallyCanBeTryWithResources | ||||
|             try { | ||||
|                 reader = new BufferedReader(new InputStreamReader(inputStream)); | ||||
|                 final StringBuilder builder = new StringBuilder(); | ||||
|                 String line; | ||||
|                 while ((line = reader.readLine()) != null) { | ||||
|                     builder.append(line) | ||||
|                             .append('\n'); | ||||
|                 } | ||||
|                 out = builder.toString(); | ||||
|             } catch (IOException e) { | ||||
|                 e.printStackTrace(); | ||||
|             } finally { | ||||
|                 if (reader != null) { | ||||
|                     try { | ||||
|                         reader.close(); | ||||
|                     } catch (IOException e) { | ||||
|                         // no op | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (out == null) { | ||||
|             throw new RuntimeException("Cannot read stream"); | ||||
|         } | ||||
| 
 | ||||
|         return out; | ||||
|     } | ||||
| 
 | ||||
|     private static class UrlProcessorInitialReadme implements UrlProcessor { | ||||
| 
 | ||||
|         private static final String GITHUB_BASE = "https://github.com/noties/Markwon/raw/master/"; | ||||
| 
 | ||||
|         private final UrlProcessorRelativeToAbsolute processor | ||||
|                 = new UrlProcessorRelativeToAbsolute(GITHUB_BASE); | ||||
| 
 | ||||
|         @NonNull | ||||
|         @Override | ||||
|         public String process(@NonNull String destination) { | ||||
|             String out; | ||||
|             final Uri uri = Uri.parse(destination); | ||||
|             if (TextUtils.isEmpty(uri.getScheme())) { | ||||
|                 out = processor.process(destination); | ||||
|             } else { | ||||
|                 out = destination; | ||||
|             } | ||||
|             return out; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,67 @@ | ||||
| package ru.noties.markwon.sample.recycler; | ||||
| 
 | ||||
| import android.support.annotation.NonNull; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| 
 | ||||
| import org.commonmark.ext.gfm.tables.TableBlock; | ||||
| 
 | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
| 
 | ||||
| import ru.noties.debug.Debug; | ||||
| import ru.noties.markwon.Markwon; | ||||
| import ru.noties.markwon.ext.tables.Table; | ||||
| 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> { | ||||
| 
 | ||||
|     private final Map<TableBlock, Table> cache = new HashMap<>(2); | ||||
| 
 | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public TableNodeHolder createHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) { | ||||
|         return new TableNodeHolder(inflater.inflate(R.layout.adapter_table_block, parent, false)); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void bindHolder(@NonNull Markwon markwon, @NonNull TableNodeHolder holder, @NonNull TableBlock node) { | ||||
| 
 | ||||
|         Table table = cache.get(node); | ||||
|         if (table == null) { | ||||
|             table = Table.parse(markwon, node); | ||||
|             cache.put(node, table); | ||||
|         } | ||||
| 
 | ||||
|         Debug.i(table); | ||||
| 
 | ||||
|         if (table != null) { | ||||
|             holder.tableEntryView.setTable(table); | ||||
|             // render table | ||||
|         } // we need to do something with null table... | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public long id(@NonNull TableBlock node) { | ||||
|         return node.hashCode(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void clear() { | ||||
|         cache.clear(); | ||||
|     } | ||||
| 
 | ||||
|     static class TableNodeHolder extends MarkwonAdapter.Holder { | ||||
| 
 | ||||
|         final TableEntryView tableEntryView; | ||||
| 
 | ||||
|         TableNodeHolder(@NonNull View itemView) { | ||||
|             super(itemView); | ||||
| 
 | ||||
|             this.tableEntryView = requireView(R.id.table_entry); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,218 @@ | ||||
| package ru.noties.markwon.sample.recycler; | ||||
| 
 | ||||
| import android.annotation.SuppressLint; | ||||
| import android.content.Context; | ||||
| import android.content.res.TypedArray; | ||||
| import android.graphics.Canvas; | ||||
| import android.graphics.Color; | ||||
| import android.graphics.Paint; | ||||
| import android.graphics.Rect; | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.text.SpannedString; | ||||
| import android.util.AttributeSet; | ||||
| import android.view.Gravity; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.LinearLayout; | ||||
| import android.widget.TextView; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| 
 | ||||
| import ru.noties.markwon.ext.tables.Table; | ||||
| import ru.noties.markwon.sample.R; | ||||
| 
 | ||||
| public class TableEntryView extends LinearLayout { | ||||
| 
 | ||||
|     // paint and rect to draw borders | ||||
|     private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); | ||||
|     private final Rect rect = new Rect(); | ||||
| 
 | ||||
|     private LayoutInflater inflater; | ||||
| 
 | ||||
|     private int rowEvenBackgroundColor; | ||||
| 
 | ||||
|     public TableEntryView(Context context) { | ||||
|         super(context); | ||||
|         init(context, null); | ||||
|     } | ||||
| 
 | ||||
|     public TableEntryView(Context context, AttributeSet attrs) { | ||||
|         super(context, attrs); | ||||
|         init(context, attrs); | ||||
|     } | ||||
| 
 | ||||
|     private void init(Context context, @Nullable AttributeSet attrs) { | ||||
|         inflater = LayoutInflater.from(context); | ||||
|         setOrientation(VERTICAL); | ||||
| 
 | ||||
|         if (attrs != null) { | ||||
|             final TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.TableEntryView); | ||||
|             try { | ||||
| 
 | ||||
|                 rowEvenBackgroundColor = array.getColor(R.styleable.TableEntryView_tev_rowEvenBackgroundColor, 0); | ||||
| 
 | ||||
| 
 | ||||
|                 final int stroke = array.getDimensionPixelSize(R.styleable.TableEntryView_tev_borderWidth, 0); | ||||
| 
 | ||||
|                 // half of requested | ||||
|                 final float strokeWidth = stroke > 0 | ||||
|                         ? stroke / 2.F | ||||
|                         : context.getResources().getDisplayMetrics().density / 2.F; | ||||
| 
 | ||||
|                 paint.setStyle(Paint.Style.STROKE); | ||||
|                 paint.setStrokeWidth(strokeWidth); | ||||
|                 paint.setColor(array.getColor(R.styleable.TableEntryView_tev_borderColor, Color.BLACK)); | ||||
| 
 | ||||
|                 if (isInEditMode()) { | ||||
|                     final String data = array.getString(R.styleable.TableEntryView_tev_debugData); | ||||
|                     if (data != null) { | ||||
| 
 | ||||
|                         boolean first = true; | ||||
| 
 | ||||
|                         final List<Table.Row> rows = new ArrayList<>(); | ||||
|                         for (String row : data.split("\\|")) { | ||||
|                             final List<Table.Column> columns = new ArrayList<>(); | ||||
|                             for (String column : row.split(",")) { | ||||
|                                 columns.add(new Table.Column(Table.Alignment.LEFT, new SpannedString(column))); | ||||
|                             } | ||||
|                             final boolean header = first; | ||||
|                             first = false; | ||||
|                             rows.add(new Table.Row(header, columns)); | ||||
|                         } | ||||
|                         final Table table = new Table(rows); | ||||
|                         setTable(table); | ||||
|                     } | ||||
|                 } | ||||
|             } finally { | ||||
|                 array.recycle(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         setWillNotDraw(false); | ||||
|     } | ||||
| 
 | ||||
|     public void setTable(@NonNull Table table) { | ||||
|         final List<Table.Row> rows = table.rows(); | ||||
|         for (int i = 0, size = rows.size(); i < size; i++) { | ||||
|             addRow(i, rows.get(i)); | ||||
|         } | ||||
|         requestLayout(); | ||||
|     } | ||||
| 
 | ||||
|     private void addRow(int index, @NonNull Table.Row row) { | ||||
| 
 | ||||
|         final ViewGroup group = ensureRow(index); | ||||
| 
 | ||||
|         final int backgroundColor = !row.header() && (index % 2) == 0 | ||||
|                 ? rowEvenBackgroundColor | ||||
|                 : 0; | ||||
| 
 | ||||
|         group.setBackgroundColor(backgroundColor); | ||||
| 
 | ||||
|         final List<Table.Column> columns = row.columns(); | ||||
| 
 | ||||
|         TextView textView; | ||||
|         Table.Column column; | ||||
| 
 | ||||
|         for (int i = 0, size = columns.size(); i < size; i++) { | ||||
|             textView = ensureCell(group, i); | ||||
|             column = columns.get(i); | ||||
|             textView.setGravity(textGravity(column.alignment())); | ||||
|             textView.setText(column.content()); | ||||
|             textView.getPaint().setFakeBoldText(row.header()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     private ViewGroup ensureRow(int index) { | ||||
| 
 | ||||
|         final int count = getChildCount(); | ||||
|         if (index >= count) { | ||||
| 
 | ||||
|             // count=0,index=1, diff=2 | ||||
|             // count=0,index=5, diff=6 | ||||
|             // count=1,index=2, diff=2 | ||||
|             int diff = index - count + 1; | ||||
|             while (diff > 0) { | ||||
|                 addView(inflater.inflate(R.layout.view_table_entry_row, this, false)); | ||||
|                 diff -= 1; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return (ViewGroup) getChildAt(index); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     private TextView ensureCell(@NonNull ViewGroup group, int index) { | ||||
| 
 | ||||
|         final int count = group.getChildCount(); | ||||
|         if (index >= count) { | ||||
|             int diff = index - count + 1; | ||||
|             while (diff > 0) { | ||||
|                 group.addView(inflater.inflate(R.layout.view_table_entry_cell, group, false)); | ||||
|                 diff -= 1; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return (TextView) group.getChildAt(index); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void onDraw(Canvas canvas) { | ||||
|         super.onDraw(canvas); | ||||
| 
 | ||||
|         final int rows = getChildCount(); | ||||
|         if (rows == 0) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         // first draw the whole border | ||||
|         rect.set(0, 0, getWidth(), getHeight()); | ||||
|         canvas.drawRect(rect, paint); | ||||
| 
 | ||||
|         ViewGroup group; | ||||
|         View view; | ||||
| 
 | ||||
|         int top; | ||||
| 
 | ||||
|         for (int row = 0; row < rows; row++) { | ||||
|             group = (ViewGroup) getChildAt(row); | ||||
|             top = group.getTop(); | ||||
|             for (int col = 0, cols = group.getChildCount(); col < cols; col++) { | ||||
|                 view = group.getChildAt(col); | ||||
|                 rect.set(view.getLeft(), top + view.getTop(), view.getRight(), top + view.getBottom()); | ||||
|                 canvas.drawRect(rect, paint); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // 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; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										1
									
								
								sample/src/main/recycler/assets/README.md
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								sample/src/main/recycler/assets/README.md
									
									
									
									
									
										Symbolic link
									
								
							| @ -0,0 +1 @@ | ||||
| ../../../../../README.md | ||||
| @ -0,0 +1,5 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:id="@+id/recycler_view" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="match_parent" /> | ||||
| @ -0,0 +1,13 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <TextView xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:id="@+id/text" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="wrap_content" | ||||
|     android:layout_marginLeft="16dip" | ||||
|     android:layout_marginRight="16dip" | ||||
|     android:lineSpacingExtra="2dip" | ||||
|     android:paddingTop="8dip" | ||||
|     android:paddingBottom="8dip" | ||||
|     android:textAppearance="?android:attr/textAppearanceMedium" | ||||
|     android:textColor="#000" | ||||
|     android:textSize="16sp" /> | ||||
| @ -0,0 +1,24 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="wrap_content" | ||||
|     android:clipChildren="false" | ||||
|     android:clipToPadding="false" | ||||
|     android:fillViewport="true" | ||||
|     android:paddingLeft="16dip" | ||||
|     android:paddingRight="16dip" | ||||
|     android:scrollbarStyle="outsideInset"> | ||||
| 
 | ||||
|     <TextView | ||||
|         android:id="@+id/text" | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:lineSpacingExtra="2dip" | ||||
|         android:paddingTop="8dip" | ||||
|         android:paddingBottom="8dip" | ||||
|         android:textAppearance="?android:attr/textAppearanceMedium" | ||||
|         android:textSize="16sp" | ||||
|         tools:text="# Hello there! and taskjs" /> | ||||
| 
 | ||||
| </HorizontalScrollView> | ||||
							
								
								
									
										22
									
								
								sample/src/main/recycler/res/layout/adapter_table_block.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								sample/src/main/recycler/res/layout/adapter_table_block.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="wrap_content" | ||||
|     android:clipChildren="false" | ||||
|     android:clipToPadding="false" | ||||
|     android:fillViewport="true" | ||||
|     android:paddingLeft="16dip" | ||||
|     android:paddingTop="8dip" | ||||
|     android:paddingRight="16dip" | ||||
|     android:paddingBottom="8dip" | ||||
|     android:scrollbarStyle="outsideInset"> | ||||
| 
 | ||||
|     <ru.noties.markwon.sample.recycler.TableEntryView | ||||
|         android:id="@+id/table_entry" | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="wrap_content" | ||||
|         app:tev_debugData="head1,head2,head3|col1,col2,col3|col1,col2,col3|col1,col2,col3" | ||||
|         app:tev_rowEvenBackgroundColor="#40ff0000" /> | ||||
| 
 | ||||
| </HorizontalScrollView> | ||||
| @ -0,0 +1,11 @@ | ||||
| <?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_height="match_parent" | ||||
|     android:layout_weight="1" | ||||
|     android:padding="4dip" | ||||
|     android:textAppearance="?android:attr/textAppearanceMedium" | ||||
|     android:textColor="#000" | ||||
|     android:textSize="16sp" | ||||
|     tools:text="Table content" /> | ||||
| @ -0,0 +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_height="wrap_content" | ||||
|     android:orientation="horizontal" /> | ||||
							
								
								
									
										9
									
								
								sample/src/main/recycler/res/values/attrs.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								sample/src/main/recycler/res/values/attrs.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <resources> | ||||
|     <declare-styleable name="TableEntryView"> | ||||
|         <attr name="tev_rowEvenBackgroundColor" format="color" /> | ||||
|         <attr name="tev_borderColor" format="color" /> | ||||
|         <attr name="tev_borderWidth" format="dimension" /> | ||||
|         <attr name="tev_debugData" format="string" /> | ||||
|     </declare-styleable> | ||||
| </resources> | ||||
| @ -4,7 +4,15 @@ | ||||
|     <!--Ignore missing translation warning--> | ||||
| 
 | ||||
|     <string name="sample_core"># \# Core\n\nSimple usage example</string> | ||||
|     <string name="sample_latex"># \# LaTeX\n\nShows how to display a **LaTeX** formula in a Markwon powered application</string> | ||||
|     <string name="sample_custom_extension"># \# Custom extension\n\nShows how to create a custom extension to display an icon referenced in markdown as `@ic-android-black-24`</string> | ||||
| 
 | ||||
|     <string name="sample_basic_plugins"># \# Basic plugins\n\nShows basic usage of plugins</string> | ||||
| 
 | ||||
|     <string name="sample_latex"># \# LaTeX\n\nShows how to display a **LaTeX** formula</string> | ||||
| 
 | ||||
|     <string name="sample_custom_extension"># \# Custom extension\n\nShows how to create a custom | ||||
|         extension to display an icon referenced in markdown as `@ic-android-black-24`</string> | ||||
| 
 | ||||
|     <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> | ||||
| 
 | ||||
| </resources> | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Dimitry Ivanov
						Dimitry Ivanov