我使用一个接收Spannable对象的方法创建了这个类,并将样式应用于它接收的任何对象。我在具有这些Spoiler标签的论坛消息解析器上使用它,其中内容仅在鼠标光标位于其上时显示。对于Android,我想让它与隐藏区域的点击一起工作,为此我写了这个:
public static class TextRuleStartSpoiler extends TextRuleStart
{
protected TextRuleStartSpoiler()
{
super("spoiler");
}
ArrayList<Spannable> hiddenSpannables = new ArrayList<Spannable>();
boolean hidden = false;
@Override
public void apply(Spannable s, TextView tv)
{
hiddenSpannables.add(s);
s.setSpan(new BackgroundColorSpan(Color.parseColor("#0A1238")),0,s.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
s.setSpan(getForegroundColorSpanShown(), 0, s.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
s.setSpan(getClickableSpanLink(),0,s.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
public static TextRuleEnd getRuleEnd()
{
return new TextRuleEnd("spoiler");
}
ForegroundColorSpan foregroundColorSpanHidden = null;
private synchronized ForegroundColorSpan getForegroundColorSpanHidden()
{
if(foregroundColorSpanHidden == null)
{
foregroundColorSpanHidden = new ForegroundColorSpan(Color.parseColor("#0A1238"));
}
return foregroundColorSpanHidden;
}
ForegroundColorSpan foregroundColorSpanShown = null;
private synchronized ForegroundColorSpan getForegroundColorSpanShown()
{
if(foregroundColorSpanShown == null)
{
foregroundColorSpanShown = new ForegroundColorSpan(Color.WHITE);
}
return foregroundColorSpanShown;
}
ClickableSpan clickableSpan = null;
private synchronized ClickableSpan getClickableSpanLink()
{
if(clickableSpan == null)
{
clickableSpan = new ClickableSpan() {
@Override
public void onClick(View widget)
{
hidden = !hidden;
if(hidden)
{
for(Spannable s : hiddenSpannables)
{
s.setSpan(getForegroundColorSpanHidden(), 0, s.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
s.removeSpan(getForegroundColorSpanShown());
}
}
else
{
for(Spannable s : hiddenSpannables)
{
s.setSpan(getForegroundColorSpanShown(), 0, s.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
s.removeSpan(getForegroundColorSpanHidden());
}
}
widget.invalidate();
}
@Override
public void updateDrawState(TextPaint ds)
{
//super.updateDrawState(ds);
}
};
}
return clickableSpan;
}
}
然而,无论我点击它多少次,都没有任何反应。我也试过,而不是设置和删除ForegroundColorSpans,将方法updateDrawState重写为这样的东西:
@Override
public void updateDrawState(TextPaint ds)
{
if(hidden)
{
ds.linkColor = Color.parseColor("#0A1238");
}
else
{
ds.linkColor = Color.WHITE;
}
super.updateDrawState(ds);
}
但它也没有改变任何东西。我添加了一些调试日志打印,我确信这些方法是使用正确的参数调用的,但它没有以正确的方式更新,即使我尝试在onClick之后立即使视图无效,同时使用View v on onClick和TextView tv on apply。
由于我编写的代码的性质,我不可能对TextView造成太多混乱,因为它将充满其他Spannable对象,其规则与特定的规则完全无关。
实际上,问题不在于代码的这一部分,而是实际上我是如何在TextView中插入Spannable对象的。我正在迭代多个Spannables,这让我认为我可以使用TextView.append方法,但是,这会将BufferType更改为EDITABLE,这会禁用文本中的视觉更新,所以我将其更改为
tv.setText(TextUtils.concat(tv.getText(), s), TextView.BufferType.SPANNABLE);
答案 0 :(得分:1)
你应该做的是完全覆盖updateDrawState方法的默认实现,不要调用super方法。
示例代码(SpoilerSpan):
public class SpoilerSpan extends ClickableSpan {
private boolean shown = false;
public void setShown(boolean shown){
this.shown = shown;
}
public boolean getShown(){
return this.shown;
}
@Override
public void onClick(View widget) {
//Toggle the shown state
setShown(!getShown());
//Invalidate the view
widget.invalidate();
}
@Override
public void updateDrawState(TextPaint ds) {
//Don't call the super method otherwise this may override our settings!
//super.updateDrawState(ds);
//No need to disable the default underline style because the super method isn't called.
//ds.setUnderlineText(false);
if(getShown()){
ds.setColor(Color.BLACK);
ds.bgColor = 0xFFE7DAC2;
} else {
//Spoiler is not shown, make the text color the same as the background color
ds.setColor(0xFFE7DAC2);
ds.bgColor = 0xFFE7DAC2;
}
}
}
<强>用法:强>
TextView tv = (TextView) findViewById(R.id.test);
tv.setMovementMethod(LinkMovementMethod.getInstance());
SpannableString testText = new SpannableString("This is some text. This is a spoiler and this isn't.");
testText.setSpan(new SpoilerSpan(), 19, 36, Spannable.SPAN_POINT_MARK);
tv.setText(testText, BufferType.SPANNABLE);
<强>结果:强>