style - ios textview html
Android:如何使用Html.TagHandler? (4)
我正在尝试为留言板构建一个Android应用程序。 要显示帖子内容的格式化html,我选择了TextView和Html.fromHtml()方法。 遗憾的是,这只涵盖了几个html标签。 未知标记由实现TagHandler的类处理,并且必须由我自己生成。
现在,我搜索了很多,无法找到这个类应该如何工作的例子。 让我们考虑我有一个用于标记某些文本的u标记(我知道这已被弃用,但无论如何)。 我的TagHandler是怎样的?
它以下列方式调用:
public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) {
前两个论点都没问题。 我想我必须使用output.append()修改输出。 但是我如何附加在那里加下划线的东西?
此解决方案可在Android sdk中找到
在android.text.html
。 596 - 626行。复制/粘贴
private static <T> Object getLast(Spanned text, Class<T> 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(SpannableStringBuilder text, Object mark) {
int len = text.length();
text.setSpan(mark, len, len, Spannable.SPAN_MARK_MARK);
}
private static <T> void end(SpannableStringBuilder text, Class<T> 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);
}
}
要使用,请覆盖TagHandler,如下所示:
public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) {
if(tag.equalsIgnoreCase("strike") || tag.equals("s")) {
if(opening){
start((SpannableStringBuilder) output, new Strike();
} else {
end((SpannableStringBuilder) output, Strike.class, new StrikethroughSpan());
}
}
}
/*
* Notice this class. It doesn't really do anything when it spans over the text.
* The reason is we just need to distinguish what needs to be spanned, then on our closing
* tag, we will apply the spannable. For each of your different spannables you implement, just
* create a class here.
*/
private static class Strike{}
我们已经在内部开发了这个库https://github.com/square1-io/rich-text-android一段时间了,我们在许多内容密集型新闻应用程序中使用它。
该库可以解析最常见的html标签,包括视频和img以及远程下载图像。 然后,可以使用自定义视图RichTextView替换TextView以显示已解析的内容。
我们最近公开发布了它,因此文档仍然不完整,但提供的示例应该很容易理解,看它是否符合您的需求。
所以,我终于自己弄明白了。
public class MyHtmlTagHandler implements TagHandler {
public void handleTag(boolean opening, String tag, Editable output,
XMLReader xmlReader) {
if(tag.equalsIgnoreCase("strike") || tag.equals("s")) {
processStrike(opening, output);
}
}
private void processStrike(boolean opening, Editable output) {
int len = output.length();
if(opening) {
output.setSpan(new StrikethroughSpan(), len, len, Spannable.SPAN_MARK_MARK);
} else {
Object obj = getLast(output, StrikethroughSpan.class);
int where = output.getSpanStart(obj);
output.removeSpan(obj);
if (where != len) {
output.setSpan(new StrikethroughSpan(), where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
private Object getLast(Editable text, Class kind) {
Object[] objs = text.getSpans(0, text.length(), kind);
if (objs.length == 0) {
return null;
} else {
for(int i = objs.length;i>0;i--) {
if(text.getSpanFlags(objs[i-1]) == Spannable.SPAN_MARK_MARK) {
return objs[i-1];
}
}
return null;
}
}
}
对于TextView,您可以将其称为:
myTextView.setText (Html.fromHtml(text.toString(), null, new MyHtmlTagHandler()));
如果有人需要它。
干杯
虽然我可以在Html.java API中看到它,但样式和text-align应该可以与标签<p>
, <div>
等一起使用。我无法使用<p align="center">
或<p style="text-align: center">
和许多其他变体。 无法进行文本的中心对齐,以及其他样式,如字体大小,来自我的ttf文件的多个字体,背景颜色,我已经基于TextView创建了自己的htmlTextView,但是使用了我自己的tagHandler类。 鉴于一两个轻微的刺激,大多数标签都很好,但我的自定义对齐标签, 左 , 中 , 右,仅在特殊条件下工作(我不明白),否则。 他们不工作或崩溃的应用程序! 这是我的对齐标记句柄。 它具有与所有其他自定义标记处理程序相同的结构,但实际上表现得很奇怪! 我的标签处理程序的基本形式是相同的, 并不是我构思的 ! 我在网上搜索了几个小时后找到了taghandler模板。 我很感激发布它的人,但我的记忆和组织能力使我无法记住谁或在哪里,所以如果你认识这段代码,请告诉我。 我唯一的链接(在这里)是在我的代码中: :Android:如何使用Html.TagHandler?
private void ProcessAlignment(Layout.Alignment align, boolean opening, Editable output) {
int len = output.length();
if (opening) {
output.setSpan(new AlignmentSpan.Standard(align), len, len, Spannable.SPAN_MARK_MARK);
} else {
Object obj = getLast(output, AlignmentSpan.Standard.class);
int where = output.getSpanStart(obj);
output.removeSpan(obj);
if (where != len) {
output.setSpan(new AlignmentSpan.Standard(align), where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
我认为问题是结束标记没有连接正确的开始标记。
private Object getLast(Editable text, Class kind) {
Object[] objs = text.getSpans(0, text.length(), kind);
if (objs.length == 0) {
return null;
} else {
for (int i = objs.length - 1; i >= 0; --i) {
if (text.getSpanFlags(objs[i]) == Spannable.SPAN_MARK_MARK) {
return objs[i];
}
}
return null;
}
}
这是总课程,有些事情是不对的。 最大的组成部分是我的理解! 也许有人可以帮助我更好地理解......
public class htmlTextView extends AppCompatTextView {
static Typeface mLogo;
static Typeface mGAMZ;
static Typeface mChalk;
static Typeface mSouvenir;
int GS_PAINTFLAGS = FILTER_BITMAP_FLAG | ANTI_ALIAS_FLAG | SUBPIXEL_TEXT_FLAG | HINTING_ON;
public htmlTextView(Context context) {
super(context);
initialise();
}
public htmlTextView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initialise();
}
public htmlTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialise();
}
private void initialise() {
mLogo = Typeface.createFromAsset(theAssetManager, "fonts/logo.ttf");
mGAMZ = Typeface.createFromAsset(theAssetManager, "fonts/GAMZ One.ttf");
mChalk = Typeface.createFromAsset(theAssetManager, "fonts/swapfix.ttf");
mSouvenir = Typeface.createFromAsset(theAssetManager, "fonts/Souvenir Regular.ttf");
setPaintFlags(GS_PAINTFLAGS);
}
public void setDefaultTypefaceSouvenir() {
setTypeface(mSouvenir);
}
public void setDefaultTypefaceGAMZ() {
setTypeface(mGAMZ);
}
public void setDefaultTypefaceChalk() {
setTypeface(mChalk);
}
/*public myTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}*/
public void setHTML(String htmltext) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // Nougat API 24
setText(Html.fromHtml(htmltext, Html.FROM_HTML_MODE_LEGACY,
null, new TypefaceTagHandler()));
} else {
setText(Html.fromHtml(htmltext, null, new TypefaceTagHandler()));
}
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
@Override
public Bitmap getDrawingCache(boolean autoScale) {
return super.getDrawingCache(autoScale);
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
}
// http://.com/questions/4044509/android-how-to-use-the-html-taghandler
private static class TypefaceTagHandler implements Html.TagHandler {
private void ProcessAlignment(Layout.Alignment align, boolean opening, Editable output) {
int len = output.length();
if (opening) {
output.setSpan(new AlignmentSpan.Standard(align), len, len, Spannable.SPAN_MARK_MARK);
} else {
Object obj = getLast(output, AlignmentSpan.Standard.class);
int where = output.getSpanStart(obj);
output.removeSpan(obj);
if (where != len) {
output.setSpan(new AlignmentSpan.Standard(align), where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
private void ProcessTypefaceTag(Typeface tf, boolean opening, Editable output) {
int len = output.length();
if (opening) {
output.setSpan(new CustomTypefaceSpan("", tf), len, len,
Spannable.SPAN_MARK_MARK);
} else {
Object obj = getLast(output, CustomTypefaceSpan.class);
int where = output.getSpanStart(obj);
output.removeSpan(obj);
if (where != len) {
output.setSpan(new CustomTypefaceSpan("", tf), where, len,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
private void ProcessScaleTag(float scalefactor, boolean opening, Editable output) {
int len = output.length();
if (opening) {
output.setSpan(new RelativeSizeSpan(scalefactor), len, len,
Spannable.SPAN_MARK_MARK);
} else {
Object obj = getLast(output, RelativeSizeSpan.class);
int where = output.getSpanStart(obj);
output.removeSpan(obj);
if (where != len) {
output.setSpan(new RelativeSizeSpan(scalefactor), where, len,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
private void ProcessBox(int colour, boolean opening, Editable output) {
int len = output.length();
if (opening) {
output.setSpan(new BackgroundColorSpan(colour), len, len,
Spannable.SPAN_MARK_MARK);
} else {
Object obj = getLast(output, BackgroundColorSpan.class);
int where = output.getSpanStart(obj);
output.removeSpan(obj);
if (where != len) {
output.setSpan(new BackgroundColorSpan(colour), where, len,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
private void ProcessTextColour(int colour, boolean opening, Editable output) {
int len = output.length();
if (opening) {
output.setSpan(new ForegroundColorSpan(colour), len, len,
Spannable.SPAN_MARK_MARK);
} else {
Object obj = getLast(output, ForegroundColorSpan.class);
int where = output.getSpanStart(obj);
output.removeSpan(obj);
if (where != len) {
output.setSpan(new ForegroundColorSpan(colour), where, len,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
final HashMap<String, String> attributes = new HashMap<>();
@Override
public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) {
String Attr = "";
//if (!opening) attributes.clear();
processAttributes(xmlReader);
if ("txt".equalsIgnoreCase(tag)) {
Attr = attributes.get("clr");
System.out.println("clr Attr: " + Attr + ", opening: " + opening);
if (Attr == null || Attr.isEmpty()
|| "black".equalsIgnoreCase(Attr)
|| Attr.charAt(0) == 'k') {
System.out.println("did black, opening: " + opening);
ProcessTextColour(parseColor("#000000"), opening, output);
} else {
if (Attr.equalsIgnoreCase("g")) {
ProcessTextColour(parseColor("#b2b3b3"), opening, output);
} else {
System.out.println("did colour, opening: " + opening);
ProcessTextColour(parseColor(Attr), opening, output);
}
}
return;
}
if ("box".equalsIgnoreCase(tag)) {
ProcessBox(parseColor("#d7d6d5"), opening, output);
return;
}
if ("scl".equalsIgnoreCase(tag)) {
Attr = attributes.get("fac");
System.out.println("scl Attr: " + Attr);
if (Attr != null && !Attr.isEmpty()) {
ProcessScaleTag(parseFloat(Attr), opening, output);
}
return;
}
if ("left".equalsIgnoreCase(tag)) {
ProcessAlignment(Layout.Alignment.ALIGN_NORMAL, opening, output);
return;
}
if ("centre".equalsIgnoreCase(tag)) {
ProcessAlignment(Layout.Alignment.ALIGN_CENTER, opening, output);
return;
}
if ("right".equalsIgnoreCase(tag)) {
ProcessAlignment(Layout.Alignment.ALIGN_OPPOSITE, opening, output);
return;
}
if ("logo".equalsIgnoreCase(tag)) {
ProcessTypefaceTag(mLogo, opening, output);
return;
}
if ("gamz".equalsIgnoreCase(tag)) {
ProcessTypefaceTag(mGAMZ, opening, output);
return;
}
if ("chalk".equalsIgnoreCase(tag)) {
System.out.println("chalk " + (opening ? "opening" : "closing"));
ProcessTypefaceTag(mChalk, opening, output);
return;
}
}
private Object getLast(Editable text, Class kind) {
Object[] objs = text.getSpans(0, text.length(), kind);
if (objs.length == 0) {
return null;
} else {
for (int i = objs.length - 1; i >= 0; --i) {
if (text.getSpanFlags(objs[i]) == Spannable.SPAN_MARK_MARK) {
return objs[i];
}
}
return null;
}
}
private void processAttributes(final XMLReader xmlReader) {
try {
Field elementField = xmlReader.getClass().getDeclaredField("theNewElement");
elementField.setAccessible(true);
Object element = elementField.get(xmlReader);
Field attsField = element.getClass().getDeclaredField("theAtts");
attsField.setAccessible(true);
Object atts = attsField.get(element);
Field dataField = atts.getClass().getDeclaredField("data");
dataField.setAccessible(true);
String[] data = (String[])dataField.get(atts);
Field lengthField = atts.getClass().getDeclaredField("length");
lengthField.setAccessible(true);
int len = (Integer)lengthField.get(atts);
/**
* MSH: Look for supported attributes and add to hash map.
* This is as tight as things can get :)
* The data index is "just" where the keys and values are stored.
*/
for(int i = 0; i < len; i++)
attributes.put(data[i * 5 + 1], data[i * 5 + 4]);
}
catch (Exception e) {
Log.d(TAG, "Exception: " + e);
}
}
}
private static class CustomTypefaceSpan extends TypefaceSpan {
private final Typeface newType;
public CustomTypefaceSpan(String family, Typeface type) {
super(family);
newType = type;
}
@Override
public void updateDrawState(TextPaint ds) {
applyCustomTypeFace(ds, newType);
}
@Override
public void updateMeasureState(TextPaint paint) {
applyCustomTypeFace(paint, newType);
}
private void applyCustomTypeFace(Paint paint, Typeface tf) {
int oldStyle;
Typeface old = paint.getTypeface();
if (old == null) {
oldStyle = 0;
} else {
oldStyle = old.getStyle();
}
int fake = oldStyle & ~tf.getStyle();
if ((fake & Typeface.BOLD) != 0) {
paint.setFakeBoldText(true);
}
if ((fake & Typeface.ITALIC) != 0) {
paint.setTextSkewX(-0.25f);
}
paint.setTypeface(tf);
}
}
}
htmlTextView是从活动创建的:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
theAssetManager = getAssets();
htmlTextView tv = new htmlTextView(this);
tv.setDefaultTypefaceSouvenir();
tv.setTextColor(BLACK);
tv.setBackgroundColor(0xfff0f0f0);
tv.setPadding(4, 4, 4, 4);
tv.setTextSize(30);
tv.setMovementMethod(new ScrollingMovementMethod());
tv.setHTML(getString(R.string.htmljumblies));
//tv.setHTML(getString(R.string.htmltest));
RelativeLayout rl = (RelativeLayout) findViewById(R.id.rl);
rl.addView(tv);
}
和htmljumblies在strings.xml中定义如下。 此特定版本将使应用程序崩溃,但如果从第7行和第9行删除第一个<centre>
, </centre>
标记,则Jumblies将显示为集中式? 令人困惑和沮丧! 保留它们并删除包含Jumblies的<centre>
, </centre>
标签,没有任何反应。 标题中的行不是中心对齐的!
<string name="htmljumblies">
<![CDATA[⟹<logo><scl fac="1.1"><font color="#e5053a">GAMZ</font></scl></logo>
<chalk><scl fac="1.8"> SWAP </scl></chalk>
<scl fac="1.00">Set <b>1</b>, Game <b>1</b></scl>
<br>
<centre>
<gamz><font color="#e5053a"><scl fac="1.50">a</scl></font><scl fac="0.90">(9)</scl></gamz>, <gamz><font color="#00a3dd"><scl fac="1.50">e</scl></font><scl fac="0.90">(8)</scl></gamz>, <gamz><font color="#fba311"><scl fac="1.50">i</scl></font><scl fac="0.90">(8)</scl></gamz>, <gamz><font color="#bc5e1e"><scl fac="1.50">o</scl></font><scl fac="0.90">(8)</scl></gamz>, <gamz><font color="#bf30b5"><scl fac="1.50">u</scl></font><scl fac="0.90">(9)</scl></gamz>
</centre>
<br>
This is an example of my custom <b>htmlTextView</b> drawn from HTML format
text with custom tags to use custom fonts, colouring typeface sizing and highlight boxes.
The default font is <b><i>Souvenir</i></b>, but 3 other fonts are used:<br>
The <font color="#e5053a"><b><logo><scl fac="1.1">GAMZ</scl></logo></b></font>
<font color="#000080"><gamz><scl fac="0.8"><box>letter</box>
<box>fonts</box><sc></gamz></font>
and <chalk><scl fac="1.8">swapfix</scl></chalk>, essentially
<chalk><scl fac="0.9">Staccato 555</scl></chalk>,
as used in the words <chalk><scl fac="1.2">SWAP</scl></chalk> and
<chalk><scl fac="1.2">FIX</scl></chalk>
on the <font color="#e5053a"><b><logo><scl fac="1.1">GAMZ</scl></logo></b></font>
boxes.
<br>
<centre>
<scl fac="2"><box><b> <u>The Jumblies</u> </b></box></scl><br>
<font color="#0000ff">
They went to sea in a Sieve, they did,<br>
In a Sieve they went to sea:<br>
In spite of all their friends could say,<br>
On a winter\'s morn, on a stormy day,<br>
In a Sieve they went to sea!<br>
And when the Sieve turned round and round,<br>
And every one cried, \'You\'ll all be drowned!\'<br>
They called aloud, \'Our Sieve ain\'t big,<br>
But we don\'t care a button! we don\'t care a fig!<br>
In a Sieve we\'ll go to sea!\'<br>
Far and few, far and few,<br>
Are the lands where the Jumblies live;<br>
Their heads are green, and their hands are blue,<br>
And they went to sea in a Sieve.<br>
<br>
They sailed away in a Sieve, they did,<br>
In a Sieve they sailed so fast,<br>
With only a beautiful pea-green veil<br>
Tied with a riband by way of a sail,<br>
To a small tobacco-pipe mast;<br>
And every one said, who saw them go,<br>
\'O won\'t they be soon upset, you know!<br>
For the sky is dark, and the voyage is long,<br>
And happen what may, it\'s extremely wrong<br>
In a Sieve to sail so fast!\'<br>
Far and few, far and few,<br>
Are the lands where the Jumblies live;<br>
Their heads are green, and their hands are blue,<br>
And they went to sea in a Sieve.<br>
<br>
The water it soon came in, it did,<br>
The water it soon came in;<br>
So to keep them dry, they wrapped their feet<br>
In a pinky paper all folded neat,<br>
And they fastened it down with a pin.<br>
And they passed the night in a crockery-jar,<br>
And each of them said, \'How wise we are!<br>
Though the sky be dark, and the voyage be long,<br>
Yet we never can think we were rash or wrong,<br>
While round in our Sieve we spin!\'<br>
Far and few, far and few,<br>
Are the lands where the Jumblies live;<br>
Their heads are green, and their hands are blue,<br>
And they went to sea in a Sieve.<br>
<br>
And all night long they sailed away;<br>
And when the sun went down,<br>
They whistled and warbled a moony song<br>
To the echoing sound of a coppery gong,<br>
In the shade of the mountains brown.<br>
\'O Timballo! How happy we are,<br>
When we live in a Sieve and a crockery-jar,<br>
And all night long in the moonlight pale,<br>
We sail away with a pea-green sail,<br>
In the shade of the mountains brown!\'<br>
Far and few, far and few,<br>
Are the lands where the Jumblies live;<br>
Their heads are green, and their hands are blue,<br>
And they went to sea in a Sieve.<br>
<br>
They sailed to the Western Sea, they did,<br>
To a land all covered with trees,<br>
And they bought an Owl, and a useful Cart,<br>
And a pound of Rice, and a Cranberry Tart,<br>
And a hive of silvery Bees.<br>
And they bought a Pig, and some green Jack-daws,<br>
And a lovely Monkey with lollipop paws,<br>
And forty bottles of Ring-Bo-Ree,<br>
And no end of Stilton Cheese.<br>
Far and few, far and few,<br>
Are the lands where the Jumblies live;<br>
Their heads are green, and their hands are blue,<br>
And they went to sea in a Sieve.<br>
<br>
And in twenty years they all came back,<br>
In twenty years or more,<br>
And every one said, \'How tall they\'ve grown!<br>
For they\'ve been to the Lakes, and the Torrible Zone,<br>
And the hills of the Chankly Bore!\'<br>
And they drank their health, and gave them a feast<br>
Of dumplings made of beautiful yeast;<br>
And every one said, \'If we only live,<br>
We too will go to sea in a Sieve,---<br>
To the hills of the Chankly Bore!\'<br>
Far and few, far and few,<br>
Are the lands where the Jumblies live;<br>
Their heads are green, and their hands are blue,<br>
And they went to sea in a Sieve.</centre></font>
]]>
</string>