Skip to content

Commit

Permalink
Cleanup and README
Browse files Browse the repository at this point in the history
  • Loading branch information
Dominik Schürmann committed May 14, 2014
1 parent 51d7e3f commit c31ef2a
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 33 deletions.
51 changes: 45 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,40 @@
# HtmlTextView for Android

This HtmlTextView supports all HTML tags supported by Android's Html class (see [The CommonsBlog](http://commonsware.com/blog/Android/2010/05/26/html-tags-supported-by-textview.html) and [history of Html class](https://github.com/android/platform_frameworks_base/commits/master/core/java/android/text/Html.java) for newer additions).
Additionally, list tags are supported (``<ul>``, ``<ol>``, ``<dd>``) and code tags with ``<code>``.
HtmlTextView is an extended TextView component for Android, which can load HTML and converts it into Spannable for displaying it.
It is a replacement for usage of the WebView component, which behaves strange on some Android versions, flickers while Loading, etc.

This also includes a workaround to prevent TextView from crashing on [specific Android versions](http://code.google.com/p/android/issues/detail?id=35466).
## Tags supported by Android ([history of Html class](https://github.com/android/platform_frameworks_base/commits/master/core/java/android/text/Html.java))
* ``<p>``
* ``<div>`` handled exactly like ``<p>``
* ``<br>``
* ``<b>``
* ``<i>``
* ``<strong>`` ([bug on some Android versions: generates italic](https://code.google.com/p/android/issues/detail?id=3473))
* ``<em>`` ([bug on some Android versions: generates bold](https://code.google.com/p/android/issues/detail?id=3473))
* ``<u>``
* ``<strike>``
* ``<tt>``
* ``<dfn>``
* ``<sub>``
* ``<sup>``
* ``<blockquote>``
* ``<cite>``
* ``<big>``
* ``<small>``
* ``<font size="..." color="..." face="...">``
* ``<h1>``, ``<h2>``, ``<h3>``, ``<h4>``, ``<h5>``, ``<h6>``
* ``<a href="...">``
* ``<img src="...">``

## Extended support by HtmlTextView
* ``<ul>``
* ``<ol>``
* ``<dd>``
* ``<li>``
* ``<code>``
* ``<center>``

The library also includes a workaround to prevent TextView from crashing on [specific Android versions](http://code.google.com/p/android/issues/detail?id=35466) and the possibility to load images from local drawables folder or from the Internet.

This library is kept tiny without external dependencies.
I am using it to provide Help/About Activities in my apps.
Expand All @@ -13,15 +44,22 @@ I am using it to provide Help/About Activities in my apps.
```
HtmlTextView text = new HtmlTextView(this);
// loads html from string
text.setHtmlFromString("<b>Hello</b><ul><li>world</li><li>cats</li></ul>");
// loads html from string and displays cat_pic.png from the app's drawable folder
text.setHtmlFromString("<h2>Hello wold</h2><ul><li>cats</li><li>dogs</li></ul><img src="cat_pic"/>", true);
```
or
```
HtmlTextView text = new HtmlTextView(this);
// loads html from string and displays http://www.example.com/cat_pic.png from the Internet
text.setHtmlFromString("<h2>Hello wold</h2><ul><li>cats</li><li>dogs</li></ul><img src="http://www.example.com/cat_pic.png"/>", false);
```
or
```
HtmlTextView text = new HtmlTextView(this);
// loads html from raw resource, i.e., a html file in res/raw/, this allows translatable resource (e.g., res/raw-de/ for german)
text.setHtmlFromRawResource(this, R.raw.help);
text.setHtmlFromRawResource(this, R.raw.help, true);
```

## Use library as Gradle dependency (Android library project)
Expand All @@ -38,6 +76,7 @@ See LICENSE for full license text.
- This library was put together by Dominik Schürmann
- Original [TagHandler](https://gist.github.com/mlakkadshaw/5983704) developed by [Mohammed Lakkadshaw](http://blog.mohammedlakkadshaw.com/)
- Original [UrlImageGetter](https://gist.github.com/Antarix/4167655) developed by Antarix Tandon
- Original [LocalImageGetter](http://stackoverflow.com/a/22298833) developed by drawk
- [JellyBeanSpanFixTextView](https://gist.github.com/pyricau/3424004) (with fix from comment) developed by Pierre-Yves Ricau

## Contributions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@ private static class Center {
public void handleTag(final boolean opening, final String tag, Editable output, final XMLReader xmlReader) {
if (opening) {
// opening tag
if (HtmlTextView.DEBUG)
Log.d(HtmlTextView.TAG, "opening: " + output.toString());
if (HtmlTextView.DEBUG) {
Log.d(HtmlTextView.TAG, "opening, output: " + output.toString());
}

if (tag.equalsIgnoreCase("ul") || tag.equalsIgnoreCase("ol") || tag.equalsIgnoreCase("dd")) {
mListParents.add(tag);
Expand All @@ -61,8 +62,9 @@ public void handleTag(final boolean opening, final String tag, Editable output,
}
} else {
// closing tag
if (HtmlTextView.DEBUG)
Log.d(HtmlTextView.TAG, "closing: " + output.toString());
if (HtmlTextView.DEBUG) {
Log.d(HtmlTextView.TAG, "closing, output: " + output.toString());
}

if (tag.equalsIgnoreCase("ul") || tag.equalsIgnoreCase("ol") || tag.equalsIgnoreCase("dd")) {
mListParents.remove(tag);
Expand All @@ -77,18 +79,27 @@ public void handleTag(final boolean opening, final String tag, Editable output,
}
}

/**
* Mark the opening tag by using private classes
*
* @param output
* @param mark
*/
private void start(Editable output, Object mark) {
int len = output.length();
output.setSpan(mark, len, len, Spannable.SPAN_MARK_MARK);

if (HtmlTextView.DEBUG)
if (HtmlTextView.DEBUG) {
Log.d(HtmlTextView.TAG, "len: " + len);
}
}

private void end(Editable output, Class kind, Object repl, boolean paragraphStyle) {
int len = output.length();
Object obj = getLast(output, kind);
// start of the tag
int where = output.getSpanStart(obj);
// end of the tag
int len = output.length();

output.removeSpan(obj);

Expand All @@ -107,6 +118,13 @@ private void end(Editable output, Class kind, Object repl, boolean paragraphStyl
}
}

/**
* Get last marked position of a specific tag kind (private class)
*
* @param text
* @param kind
* @return
*/
private Object getLast(Editable text, Class kind) {
Object[] objs = text.getSpans(0, text.length(), kind);
if (objs.length == 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,18 @@
import android.widget.TextView;

/**
* <p>
* <p/>
* A {@link android.widget.TextView} that insert spaces around its text spans where needed to prevent
* {@link IndexOutOfBoundsException} in {@link #onMeasure(int, int)} on Jelly Bean.
* <p>
* <p/>
* When {@link #onMeasure(int, int)} throws an exception, we try to fix the text by adding spaces
* around spans, until it works again. We then try removing some of the added spans, to minimize the
* insertions.
* <p>
* <p/>
* The fix is time consuming (a few ms, it depends on the size of your text), but it should only
* happen once per text change.
* <p>
* <p/>
* See http://code.google.com/p/android/issues/detail?id=35466
*
*/
public class JellyBeanSpanFixTextView extends TextView {

Expand All @@ -50,7 +49,7 @@ private static class FixingResult {
public final List<Object> spansWithSpacesAfter;

public static FixingResult fixed(List<Object> spansWithSpacesBefore,
List<Object> spansWithSpacesAfter) {
List<Object> spansWithSpacesAfter) {
return new FixingResult(true, spansWithSpacesBefore, spansWithSpacesAfter);
}

Expand All @@ -59,7 +58,7 @@ public static FixingResult notFixed() {
}

private FixingResult(boolean fixed, List<Object> spansWithSpacesBefore,
List<Object> spansWithSpacesAfter) {
List<Object> spansWithSpacesAfter) {
this.fixed = fixed;
this.spansWithSpacesBefore = spansWithSpacesBefore;
this.spansWithSpacesAfter = spansWithSpacesAfter;
Expand Down Expand Up @@ -96,7 +95,7 @@ private void fixOnMeasure(int widthMeasureSpec, int heightMeasureSpec) {
SpannableStringBuilder builder = new SpannableStringBuilder(text);
fixSpannedWithSpaces(builder, widthMeasureSpec, heightMeasureSpec);
} else {
if (BuildConfig.DEBUG) {
if (HtmlTextView.DEBUG) {
Log.d(HtmlTextView.TAG, "The text isn't a Spanned");
}
fallbackToString(widthMeasureSpec, heightMeasureSpec);
Expand All @@ -107,7 +106,7 @@ private void fixOnMeasure(int widthMeasureSpec, int heightMeasureSpec) {
* Add spaces around spans until the text is fixed, and then removes the unneeded spaces
*/
private void fixSpannedWithSpaces(SpannableStringBuilder builder, int widthMeasureSpec,
int heightMeasureSpec) {
int heightMeasureSpec) {
long startFix = System.currentTimeMillis();

FixingResult result = addSpacesAroundSpansUntilFixed(builder, widthMeasureSpec,
Expand All @@ -119,14 +118,14 @@ private void fixSpannedWithSpaces(SpannableStringBuilder builder, int widthMeasu
fallbackToString(widthMeasureSpec, heightMeasureSpec);
}

if (BuildConfig.DEBUG) {
if (HtmlTextView.DEBUG) {
long fixDuration = System.currentTimeMillis() - startFix;
Log.d(HtmlTextView.TAG, "fixSpannedWithSpaces() duration in ms: " + fixDuration);
}
}

private FixingResult addSpacesAroundSpansUntilFixed(SpannableStringBuilder builder,
int widthMeasureSpec, int heightMeasureSpec) {
int widthMeasureSpec, int heightMeasureSpec) {

Object[] spans = builder.getSpans(0, builder.length(), Object.class);
List<Object> spansWithSpacesBefore = new ArrayList<Object>(spans.length);
Expand All @@ -151,15 +150,16 @@ private FixingResult addSpacesAroundSpansUntilFixed(SpannableStringBuilder build
} catch (IndexOutOfBoundsException notFixed) {
}
}
if (BuildConfig.DEBUG) {
if (HtmlTextView.DEBUG) {
Log.d(HtmlTextView.TAG, "Could not fix the Spanned by adding spaces around spans");
}
return FixingResult.notFixed();
}

private boolean isNotSpace(CharSequence text, int where) {
if (where < 0)
if (where < 0) {
return true;
}
return text.charAt(where) != ' ';
}

Expand All @@ -169,7 +169,7 @@ private void setTextAndMeasure(CharSequence text, int widthMeasureSpec, int heig
}

private void removeUnneededSpaces(int widthMeasureSpec, int heightMeasureSpec,
SpannableStringBuilder builder, FixingResult result) {
SpannableStringBuilder builder, FixingResult result) {

for (Object span : result.spansWithSpacesAfter) {
int spanEnd = builder.getSpanEnd(span);
Expand Down Expand Up @@ -202,7 +202,7 @@ private void removeUnneededSpaces(int widthMeasureSpec, int heightMeasureSpec,
}

private void fallbackToString(int widthMeasureSpec, int heightMeasureSpec) {
if (BuildConfig.DEBUG) {
if (HtmlTextView.DEBUG) {
Log.d(HtmlTextView.TAG, "Fallback to unspanned text");
}
String fallbackText = getText().toString();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2014 drawk
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@ public Drawable getDrawable(String source) {

asyncTask.execute(source);

// return reference to URLDrawable where I will change with actual image from
// the src tag
// return reference to URLDrawable which will asynchronously load the image specified in the src tag
return urlDrawable;
}

Expand All @@ -78,8 +77,7 @@ protected void onPostExecute(Drawable result) {
// set the correct bound according to the result from HTTP call
urlDrawable.setBounds(0, 0, 0 + result.getIntrinsicWidth(), 0 + result.getIntrinsicHeight());

// change the reference of the current drawable to the result
// from the HTTP call
// change the reference of the current drawable to the result from the HTTP call
urlDrawable.drawable = result;

// redraw the image by invalidating the container
Expand Down Expand Up @@ -113,8 +111,6 @@ private InputStream fetch(String urlString) throws MalformedURLException, IOExce

@SuppressWarnings("deprecation")
public class UrlDrawable extends BitmapDrawable {
// the drawable that you need to set, you could set the initial drawing
// with the loading image if you need to
protected Drawable drawable;

@Override
Expand Down

0 comments on commit c31ef2a

Please sign in to comment.