title: How to toggle Spannable foreground color using ClickableSpan
body: <p>I created this class with a method that receives an Spannable object and applies an style to whatever objects it receives. I am using it on a forum message parser which have these Spoiler tags, where the content is only shown when the mouse cursor is over it. For android, I wanted to make it work with clicks on the hidden area, and for that I wrote this:</p>
  
  <pre><code>public static class TextRuleStartSpoiler extends TextRuleStart
  {
      protected TextRuleStartSpoiler()
      {
          super("spoiler");
      }
  
      ArrayList&lt;Spannable&gt; hiddenSpannables = new ArrayList&lt;Spannable&gt;();
      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;
      }
  }
  </code></pre>
  
  <p>However, no matter how much times I click on it, nothing happens. I have also tried, instead of setting and removing ForegroundColorSpans, overriding the method updateDrawState to something like this:</p>
  
  <pre><code>@Override
  public void updateDrawState(TextPaint ds)
  {
      if(hidden)
      {
          ds.linkColor = Color.parseColor("#0A1238");
      }
      else
      {
          ds.linkColor = Color.WHITE;
      }
  
      super.updateDrawState(ds);
  }
  </code></pre>
  
  <p>But it did not change anything too. I added some debug Log prints, and I know for sure that these methods are being called with the correct arguments, but it is not updated the right way, even if I try to invalidate the view right after onClick, using both the View v on onClick and the TextView tv on apply.</p>
  
  <p>Due to the nature of the code I wrote, it is not possible for me to mess too much with the TextView, since it will be full of other Spannable objects with rules completely unrelated with this one in specific.</p>
  
  <h2>Update</h2>
  
  <p>In fact, the problem was not in this part of the code, but actually on how I was inserting the Spannable objects in the TextView. I was iterating over multiple Spannables, which lead me to think I could use the <a href="https://developer.android.com/reference/android/widget/TextView.html#append%28java.lang.CharSequence%29" rel="nofollow">TextView.append</a> method, however, this changes the BufferType to EDITABLE, which disables visual updates in the text, so I changed it to</p>
  
  <pre><code>tv.setText(TextUtils.concat(tv.getText(), s), TextView.BufferType.SPANNABLE);
  </code></pre>