问题描述
限时送ChatGPT账号..背景
我知道如何在静态文本中的部分文本上设置多个跨度,正如我在
计划是%1$d 个月"的文本颜色为#3792e5",并有一个比默认下划线略深的特殊下划线.我使用了一个特殊的自定义标签uu"作为特殊的下划线,在代码中处理.
问题是,无论我做什么,我都找不到如何同时显示文本颜色和下划线.
我的尝试
由于这个问题有一个占位符(并且要格式化的文本周围的文本可能不同),我不得不使用Html.FromHtml":
String formattedStr = getString(R.string.potential_free_upgrade_1_d_months, 9);Spanned textToShow = Html.fromHtml(formattedStr, null, new TagHandler() {整数开始;@覆盖public void handleTag(final boolean opening, final String tag, Editable output, final XMLReader xmlReader) {开关(标签){案例uu":如果(开场)start = output.length();别的 {int end = output.length();//output.setSpan(new ForegroundColorSpan(0xff3792e5), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);输出.setSpan(new DrawableSpan(ResourcesCompat.getDrawable(getResources(), R.drawable.bit_below_underline, null)),开始, 结束, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);}}}});titleTextView.setText(textToShow);
DrawableSpan.java
public class DrawableSpan extends ReplacementSpan {私有 Drawable mDrawable;私有最终 Rect mPadding;公共 DrawableSpan(Drawable drawable) {极好的();mDrawable = 可绘制;mPadding = new Rect();mDrawable.getPadding(mPadding);}@覆盖public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) {RectF rect = new RectF(x, top, x + measureText(paint, text, start, end), bottom);mDrawable.setBounds((int) rect.left - mPadding.left, (int) rect.top - mPadding.top, (int) rect.right + mPadding.right, (int) rect.bottom + mPadding.bottom);canvas.drawText(文本,开始,结束,x,y,油漆);mDrawable.draw(画布);}@覆盖public int getSize(@NonNull Paintpaint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {返回 Math.round(paint.measureText(text, start, end));}私人浮点测量文本(油漆,CharSequence 文本,int 开始,int 结束){返回paint.measureText(文本,开始,结束);}}
res/drawable/bit_below_underline.xml
<padding android:bottom="30dp"/><中风机器人:宽度=1dp"android:color="#3792e5"/></形状>
我尝试将 2 个setSpan"一起调用(首先在上面的代码中进行了注释),但没有帮助.
问题
如何在文本部分设置 2 个跨度,正如我上面指定的(带有占位符的部分文本),以便一个是文本颜色,另一个是自定义下划线?
解决方案在测试您的代码时,似乎 ReplacementSpans
在任何 CharacterStyleSpan
之前绘制.所以尽量使用 MetricAffectingSpan
.它会在 ReplacementSpans
之前绘制.
所以使用自定义 MetricAffectingSpan
而不是 ForegroundColorSpan
import android.text.TextPaint;导入 android.text.style.MetricAffectingSpan;公共类 BGColorSpan 扩展 MetricAffectingSpan {私有整数颜色;公共BGColorSpan(整数颜色){this.color = 颜色;}@覆盖public void updateMeasureState(TextPaint textPaint) {textPaint.setColor(颜色);}@覆盖public void updateDrawState(TextPaint textPaint) {textPaint.setColor(颜色);}}
好的.我试图从 android SDK 源代码解释这个问题,在这个 for 循环中有 continue
关键字,它将跳过 CharacterStyleSpan
final float originalX = x;for (int i = start, inext; i
Background
I know how to set multiple spans on a partial text within a static text, as I've asked here :
final SpannableString text = new SpannableString("Hello stackOverflow");
text.setSpan(new RelativeSizeSpan(1.5f), text.length() - "stackOverflow".length(), text.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
text.setSpan(new ForegroundColorSpan(Color.RED), 3, text.length() - 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
tv.setText(text);
This will set the "stackOverflow" to have 2 spans on itself.
I also know how to set a drawable span on a part of the text, as I've asked here.
The problem
Now I need to set 2 spans on a text that's generated from formatting a text with a placeholder, while still having other styles being set as usual.
For example, suppose I have the next text in strings.xml:
<string name="potential_free_upgrade_1_d_months">
<![CDATA[
Potential free upgrade: <uu><b><font color=\'#3792e5\'>%1$d months</font></b></uu>]]>
</string>
The plan is that "%1$d months" will have a text color of "#3792e5" and will have a special underline that is a bit more below than the default one. I used a special customized tag "uu" for the special underline, to be handled in code.
Thing is, no matter what I do, I can't find how to have both the text color AND the underline being shown together.
What I've tried
Since this problem has a placeholder (and the text can be different around the text to be formatted), I had to use "Html.FromHtml" :
String formattedStr = getString(R.string.potential_free_upgrade_1_d_months, 9);
Spanned textToShow = Html.fromHtml(formattedStr, null, new TagHandler() {
int start;
@Override
public void handleTag(final boolean opening, final String tag, Editable output, final XMLReader xmlReader) {
switch (tag) {
case "uu":
if (opening)
start = output.length();
else {
int end = output.length();
//output.setSpan(new ForegroundColorSpan(0xff3792e5), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
output.setSpan(
new DrawableSpan(ResourcesCompat.getDrawable(getResources(), R.drawable.bit_below_underline, null)),
start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
});
titleTextView.setText(textToShow);
DrawableSpan.java
public class DrawableSpan extends ReplacementSpan {
private Drawable mDrawable;
private final Rect mPadding;
public DrawableSpan(Drawable drawable) {
super();
mDrawable = drawable;
mPadding = new Rect();
mDrawable.getPadding(mPadding);
}
@Override
public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) {
RectF rect = new RectF(x, top, x + measureText(paint, text, start, end), bottom);
mDrawable.setBounds((int) rect.left - mPadding.left, (int) rect.top - mPadding.top, (int) rect.right + mPadding.right, (int) rect.bottom + mPadding.bottom);
canvas.drawText(text, start, end, x, y, paint);
mDrawable.draw(canvas);
}
@Override
public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
return Math.round(paint.measureText(text, start, end));
}
private float measureText(Paint paint, CharSequence text, int start, int end) {
return paint.measureText(text, start, end);
}
}
res/drawable/bit_below_underline.xml
<shape
xmlns:android="http://schemas.android/apk/res/android"
android:shape="line">
<padding android:bottom="30dp"/>
<stroke
android:width="1dp"
android:color="#3792e5"/>
</shape>
I tried to call the 2 "setSpan" together (first is commented in the code above), but it didn't help.
The question
How can I set 2 spans on the part of the text, as I've specified above (partial text with placeholder) , so that one will be of text-color, and another of an customized-underline ?
解决方案When tested your code, it seems like ReplacementSpans
are drawn before any CharacterStyleSpan
. So try to use MetricAffectingSpan
.It will draw before ReplacementSpans
.
So Use custom MetricAffectingSpan
instead of ForegroundColorSpan
import android.text.TextPaint;
import android.text.style.MetricAffectingSpan;
public class BGColorSpan extends MetricAffectingSpan {
private int color;
public BGColorSpan(int color) {
this.color = color;
}
@Override
public void updateMeasureState(TextPaint textPaint) {
textPaint.setColor(color);
}
@Override
public void updateDrawState(TextPaint textPaint) {
textPaint.setColor(color);
}
}
OK . I am trying to explain the issue from the android SDK Source code, In this for loop there is continue
keyword it will skip the rendering of CharacterStyleSpan
final float originalX = x;
for (int i = start, inext; i < measureLimit; i = inext) {
TextPaint wp = mWorkPaint;
wp.set(mPaint);
inext = mMetricAffectingSpanSpanSet.getNextTransition(mStart + i, mStart + limit) -
mStart;
int mlimit = Math.min(inext, measureLimit);
ReplacementSpan replacement = null;
for (int j = 0; j < mMetricAffectingSpanSpanSet.numberOfSpans; j++) {
// Both intervals [spanStarts..spanEnds] and [mStart + i..mStart + mlimit] are NOT
// empty by construction. This special case in getSpans() explains the >= & <= tests
if ((mMetricAffectingSpanSpanSet.spanStarts[j] >= mStart + mlimit) ||
(mMetricAffectingSpanSpanSet.spanEnds[j] <= mStart + i)) continue;
MetricAffectingSpan span = mMetricAffectingSpanSpanSet.spans[j];
if (span instanceof ReplacementSpan) {
replacement = (ReplacementSpan)span;
} else {
// We might have a replacement that uses the draw
// state, otherwise measure state would suffice.
span.updateDrawState(wp);
}
}
if (replacement != null) {
x += handleReplacement(replacement, wp, i, mlimit, runIsRtl, c, x, top, y,
bottom, fmi, needWidth || mlimit < measureLimit);
// I think this line making your issue
continue;
}
for (int j = i, jnext; j < mlimit; j = jnext) {
jnext = mCharacterStyleSpanSet.getNextTransition(mStart + j, mStart + inext) -
mStart;
int offset = Math.min(jnext, mlimit);
wp.set(mPaint);
for (int k = 0; k < mCharacterStyleSpanSet.numberOfSpans; k++) {
// Intentionally using >= and <= as explained above
if ((mCharacterStyleSpanSet.spanStarts[k] >= mStart + offset) ||
(mCharacterStyleSpanSet.spanEnds[k] <= mStart + j)) continue;
CharacterStyle span = mCharacterStyleSpanSet.spans[k];
span.updateDrawState(wp);
}
// Only draw hyphen on last run in line
if (jnext < mLen) {
wp.setHyphenEdit(0);
}
x += handleText(wp, j, jnext, i, inext, runIsRtl, c, x,
top, y, bottom, fmi, needWidth || jnext < measureLimit, offset);
}
}
这篇关于如何使用自定义跨度在带有占位符的 TextView 格式化文本上设置多个跨度?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
更多推荐
[db:关键词]
发布评论