I'm encountering a strange issue when applying my custom QuoteSpan.
It includes all the text after this ending tag: </quote>
. But it works when I tried to replace <quote>...</quote>
into <blockquote>...</blockquote>
to skip my Custom HtmlTagHandler and to use the default implementation of QuoteSpan by Android. See screenshots below:
Expected Result (Using the default QuoteSpan): https://i.stack.imgur.com/bADnU.png
Current Output (Using my custom QuoteSpan): https://i.stack.imgur.com/VFpkz.png
Custom HtmlTagHandler - for unhandled html tags (for the quote
tag)
package com.demoparser.http.parser;
import android.graphics.Typeface;
import android.text.Editable;
import android.text.Html;
import android.text.Spannable;
import android.text.Spanned;
import android.text.style.RelativeSizeSpan;
import android.text.style.StrikethroughSpan;
import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan;
import com.demoparser.util.CustomQuoteSpan;
import org.xml.sax.XMLReader;
public class HtmlTagHandler implements Html.TagHandler {
private static final float[] HEADER_SIZES = {
1.5f, 1.4f, 1.3f, 1.2f, 1.1f, 1f,
};
@Override
public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) {
if (tag.equalsIgnoreCase("del")) {
if (opening) {
start(output, new Strikethrough());
} else {
end(output, Strikethrough.class, new StrikethroughSpan());
}
} else if (tag.equalsIgnoreCase("pre")) {
if (opening) {
start(output, new Monospace());
} else {
end(output, Monospace.class, new TypefaceSpan("monospace"));
}
} else if (tag.equalsIgnoreCase("quote")) {
if (opening) {
handleP(output);
start(output, new BlockQuote());
} else {
handleP(output);
end(output, BlockQuote.class, new CustomQuoteSpan());
}
}
}
private static void handleP(Editable text) {
int len = text.length();
if (len >= 1 && text.charAt(len - 1) == '\n') {
if (len >= 2 && text.charAt(len - 2) == '\n') {
return;
}
text.append("\n");
return;
}
if (len != 0) {
text.append("\n\n");
}
}
private static Object getLast(Spanned text, Class kind) {
/*
* This knows that the last returned object from getSpans()
* will be the most recently added.
*/
Object[] objs = text.getSpans(0, text.length(), kind);
if (objs.length == 0) {
return null;
} else {
return objs[objs.length - 1];
}
}
private static void start(Editable text, Object mark) {
int len = text.length();
text.setSpan(mark, len, len, Spannable.SPAN_MARK_MARK);
}
private static void end(Editable text, Class kind, Object repl) {
int len = text.length();
Object obj = getLast(text, kind);
int where = text.getSpanStart(obj);
text.removeSpan(obj);
if (where != len) {
text.setSpan(repl, where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
public static class Strikethrough {}
public static class Monospace {}
public static class BlockQuote {}
}
Custom QuoteSpan
package com.demoparser.util;
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import android.graphics.Canvas;
import android.graphics.Paint;
import android.text.Layout;
import android.text.style.LeadingMarginSpan;
import android.text.style.LineBackgroundSpan;
public class CustomQuoteSpan implements LeadingMarginSpan, LineBackgroundSpan {
private static final int STRIPE_WIDTH = 5;
private static final int GAP_WIDTH = 8;
private final int mBackgroundColor;
private final int mColor;
public QuoteSpan() {
super();
mBackgroundColor = 0xffddf1fd;
mColor = 0xff098fdf;
}
public int getLeadingMargin(boolean first) {
return STRIPE_WIDTH + GAP_WIDTH;
}
public void drawLeadingMargin(Canvas c, Paint p, int x, int dir,
int top, int baseline, int bottom,
CharSequence text, int start, int end,
boolean first, Layout layout) {
Paint.Style style = p.getStyle();
int color = p.getColor();
p.setStyle(Paint.Style.FILL);
p.setColor(mColor);
c.drawRect(x, top, x + dir * STRIPE_WIDTH, bottom, p);
p.setStyle(style);
p.setColor(color);
}
@Override
public void drawBackground(Canvas c, Paint p, int left, int right, int top, int baseline, int bottom, CharSequence text, int start, int end, int lnum) {
int paintColor = p.getColor();
p.setColor(mBackgroundColor);
c.drawRect(left, top, right, bottom, p);
p.setColor(paintColor);
}
}
And the HTML elements that I'm trying to apply the custom QuoteSpan:
<quote>
<div class="q_b">
<div class="q_tl">
<div class="q_tr">
<div class="q_bl">
<div class="q_br">
<div class="q_body">
<b class="qfont">Quote:</b>
<div style="padding:5px;">
<div class="q_by">
Originally Posted by
<strong>: <a href="/index.php?thread=1#msg2"> username on 1-1-2016 00:00 AM</a></strong>
</div>
Sample Quoted Text 1
<br>
<br>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</quote>
<br>
<br>
This message should not be inside the QuoteSpan
I'm using Html.fromHtml(...)
to render the span.
Html.fromHtml(message, null, new HtmlTagHandler())
Here's the Logs for the index of opening and closing tag:
START: 0
END: 138 <-- should be 87
Anyone have any idea to achieve the same result as the default QuoteSpan's output or point it out the wrong implementation? Thank you in advance.
Instead of using a Custom Quote Span use the Quote Span class constructor that accepts a Parcel and use this Java code :
If you want in Kotlin see this Repository : Custom Android Quote Span with AppCompat support