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