[Android]为TextView提供双色文本配置的小工具ColorPhrase

本文链接http://blog.csdn.net/jan_s/article/details/51338944,转载请留言。


在安卓开发过程中,经常会看到文本中有重点的字段是需要换色的,为了表现其特殊性。这个时候大多数人都会用比较简单的方式就是再New 一个TextView出来,显然很快速,然而这样做无疑是给布局绘制添加麻烦,这里简单提供一个工具ColorPhrase类,帮你解决这一的麻烦。


先看demo效果图


使用方式:

1.MainActivity.java

public class MainActivity extends Activity {
	private EditText editText;
	private TextView textView;
	private Button button;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		editText = (EditText) findViewById(R.id.editText1);
		textView = (TextView) findViewById(R.id.textView1);
		button = (Button) findViewById(R.id.button1);
		button.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				//这里是使用方法:将主要换色的文本用“{}”区分出来,然后对其文本风格颜色做处理,这就是ColorPhrase的工作了。
				String pattern = editText.getText().toString();
				CharSequence chars = ColorPhrase.from(pattern).withSeparator("{}").innerColor(getResources().getColor(R.color.in_color)).outerColor(getResources().getColor(R.color.out_color)).format();
				textView.setText(chars);
			}
		});
	}
}

2.ColorPhrase.java-这是主要的功能实现类,拷贝到项目中即可。

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,default 0xFF666666
	private int innerColor;// color that between the separators,default 0xFFA6454A
	/** Indicates parsing is complete. */
	private static final int EOF = 0;

	/**
	 * Entry point into this API.
	 * 
	 * @throws IllegalArgumentException
	 *             if pattern contains any syntax errors.
	 */
	@TargetApi(Build.VERSION_CODES.HONEYCOMB)
	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
		outerColor = 0xFF666666;//initialize the default value
		innerColor =0xFFE6454A;//initialize the default value
	}

	/**
	 * 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 {
				return inner(prev);
			}
		}
		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 (!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);
			}

			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 left separator.
		consume();
		char rightSeparator = getRightSeparator();
		while (curChar != rightSeparator && curChar != EOF) {
			sb.append(curChar);
			consume();
		}

		if (curChar == EOF) {
			throw new IllegalArgumentException("Missing closing separator");
		}
        //consume the right separator.
		consume();

		if (sb.length() == 0) {
			throw new IllegalStateException("Disallow empty content between separators,for example {}");
		}

		String key = sb.toString();
		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);
	}

	/**
	 * Returns the raw pattern without expanding keys; only useful for
	 * debugging. Does not pass through to {@link #format()} because doing so
	 * would drop all spans.
	 */
	@Override
	public String toString() {
		return pattern.toString();
	}

	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);

		/** 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) {

			int startPoint = getFormattedStart();
			int endPoint = startPoint + textLength;
			target.setSpan(new ForegroundColorSpan(color), startPoint, endPoint, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
		}

		@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) {
			int start = getFormattedStart();
			target.replace(start, start + 2, String.valueOf(leftSeparetor));
		}

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

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

		private int color;

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

		@Override
		void expand(SpannableStringBuilder target) {

			int replaceFrom = getFormattedStart();
			// Add 2 to account for the separators.
			int replaceTo = replaceFrom + innerText.length() + 2;
			target.replace(replaceFrom, replaceTo, innerText);
			target.setSpan(new ForegroundColorSpan(color), replaceFrom, replaceTo - 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
		}

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


好了,需要demo的朋友请点此下载

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值