Table plugin, better borders

This commit is contained in:
Dimitry Ivanov 2020-03-29 15:54:44 +03:00
parent 33f0dcb841
commit ddfa9c98b8
10 changed files with 240 additions and 22 deletions

View File

@ -1,12 +1,16 @@
# Changelog
# 4.3.1-SNAPSHOT
# $nap;
* Fix DexGuard optimization issue ([#216])<br>Thanks [@francescocervone]
* 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
[#224]: https://github.com/noties/Markwon/issues/224
[@francescocervone]: https://github.com/francescocervone
# 4.3.0
* add `MarkwonInlineParserPlugin` in `inline-parser` module
* `JLatexMathPlugin` now supports inline LaTeX structures via `MarkwonInlineParserPlugin`

View File

@ -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);
}
}
}

View 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>

View File

@ -8,4 +8,9 @@
<attr name="fcdv_checked" format="boolean" />
</declare-styleable>
<declare-styleable name="ColorBlendView">
<attr name="cbv_foreground" format="color" />
<attr name="cbv_background" format="color" />
</declare-styleable>
</resources>

View File

@ -1,11 +1,33 @@
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 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);
}
// 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() {
}
}

View File

@ -123,8 +123,13 @@ public class TablePlugin extends AbstractMarkwonPlugin {
visitor.blockStart(tableBlock);
final int length = visitor.length();
visitor.visitChildren(tableBlock);
// @since $nap; apply table span for the full table
visitor.setSpans(length, new TableSpan());
visitor.blockEnd(tableBlock);
}
})

View File

@ -5,6 +5,7 @@ import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.text.Layout;
import android.text.Spanned;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.text.style.ReplacementSpan;
@ -19,6 +20,8 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import io.noties.markwon.utils.LeadingMarginUtils;
public class TableRowSpan extends ReplacementSpan {
public static final int ALIGN_LEFT = 0;
@ -139,16 +142,16 @@ public class TableRowSpan extends ReplacementSpan {
int top,
int y,
int bottom,
@NonNull Paint paint) {
@NonNull Paint p) {
if (recreateLayouts(canvas.getWidth())) {
width = canvas.getWidth();
// @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...
textPaint.set((TextPaint) paint);
textPaint.set((TextPaint) p);
} else {
textPaint.set(paint);
textPaint.set(p);
}
makeNewLayouts();
}
@ -161,28 +164,25 @@ public class TableRowSpan extends ReplacementSpan {
final int w = width / size;
// feels like magic...
final int heightDiff = (bottom - top - height) / 4;
// @since 1.1.1
// draw backgrounds
{
if (header) {
theme.applyTableHeaderRowStyle(this.paint);
theme.applyTableHeaderRowStyle(paint);
} else if (odd) {
theme.applyTableOddRowStyle(this.paint);
theme.applyTableOddRowStyle(paint);
} else {
// even
theme.applyTableEvenRowStyle(this.paint);
theme.applyTableEvenRowStyle(paint);
}
// if present (0 is transparent)
if (this.paint.getColor() != 0) {
if (paint.getColor() != 0) {
final int save = canvas.save();
try {
rect.set(0, 0, width, bottom - top);
canvas.translate(x, top - heightDiff);
canvas.drawRect(rect, this.paint);
canvas.translate(x, top);
canvas.drawRect(rect, paint);
} finally {
canvas.restoreToCount(save);
}
@ -192,14 +192,49 @@ public class TableRowSpan extends ReplacementSpan {
// @since 1.1.1 reset after applying background color
// 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)
this.paint.set(paint);
theme.applyTableBorderStyle(this.paint);
paint.set(p);
theme.applyTableBorderStyle(paint);
final int borderWidth = theme.tableBorderWidth(paint);
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) {
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;
for (int i = 0; i < size; i++) {
@ -207,10 +242,23 @@ public class TableRowSpan extends ReplacementSpan {
final int save = canvas.save();
try {
canvas.translate(x + (i * w), top - heightDiff);
canvas.translate(x + (i * w), top);
// @since $nap;
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);

View File

@ -0,0 +1,7 @@
package io.noties.markwon.ext.tables;
/**
* @since $nap;
*/
public class TableSpan {
}

View File

@ -10,6 +10,7 @@ import androidx.annotation.Px;
import io.noties.markwon.utils.ColorUtils;
import io.noties.markwon.utils.Dip;
@SuppressWarnings("WeakerAccess")
public class TableTheme {
@NonNull
@ -101,7 +102,8 @@ public class TableTheme {
}
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) {

View File

@ -1,17 +1,22 @@
package io.noties.markwon.sample.table;
import android.graphics.Color;
import android.os.Bundle;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import io.noties.debug.Debug;
import io.noties.markwon.Markwon;
import io.noties.markwon.ext.tables.TablePlugin;
import io.noties.markwon.ext.tables.TableTheme;
import io.noties.markwon.linkify.LinkifyPlugin;
import io.noties.markwon.sample.ActivityWithMenuOptions;
import io.noties.markwon.sample.MenuOptions;
import io.noties.markwon.sample.R;
import io.noties.markwon.utils.ColorUtils;
import io.noties.markwon.utils.Dip;
public class TableActivity extends ActivityWithMenuOptions {
@ -19,6 +24,7 @@ public class TableActivity extends ActivityWithMenuOptions {
@Override
public MenuOptions menuOptions() {
return MenuOptions.create()
.add("customize", this::customize)
.add("tableAndLinkify", this::tableAndLinkify);
}
@ -33,6 +39,32 @@ public class TableActivity extends ActivityWithMenuOptions {
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() {
final String md = "" +
"| HEADER | HEADER | HEADER |\n" +
@ -45,7 +77,7 @@ public class TableActivity extends ActivityWithMenuOptions {
"\n" +
"测试\n" +
"\n" +
"[https://www.baidu.com](https://www.baidu.com)";
"[link link](https://link.link)";
final Markwon markwon = Markwon.builder(this)
.usePlugin(LinkifyPlugin.create())