ColorPhrase實現處理帶顏色的字符串

ColorPhrase使用方法:

mTextSearch = (TextView)  findViewById(R.id.txt_search);

text = ColorPhrase.from("查看{已使用/過期優惠券}或者{優惠券}")
				.withSeparator("{}")
				.innerColor(0xFFE6454A)
				.outerColor(0xFF666666)
				.format();
mTextSearch.setText(text);

來看看效果圖:


本來以爲這個是已經寫好的庫,只需要在build中compile一下即可,然而我發現我師父並沒有將項目發佈到Jcenter上面去,so,還是來一段源碼,要用的時候,還是需要將這個源碼放進去的:

/**
 * 參考Phrase類的使用,改變字符串個別字符的顏色
 * @author xiayong
 *
 */
public class ColorPhrase {
	  /** The unmodified original pattern. */
	  private final CharSequence pattern;
	  /** Cached result after replacing all keys with corresponding values. */
	  private CharSequence formatted;
	  /** The constructor parses the original pattern into this doubly-linked list of tokens. */
	  private Token head;

	  /** When parsing, this is the current character. */
	  private char curChar;
	  private String separator;//default "{}"
	  private int curCharIndex;
	  private int outerColor;//color that outside the separators
	  private int innerColor;//color that between the separators
	  /** Indicates parsing is complete. */
	  private static final int EOF = 0;

	  /**
	   * Entry point into this API.
	   *
	   * @throws IllegalArgumentException if pattern contains any syntax errors.
	   */
	  public static ColorPhrase from(Fragment f, int patternResourceId) {
		    return from(f.getResources(), patternResourceId);
		  }

		  /**
		   * Entry point into this API.
		   *
		   * @throws IllegalArgumentException if pattern contains any syntax errors.
		   */
		  public static ColorPhrase from(View v, int patternResourceId) {
		    return from(v.getResources(), patternResourceId);
		  }

		  /**
		   * Entry point into this API.
		   *
		   * @throws IllegalArgumentException if pattern contains any syntax errors.
		   */
		  public static ColorPhrase from(Context c, int patternResourceId) {
		    return from(c.getResources(), patternResourceId);
		  }

		  /**
		   * Entry point into this API.
		   *
		   * @throws IllegalArgumentException if pattern contains any syntax errors.
		   */
		  public static ColorPhrase from(Resources r, int patternResourceId) {
		    return from(r.getText(patternResourceId));
		  }

