Table plugin, better borders
This commit is contained in:
parent
33f0dcb841
commit
ddfa9c98b8
@ -1,12 +1,16 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
# 4.3.1-SNAPSHOT
|
# $nap;
|
||||||
* Fix DexGuard optimization issue ([#216])<br>Thanks [@francescocervone]
|
* Fix DexGuard optimization issue ([#216])<br>Thanks [@francescocervone]
|
||||||
* module `images`: `GifSupport` and `SvgSupport` use `Class.forName` instead access to full qualified class name
|
* module `images`: `GifSupport` and `SvgSupport` use `Class.forName` instead access to full qualified class name
|
||||||
|
* `ext-table`: fix links in tables ([#224])
|
||||||
|
* `ext-table`: proper borders (equal for all sides)
|
||||||
|
|
||||||
[#216]: https://github.com/noties/Markwon/pull/216
|
[#216]: https://github.com/noties/Markwon/pull/216
|
||||||
|
[#224]: https://github.com/noties/Markwon/issues/224
|
||||||
[@francescocervone]: https://github.com/francescocervone
|
[@francescocervone]: https://github.com/francescocervone
|
||||||
|
|
||||||
|
|
||||||
# 4.3.0
|
# 4.3.0
|
||||||
* add `MarkwonInlineParserPlugin` in `inline-parser` module
|
* add `MarkwonInlineParserPlugin` in `inline-parser` module
|
||||||
* `JLatexMathPlugin` now supports inline LaTeX structures via `MarkwonInlineParserPlugin`
|
* `JLatexMathPlugin` now supports inline LaTeX structures via `MarkwonInlineParserPlugin`
|
||||||
|
@ -0,0 +1,59 @@
|
|||||||
|
package io.noties.markwon.debug;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import io.noties.markwon.app.R;
|
||||||
|
import io.noties.markwon.utils.ColorUtils;
|
||||||
|
|
||||||
|
public class ColorBlendView extends View {
|
||||||
|
|
||||||
|
private final Rect rect = new Rect();
|
||||||
|
private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
|
||||||
|
private int background;
|
||||||
|
private int foreground;
|
||||||
|
|
||||||
|
public ColorBlendView(Context context, @Nullable AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
|
||||||
|
if (attrs != null) {
|
||||||
|
final TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ColorBlendView);
|
||||||
|
try {
|
||||||
|
background = array.getColor(R.styleable.ColorBlendView_cbv_background, 0);
|
||||||
|
foreground = array.getColor(R.styleable.ColorBlendView_cbv_foreground, 0);
|
||||||
|
} finally {
|
||||||
|
array.recycle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
paint.setStyle(Paint.Style.FILL);
|
||||||
|
|
||||||
|
setWillNotDraw(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDraw(Canvas canvas) {
|
||||||
|
super.onDraw(canvas);
|
||||||
|
|
||||||
|
final int side = getWidth() / 11;
|
||||||
|
|
||||||
|
rect.set(0, 0, side, getHeight());
|
||||||
|
|
||||||
|
canvas.translate(getPaddingLeft(), 0F);
|
||||||
|
|
||||||
|
for (int i = 0; i < 11; i++) {
|
||||||
|
final float alpha = i / 10F;
|
||||||
|
paint.setColor(ColorUtils.blend(foreground, background, alpha));
|
||||||
|
canvas.drawRect(rect, paint);
|
||||||
|
canvas.translate(side, 0F);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
34
app/src/debug/res/layout/debug_color_blend.xml
Normal file
34
app/src/debug/res/layout/debug_color_blend.xml
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="#f00">
|
||||||
|
|
||||||
|
<io.noties.markwon.debug.ColorBlendView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="64dip"
|
||||||
|
app:cbv_background="#fff"
|
||||||
|
app:cbv_foreground="#f0f"/>
|
||||||
|
|
||||||
|
<io.noties.markwon.debug.ColorBlendView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="64dip"
|
||||||
|
app:cbv_background="#000"
|
||||||
|
app:cbv_foreground="#f0f"/>
|
||||||
|
|
||||||
|
<io.noties.markwon.debug.ColorBlendView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="64dip"
|
||||||
|
app:cbv_background="#fff"
|
||||||
|
app:cbv_foreground="#00f"/>
|
||||||
|
|
||||||
|
<io.noties.markwon.debug.ColorBlendView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="64dip"
|
||||||
|
app:cbv_background="#000"
|
||||||
|
app:cbv_foreground="#00f"/>
|
||||||
|
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -8,4 +8,9 @@
|
|||||||
<attr name="fcdv_checked" format="boolean" />
|
<attr name="fcdv_checked" format="boolean" />
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|
||||||
|
<declare-styleable name="ColorBlendView">
|
||||||
|
<attr name="cbv_foreground" format="color" />
|
||||||
|
<attr name="cbv_background" format="color" />
|
||||||
|
</declare-styleable>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
@ -1,11 +1,33 @@
|
|||||||
package io.noties.markwon.utils;
|
package io.noties.markwon.utils;
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
|
|
||||||
|
import androidx.annotation.ColorInt;
|
||||||
|
import androidx.annotation.FloatRange;
|
||||||
|
import androidx.annotation.IntRange;
|
||||||
|
|
||||||
public abstract class ColorUtils {
|
public abstract class ColorUtils {
|
||||||
|
|
||||||
public static int applyAlpha(int color, int alpha) {
|
@ColorInt
|
||||||
|
public static int applyAlpha(
|
||||||
|
@ColorInt int color,
|
||||||
|
@IntRange(from = 0, to = 255) int alpha) {
|
||||||
return (color & 0x00FFFFFF) | (alpha << 24);
|
return (color & 0x00FFFFFF) | (alpha << 24);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// blend two colors w/ specified ratio, resulting color won't have alpha channel
|
||||||
|
@ColorInt
|
||||||
|
public static int blend(
|
||||||
|
@ColorInt int foreground,
|
||||||
|
@ColorInt int background,
|
||||||
|
@FloatRange(from = 0.0F, to = 1.0F) float ratio) {
|
||||||
|
return Color.rgb(
|
||||||
|
(int) (((1F - ratio) * Color.red(foreground)) + (ratio * Color.red(background))),
|
||||||
|
(int) (((1F - ratio) * Color.green(foreground)) + (ratio * Color.green(background))),
|
||||||
|
(int) (((1F - ratio) * Color.blue(foreground)) + (ratio * Color.blue(background)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private ColorUtils() {
|
private ColorUtils() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,8 +123,13 @@ public class TablePlugin extends AbstractMarkwonPlugin {
|
|||||||
|
|
||||||
visitor.blockStart(tableBlock);
|
visitor.blockStart(tableBlock);
|
||||||
|
|
||||||
|
final int length = visitor.length();
|
||||||
|
|
||||||
visitor.visitChildren(tableBlock);
|
visitor.visitChildren(tableBlock);
|
||||||
|
|
||||||
|
// @since $nap; apply table span for the full table
|
||||||
|
visitor.setSpans(length, new TableSpan());
|
||||||
|
|
||||||
visitor.blockEnd(tableBlock);
|
visitor.blockEnd(tableBlock);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -5,6 +5,7 @@ import android.graphics.Canvas;
|
|||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.text.Layout;
|
import android.text.Layout;
|
||||||
|
import android.text.Spanned;
|
||||||
import android.text.StaticLayout;
|
import android.text.StaticLayout;
|
||||||
import android.text.TextPaint;
|
import android.text.TextPaint;
|
||||||
import android.text.style.ReplacementSpan;
|
import android.text.style.ReplacementSpan;
|
||||||
@ -19,6 +20,8 @@ import java.lang.annotation.RetentionPolicy;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import io.noties.markwon.utils.LeadingMarginUtils;
|
||||||
|
|
||||||
public class TableRowSpan extends ReplacementSpan {
|
public class TableRowSpan extends ReplacementSpan {
|
||||||
|
|
||||||
public static final int ALIGN_LEFT = 0;
|
public static final int ALIGN_LEFT = 0;
|
||||||
@ -139,16 +142,16 @@ public class TableRowSpan extends ReplacementSpan {
|
|||||||
int top,
|
int top,
|
||||||
int y,
|
int y,
|
||||||
int bottom,
|
int bottom,
|
||||||
@NonNull Paint paint) {
|
@NonNull Paint p) {
|
||||||
|
|
||||||
if (recreateLayouts(canvas.getWidth())) {
|
if (recreateLayouts(canvas.getWidth())) {
|
||||||
width = canvas.getWidth();
|
width = canvas.getWidth();
|
||||||
// @since $nap; it's important to cast to TextPaint in order to display links, etc
|
// @since $nap; it's important to cast to TextPaint in order to display links, etc
|
||||||
if (paint instanceof TextPaint) {
|
if (p instanceof TextPaint) {
|
||||||
// there must be a reason why this method receives Paint instead of TextPaint...
|
// there must be a reason why this method receives Paint instead of TextPaint...
|
||||||
textPaint.set((TextPaint) paint);
|
textPaint.set((TextPaint) p);
|
||||||
} else {
|
} else {
|
||||||
textPaint.set(paint);
|
textPaint.set(p);
|
||||||
}
|
}
|
||||||
makeNewLayouts();
|
makeNewLayouts();
|
||||||
}
|
}
|
||||||
@ -161,28 +164,25 @@ public class TableRowSpan extends ReplacementSpan {
|
|||||||
|
|
||||||
final int w = width / size;
|
final int w = width / size;
|
||||||
|
|
||||||
// feels like magic...
|
|
||||||
final int heightDiff = (bottom - top - height) / 4;
|
|
||||||
|
|
||||||
// @since 1.1.1
|
// @since 1.1.1
|
||||||
// draw backgrounds
|
// draw backgrounds
|
||||||
{
|
{
|
||||||
if (header) {
|
if (header) {
|
||||||
theme.applyTableHeaderRowStyle(this.paint);
|
theme.applyTableHeaderRowStyle(paint);
|
||||||
} else if (odd) {
|
} else if (odd) {
|
||||||
theme.applyTableOddRowStyle(this.paint);
|
theme.applyTableOddRowStyle(paint);
|
||||||
} else {
|
} else {
|
||||||
// even
|
// even
|
||||||
theme.applyTableEvenRowStyle(this.paint);
|
theme.applyTableEvenRowStyle(paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if present (0 is transparent)
|
// if present (0 is transparent)
|
||||||
if (this.paint.getColor() != 0) {
|
if (paint.getColor() != 0) {
|
||||||
final int save = canvas.save();
|
final int save = canvas.save();
|
||||||
try {
|
try {
|
||||||
rect.set(0, 0, width, bottom - top);
|
rect.set(0, 0, width, bottom - top);
|
||||||
canvas.translate(x, top - heightDiff);
|
canvas.translate(x, top);
|
||||||
canvas.drawRect(rect, this.paint);
|
canvas.drawRect(rect, paint);
|
||||||
} finally {
|
} finally {
|
||||||
canvas.restoreToCount(save);
|
canvas.restoreToCount(save);
|
||||||
}
|
}
|
||||||
@ -192,14 +192,49 @@ public class TableRowSpan extends ReplacementSpan {
|
|||||||
// @since 1.1.1 reset after applying background color
|
// @since 1.1.1 reset after applying background color
|
||||||
// as background changes color attribute and if not specific tableBorderColor
|
// as background changes color attribute and if not specific tableBorderColor
|
||||||
// is specified then after this row all borders will have color of this row (plus alpha)
|
// is specified then after this row all borders will have color of this row (plus alpha)
|
||||||
this.paint.set(paint);
|
paint.set(p);
|
||||||
theme.applyTableBorderStyle(this.paint);
|
theme.applyTableBorderStyle(paint);
|
||||||
|
|
||||||
final int borderWidth = theme.tableBorderWidth(paint);
|
final int borderWidth = theme.tableBorderWidth(paint);
|
||||||
final boolean drawBorder = borderWidth > 0;
|
final boolean drawBorder = borderWidth > 0;
|
||||||
|
|
||||||
|
// why divided by 4 gives a more or less good result is still not clear (shouldn't it be 2?)
|
||||||
|
final int heightDiff = (bottom - top - height) / 4;
|
||||||
|
|
||||||
|
// required for borderTop calculation
|
||||||
|
final boolean isFirstTableRow;
|
||||||
|
|
||||||
|
// @since $nap;
|
||||||
if (drawBorder) {
|
if (drawBorder) {
|
||||||
rect.set(0, 0, w, bottom - top);
|
boolean first = false;
|
||||||
|
// only if first draw the line
|
||||||
|
{
|
||||||
|
final Spanned spanned = (Spanned) text;
|
||||||
|
final TableSpan[] spans = spanned.getSpans(start, end, TableSpan.class);
|
||||||
|
if (spans != null && spans.length > 0) {
|
||||||
|
final TableSpan span = spans[0];
|
||||||
|
if (LeadingMarginUtils.selfStart(start, text, span)) {
|
||||||
|
first = true;
|
||||||
|
rect.set((int) x, top, width, top + borderWidth);
|
||||||
|
canvas.drawRect(rect, paint);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw the line at the bottom
|
||||||
|
rect.set((int) x, bottom - borderWidth, width, bottom);
|
||||||
|
canvas.drawRect(rect, paint);
|
||||||
|
|
||||||
|
isFirstTableRow = first;
|
||||||
|
} else {
|
||||||
|
isFirstTableRow = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int borderWidthHalf = borderWidth / 2;
|
||||||
|
|
||||||
|
// to NOT overlap borders inset top and bottom
|
||||||
|
final int borderTop = isFirstTableRow ? borderWidth : 0;
|
||||||
|
final int borderBottom = bottom - top - borderWidth;
|
||||||
|
|
||||||
StaticLayout layout;
|
StaticLayout layout;
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
@ -207,10 +242,23 @@ public class TableRowSpan extends ReplacementSpan {
|
|||||||
final int save = canvas.save();
|
final int save = canvas.save();
|
||||||
try {
|
try {
|
||||||
|
|
||||||
canvas.translate(x + (i * w), top - heightDiff);
|
canvas.translate(x + (i * w), top);
|
||||||
|
|
||||||
|
// @since $nap;
|
||||||
if (drawBorder) {
|
if (drawBorder) {
|
||||||
canvas.drawRect(rect, this.paint);
|
// first vertical border will have full width (it cannot exceed canvas)
|
||||||
|
if (i == 0) {
|
||||||
|
rect.set(0, borderTop, borderWidth, borderBottom);
|
||||||
|
} else {
|
||||||
|
rect.set(-borderWidthHalf, borderTop, borderWidthHalf, borderBottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.drawRect(rect, paint);
|
||||||
|
|
||||||
|
if (i == (size - 1)) {
|
||||||
|
rect.set(w - borderWidth, borderTop, w, borderBottom);
|
||||||
|
canvas.drawRect(rect, paint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.translate(padding, padding + heightDiff);
|
canvas.translate(padding, padding + heightDiff);
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
package io.noties.markwon.ext.tables;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since $nap;
|
||||||
|
*/
|
||||||
|
public class TableSpan {
|
||||||
|
}
|
@ -10,6 +10,7 @@ import androidx.annotation.Px;
|
|||||||
import io.noties.markwon.utils.ColorUtils;
|
import io.noties.markwon.utils.ColorUtils;
|
||||||
import io.noties.markwon.utils.Dip;
|
import io.noties.markwon.utils.Dip;
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
public class TableTheme {
|
public class TableTheme {
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@ -101,7 +102,8 @@ public class TableTheme {
|
|||||||
}
|
}
|
||||||
|
|
||||||
paint.setColor(color);
|
paint.setColor(color);
|
||||||
paint.setStyle(Paint.Style.STROKE);
|
// @since $nap; before it was STROKE... change to FILL as we draw border differently
|
||||||
|
paint.setStyle(Paint.Style.FILL);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void applyTableOddRowStyle(@NonNull Paint paint) {
|
public void applyTableOddRowStyle(@NonNull Paint paint) {
|
||||||
|
@ -1,17 +1,22 @@
|
|||||||
package io.noties.markwon.sample.table;
|
package io.noties.markwon.sample.table;
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import io.noties.debug.Debug;
|
||||||
import io.noties.markwon.Markwon;
|
import io.noties.markwon.Markwon;
|
||||||
import io.noties.markwon.ext.tables.TablePlugin;
|
import io.noties.markwon.ext.tables.TablePlugin;
|
||||||
|
import io.noties.markwon.ext.tables.TableTheme;
|
||||||
import io.noties.markwon.linkify.LinkifyPlugin;
|
import io.noties.markwon.linkify.LinkifyPlugin;
|
||||||
import io.noties.markwon.sample.ActivityWithMenuOptions;
|
import io.noties.markwon.sample.ActivityWithMenuOptions;
|
||||||
import io.noties.markwon.sample.MenuOptions;
|
import io.noties.markwon.sample.MenuOptions;
|
||||||
import io.noties.markwon.sample.R;
|
import io.noties.markwon.sample.R;
|
||||||
|
import io.noties.markwon.utils.ColorUtils;
|
||||||
|
import io.noties.markwon.utils.Dip;
|
||||||
|
|
||||||
public class TableActivity extends ActivityWithMenuOptions {
|
public class TableActivity extends ActivityWithMenuOptions {
|
||||||
|
|
||||||
@ -19,6 +24,7 @@ public class TableActivity extends ActivityWithMenuOptions {
|
|||||||
@Override
|
@Override
|
||||||
public MenuOptions menuOptions() {
|
public MenuOptions menuOptions() {
|
||||||
return MenuOptions.create()
|
return MenuOptions.create()
|
||||||
|
.add("customize", this::customize)
|
||||||
.add("tableAndLinkify", this::tableAndLinkify);
|
.add("tableAndLinkify", this::tableAndLinkify);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,6 +39,32 @@ public class TableActivity extends ActivityWithMenuOptions {
|
|||||||
tableAndLinkify();
|
tableAndLinkify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void customize() {
|
||||||
|
final String md = "" +
|
||||||
|
"| HEADER | HEADER | HEADER |\n" +
|
||||||
|
"|:----:|:----:|:----:|\n" +
|
||||||
|
"| 测试 | 测试 | 测试 |\n" +
|
||||||
|
"| 测试 | 测试 | 测测测12345试测试测试 |\n" +
|
||||||
|
"| 测试 | 测试 | 123445 |\n" +
|
||||||
|
"| 测试 | 测试 | (650) 555-1212 |\n" +
|
||||||
|
"| 测试 | 测试 | [link](#) |\n";
|
||||||
|
|
||||||
|
final Markwon markwon = Markwon.builder(this)
|
||||||
|
.usePlugin(TablePlugin.create(builder -> {
|
||||||
|
final Dip dip = Dip.create(this);
|
||||||
|
builder
|
||||||
|
.tableBorderWidth(dip.toPx(2))
|
||||||
|
.tableBorderColor(Color.YELLOW)
|
||||||
|
.tableCellPadding(dip.toPx(4))
|
||||||
|
.tableHeaderRowBackgroundColor(ColorUtils.applyAlpha(Color.RED, 80))
|
||||||
|
.tableEvenRowBackgroundColor(ColorUtils.applyAlpha(Color.GREEN, 80))
|
||||||
|
.tableOddRowBackgroundColor(ColorUtils.applyAlpha(Color.BLUE, 80));
|
||||||
|
}))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
markwon.setMarkdown(textView, md);
|
||||||
|
}
|
||||||
|
|
||||||
private void tableAndLinkify() {
|
private void tableAndLinkify() {
|
||||||
final String md = "" +
|
final String md = "" +
|
||||||
"| HEADER | HEADER | HEADER |\n" +
|
"| HEADER | HEADER | HEADER |\n" +
|
||||||
@ -45,7 +77,7 @@ public class TableActivity extends ActivityWithMenuOptions {
|
|||||||
"\n" +
|
"\n" +
|
||||||
"测试\n" +
|
"测试\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"[https://www.baidu.com](https://www.baidu.com)";
|
"[link link](https://link.link)";
|
||||||
|
|
||||||
final Markwon markwon = Markwon.builder(this)
|
final Markwon markwon = Markwon.builder(this)
|
||||||
.usePlugin(LinkifyPlugin.create())
|
.usePlugin(LinkifyPlugin.create())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user