您的位置:首页 > 移动开发 > Android开发

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

2016-05-07 17:42 477 查看
本文链接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的朋友请点此下载
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息