		  /**
		   * Entry point into this API; pattern must be non-null.
		   *
		   * @throws IllegalArgumentException if pattern contains any syntax errors.
		   */
		  public static ColorPhrase from(CharSequence pattern) {
		    return new ColorPhrase(pattern);
		  }
		  private ColorPhrase(CharSequence pattern) {
			    curChar = (pattern.length() > 0) ? pattern.charAt(0) : EOF;

			    this.pattern = pattern;
			 // Invalidate the cached formatted text.
			    formatted = null;
			    separator = "{}";//initialize the default separator 
			  }
		  /**
		   * set the separator of the target,called after from() method.
		   * @param _separator
		   * @return
		   */
		  public ColorPhrase withSeparator(String _separator){
			  if(TextUtils.isEmpty(_separator)){
				  throw new IllegalArgumentException("separator must not be empty!");
			  }
			  if(_separator.length()>2){
				  throw new IllegalArgumentException("separator‘s length must not be more than 3 charactors!");
			  }
			  this.separator = _separator;
			  return this;
		  }
		  /**
		   * init the outerColor
		   * @param _outerColor
		   * @return
		   */
		 public ColorPhrase outerColor(int _outerColor){
			 this.outerColor = _outerColor;
			 return this;
		 }
		 /**
		  * init the innerColor
		  * @param _innerColor
		  * @return
		  */
		 public ColorPhrase innerColor(int _innerColor){
			 this.innerColor = _innerColor;
			 return this;
		 }
		 /**
		  * cut the pattern with the separators and linked them with double link structure;
		  */
		 private void createDoubleLinkWithToken(){
			// A hand-coded lexer based on the idioms in "Building Recognizers By Hand".
			    // http://www.antlr2.org/book/byhand.pdf.
			    Token prev = null;
			    Token next;
			    while ((next = token(prev)) != null) {
			      // Creates a doubly-linked list of tokens starting with head.
			      if (head == null) head = next;
			      prev = next;
			    }
		 }
		  /** Returns the next token from the input pattern, or null when finished parsing. */
		  private Token token(Token prev) {
		    if (curChar == EOF) {
		      return null;
		    }
		    if (curChar == getLeftSeparator()) {
		      char nextChar = lookahead();
		      if (nextChar == getLeftSeparator()) {
		        return leftSeparator(prev);
		      } else /*if (nextChar >= 'a' && nextChar <= 'z')*/ {
		        return inner(prev);
		      } /*else {
		        throw new IllegalArgumentException(
		            "Unexpected character '" + nextChar + "'; expected key.");
		      }*/
		    }
		    return outer(prev);
		  }
		  private char getLeftSeparator(){
			  return separator.charAt(0);
		  }
		  private char getRightSeparator(){
			  if(separator.length() == 2){
				  return separator.charAt(1);
			  }
			  return separator.charAt(0);
		  }
		  /**
		   * Returns the text after replacing all keys with values.
		   *
		   * @throws IllegalArgumentException if any keys are not replaced.
		   */
		  public CharSequence format() {
		    if (formatted == null) {
//		      if (!keysToValues.keySet().containsAll(keys)) {
//		        Set<String> missingKeys = new HashSet<String>(keys);
//		        missingKeys.removeAll(keysToValues.keySet());
//		        throw new IllegalArgumentException("Missing keys: " + missingKeys);
//		      }
		    	if(!checkPattern()){
		    		throw new IllegalStateException("the separators don't match in the pattern!");
		    	}
		    	createDoubleLinkWithToken();
		      // Copy the original pattern to preserve all spans, such as bold, italic, etc.
		      SpannableStringBuilder sb = new SpannableStringBuilder(pattern);
		      for (Token t = head; t != null; t = t.next) {
		        t.expand(sb/*, keysToValues*/);
		      }

		      formatted = sb;
		    }
		    return formatted;
		  }
		  /**
		   * check if the pattern has legal separators
		   * @return
		   */
		  private boolean checkPattern(){
			  if(pattern == null){
				  return false;
			  }
			  char leftSeparator = getLeftSeparator();
			  char rightSeparator = getRightSeparator();
			  Stack<Character> separatorStack = new Stack<Character>();
			  for(int i=0;i<pattern.length();i++){
				  char cur = pattern.charAt(i);
				  if(cur == leftSeparator){
					  separatorStack.push(cur);
				  }else if(cur == rightSeparator){
					 if(!separatorStack.isEmpty() &&(separatorStack.pop() == leftSeparator)){
						 continue;
					 }else{
						 return false;
					 }
				  }
			  }
			  return separatorStack.isEmpty();
		  }
		  private InnerToken inner(Token prev) {

		    // Store keys as normal Strings; we don't want keys to contain spans.
		    StringBuilder sb = new StringBuilder();

		    // Consume the opening '{'.
		    consume();
//		    while ((curChar >= 'a' && curChar <= 'z') || curChar == '_') {
//		      sb.append(curChar);
//		      consume();
//		    }
		    char rightSeparator = getRightSeparator();
		    while(curChar != rightSeparator && curChar !=EOF){
		    	sb.append(curChar);
		    	consume();
		    }
		    
		    // Consume the closing '}'.
		    if (curChar == EOF) {//curChar != ‘}’
		      throw new IllegalArgumentException("Missing closing separator");
		    }
		    consume();

		    // Disallow empty keys: {}.
		    if (sb.length() == 0) {
//		      throw new IllegalArgumentException("Empty key: {}");
		      throw new IllegalStateException("Disallow empty content between separators,for example {}");
		    }

		    String key = sb.toString();
//		    keys.add(key);
		    return new InnerToken(prev, key,innerColor);
		  }

		  /** Consumes and returns a token for a sequence of text. */
		  private OuterToken outer(Token prev) {
		    int startIndex = curCharIndex;

		    while (curChar != getLeftSeparator() && curChar != EOF) {
		      consume();
		    }
		    return new OuterToken(prev, curCharIndex - startIndex,outerColor);
		  }

