[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的朋友请点此下载

时间: 2024-10-28 05:44:51

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

Android点滴---TextView,RadioButton 设置 HTML文本,加载网络图片

现在在做一个题库类的项目,由于有些数学符号或者化学符号之类的没办法直接在前端显示,所以就使用了图文混排: 后台返回的数据直接是HTML格式的数据. 所以就开始去研究控件如何去显示HTML 先贴上参考的文章,感谢分享! 1.这种只适合加载本地图片,或者兼容版本在4.0以下 Android中Textview显示带html文本二-------[Textview显示本地图片] 上面这种方式,只要在百度上搜一下 Android TextView 设置 HTML 数据,就会找出来很多类似的,但是感觉这为大神

Android中Textview显示带html文本【Textview显示本地图片】

Textview可以显示基本的HTML标签 <a href="..."> <b> <big> <blockquote> <br> <cite> <dfn> <div align="..."> <em> <font size="..." color="..." face="..."> <

几个常用文本处理小工具tr ,wc,cut,sort,uniq用法详解

几个文本处理的小工具:tr ,wc,cut,sort,uniq 1. tr命令可以对来自标准输入的字符进行替换.压缩和删除.它可以将一组字符变成另一组字符,经常用来编写优美的单行命令,作用很强大. 语法:tr 选项  SET1 SET2 -c或--complerment:取代所有不属于第一字符集的字符(就是补集): -d或--delete:删除所有属于第一字符集的字符: -s或--squeeze-repeats:把连续重复的字符以单独一个字符表示: -t或--truncate-set1:先删除第

Android:TextView显示富文本信息

最近需要在TextView中显示一些超链接等信息,如URL(点击后跳转),显示网络图片等. 整理如下: 1.显示URl,在需要显示URL超链接的TextView中设置 textview.setText(Html.fromHtml(urlString)) (urlString 用html语法来标识) versionText.setMovementMethod(LinkMovementMethod.getInstance()); 上面一句必须设置,否则将无法跳转. 2.显示网络图片 textview

我的Android进阶之旅------&gt; Android为TextView组件中显示的文本添加背景色

通过上一篇文章 我的Android进阶之旅------> Android在TextView中显示图片方法 (地址:http://blog.csdn.net/ouyang_peng/article/details/46916963) 我们学会了在TextView中显示图片的方法,现在我们来学习如何为TextView组件中显示的文本添加背景色.要求完成的样子如图所示: 首先来学习使用BackgroundColorSpan对象设置文字背景色,代码如下: TextView textView=(TextV

Android 自定义TextView 实现文本间距

Android系统中TextView默认显示中文时会比较紧凑,不是很美观.为了让每行保持一定的行间距,可以设置属性android:lineSpacingExtra或android:lineSpacingMultiplier. 但是有时候我们需要在TextView的文本之间有间距,两个字的话,我们可以在xml文件中,用敲空格的方式来实现,如果有很多文本或者是一个变量的文本呢.我们还这样用敲空格的方式来实现吗?oh no~! 如何实现行间距和文本间距呢?(请往下看 ↓). 1.设置TextView的

Android 自定义控件-TextView

很多时候系统自带的View满足不了设计的要求,就需要自定义View控件.自定义View首先要实现一个继承自View的类.添加类的构造方法,override父类的方法,如onDraw,(onMeasure)等.如果自定义的View有自己的属性,需要在values下建立attrs.xml文件,在其中定义属性,同时代码也要做修改. 一个简单的例子: ·新建一个MyView类,继承自TextView,并添加构造方法:   package com.example.custview; import andr

[Android]Samba服务器在Ubuntu下的配置与Windows系统间的数据共享

安装Samba 安装sambasudo apt-get install samba[编辑]Kubuntu 安装系统设置的共享模块sudo apt-get install kdenetwork-filesharing[编辑]配置 1.windows 访问 ubuntu 第一步创建共享目录: 比如要创建/home/用户名/share首先创建这个文件夹 (这个用户名就是你的用户名,为了方便易懂我才这样写的,到时记得自己改啊) 代码:mkdir /home/用户名/share (新建share文件夹)c

Android Studio下项目构建的Gradle配置及打包应用变体

Gradle简介 ??Gradle是一个自动化构建工具,采用Groovy的Domain Specific Language(领域特定语言)来描述和控制构建逻辑.具有语法简洁.可读性强.配置灵活等特点.基于Intellij IDEA社区版本开发的Android Studio天生支持Gradle构建程序.Groovy是一种基于JVM的敏捷开发语言,结合了Phthon.Ruby和Smalltalk的许多强大特性.同时,Groovy代码既能够与java代码很好地结合,也能够用于扩展现有的代码. Grad