Bien que je puisse le voir dans l'API Html.java que style et text-align devraient être utilisables avec les balises <p>
, <div>
etc. Je ne parviens pas à le faire fonctionner avec <p align="center">
o <p style="text-align: center">
et de nombreuses autres variantes. Ne pouvant pas faire cet alignement central du texte, et d'autres styles comme la taille de la police, plusieurs visages de police à partir de mes fichiers ttf, la couleur de fond, j'ai fait mon propre htmlTextView basé sur TextView mais avec ma propre classe tagHandler. À part une ou deux irritations mineures, la plupart des balises fonctionnent bien, sauf mes balises d'alignement personnalisées, gauche , centre , droite ne fonctionnent que dans des conditions particulières (que je ne comprends pas), sinon. ils ne fonctionnent pas ou plantent l'application ! Voici ma poignée de balises d'alignement. Il a la même structure que tous les autres gestionnaires de balises personnalisés mais se comporte vraiment bizarrement ! La forme de base de mes gestionnaires de balises est la même et ne sont pas conçues par moi ! J'ai trouvé le modèle taghandler après de nombreuses heures de recherche sur le web. Je suis reconnaissant à celui qui l'a posté, mais ma mémoire et mes capacités d'organisation sont telles que je ne peux pas vraiment me souvenir de qui ou d'où, donc si vous reconnaissez ce code comme étant le vôtre, faites-le moi savoir. Le seul lien (qui est ici) que j'ai est dans mon code : stackoverflow : Android : Comment utiliser le 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);
}
}
}
Je pense que le problème est que la balise de fin n'est pas connectée à la bonne balise de début.
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;
}
}
C'est la classe totale et quelque chose ne va pas. Le plus grand composant est ma compréhension ! Peut-être que quelqu'un peut m'aider à mieux comprendre...
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://stackoverflow.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);
}
}
}
Le htmlTextView est créé à partir de l'activité avec :
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);
}
et htmljumblies est défini dans strings.xml comme ci-dessous. Cette version particulière fera planter l'application mais si la première version de <centre>
, </centre>
sont supprimées des lignes 7 et 9, Les Jumblies apparaîtront centralisés ? Confusion et frustration ! Gardez-les et supprimez les <centre>
, </centre>
balises d'enveloppement Les Jumblies et rien ne se passe. La ligne de l'en-tête n'est pas alignée au 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>