		  /** Consumes and returns a token representing two consecutive curly brackets. */
		  private LeftSeparatorToken leftSeparator(Token prev) {
		    consume();
		    consume();
		    return new LeftSeparatorToken(prev,getLeftSeparator());
		  }

		  /** Returns the next character in the input pattern without advancing. */
		  private char lookahead() {
		    return curCharIndex < pattern.length() - 1 ? pattern.charAt(curCharIndex + 1) : EOF;
		  }

		  /**
		   * Advances the current character position without any error checking. Consuming beyond the
		   * end of the string can only happen if this parser contains a bug.
		   */
		  private void consume() {
		    curCharIndex++;
		    curChar = (curCharIndex == pattern.length()) ? EOF : pattern.charAt(curCharIndex);
		  }

		  private abstract static class Token {
		    private final Token prev;
		    private Token next;

		    protected Token(Token prev) {
		      this.prev = prev;
		      if (prev != null) prev.next = this;
		    }

		    /** Replace text in {@code target} with this token's associated value. */
		    abstract void expand(SpannableStringBuilder target/*t, Map<String, CharSequence> data*/);

		    /** Returns the number of characters after expansion. */
		    abstract int getFormattedLength();

		    /** Returns the character index after expansion. */
		final int getFormattedStart() {
		      if (prev == null) {
		        // The first token.
		        return 0;
		      } else {
		        // Recursively ask the predecessor node for the starting index.
		        return prev.getFormattedStart() + prev.getFormattedLength();
		      }
		    }
		  }

		  /** Ordinary text between tokens. */
		  private static class OuterToken extends Token {
		    private final int textLength;
		    private int color;
		    OuterToken(Token prev, int textLength,int _color) {
		      super(prev);
		      this.textLength = textLength;
		      this.color = _color;
		    }

		    @Override void expand(SpannableStringBuilder target/*, Map<String, CharSequence> data*/) {
		    	
		    	 int startPoint = getFormattedStart();
		    	 int endPoint = startPoint + textLength;
		    	 target.setSpan(new ForegroundColorSpan(color),startPoint , endPoint, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);//TODO think carefully
//		    	 target.setSpan(new ForegroundColorSpan(0xFF666666),startPoint , endPoint, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);//TODO think carefully
		    }

		    @Override int getFormattedLength() {
		      return textLength;
		    }
		  }

		  /** A sequence of two curly brackets. */
		  private static class LeftSeparatorToken extends Token {
			  private char leftSeparetor;
		    LeftSeparatorToken(Token prev,char _leftSeparator) {
		      super(prev);
		      leftSeparetor = _leftSeparator;
		    }

		    @Override void expand(SpannableStringBuilder target/*, Map<String, CharSequence> data*/) {
		      int start = getFormattedStart();
		      target.replace(start, start + 2,String.valueOf(leftSeparetor));
		    }

		    @Override int getFormattedLength() {
		      // for example,if separator is"{}",Replace "{{" with "{".
		      return 1;
		    }
		  }

		  private static class InnerToken extends Token {
		    /** The InnerText without separators,like '{' and '}'. */
		    private final String innerText;
		    
		    private int color;
//		    private CharSequence value;

		    InnerToken(Token prev, String _inner, int _color) {
		      super(prev);
		      this.innerText = _inner;
		      color = _color;
		    }

		    @Override void expand(SpannableStringBuilder target/*, Map<String, CharSequence> data*/) {
//		      value = data.get(innerText);

		      int replaceFrom = getFormattedStart();
		      // Add 2 to account for the opening and closing brackets. 
		      int replaceTo = replaceFrom + innerText.length() + 2;
		      target.replace(replaceFrom, replaceTo, innerText);
		      target.setSpan(new ForegroundColorSpan(color),replaceFrom , replaceTo - 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);//TODO think carefully
//		      target.setSpan(new ForegroundColorSpan(0xFFE6554A),replaceFrom , replaceTo - 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);//TODO think carefully
		    }

		    @Override int getFormattedLength() {
		      // Note that value is only present after expand. Don't error check because this is all
		      // private code.
		      return innerText.length();
		    }
		  }
}

不得不感慨,薑還是老的辣。。。。。。。強大的師父是我的望塵莫及的,默默的用他開源的代碼即可。。。。。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章