Table plugin, better borders
This commit is contained in:
		
							parent
							
								
									33f0dcb841
								
							
						
					
					
						commit
						ddfa9c98b8
					
				| @ -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`  | ||||
|  | ||||
| @ -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" /> | ||||
|     </declare-styleable> | ||||
| 
 | ||||
|     <declare-styleable name="ColorBlendView"> | ||||
|         <attr name="cbv_foreground" format="color" /> | ||||
|         <attr name="cbv_background" format="color" /> | ||||
|     </declare-styleable> | ||||
| 
 | ||||
| </resources> | ||||
| @ -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() { | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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); | ||||
|                         } | ||||
|                     }) | ||||
|  | ||||
| @ -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,25 +192,73 @@ 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++) { | ||||
|             layout = layouts.get(i); | ||||
|             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); | ||||
|  | ||||
| @ -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.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) { | ||||
|  | ||||
| @ -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()) | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Dimitry Ivanov
						Dimitry Ivanov