概述:
如果你想要在一个TextView显示一个被高亮显示的子字符串。例如,我想让"123456789"中的"345"被高亮显示。注意,我这里指的只高亮一部分,而不是全部高亮。你会怎么做?我不知道会不会有一些初学者会想到的是,让这些子字符串分部于不同的TextView,再对每个TextView进行单独处理。当然,如果你已经是一个已经有一些经验的开发者,那我想,你应该就不会再这样去思考了。因为,Android已经给我封装好了——SpannableStringBuilder。下面我就学习Android中对控件的一些封装来封装一个我们自己的TextView(既可在xml中设置也可在Java代码中设置)。
实例效果图:
这里其实有两个LightTextView。第一个是匹配所有的邮箱,第二个是匹配所有的数字。具体细节,大家可以在博客的最后下载源码进行查看。
实例功能介绍:
1.设置文本内容
2.设置需要进行匹配的正则表达式
3.设置匹配出来的子字符串的前景色
4.设置匹配出来的子字符串的背景色
5.设置是否显示前景色
6.设置是否显示背景色
7.设置是否部署以上设置
实例示范讲解:
1.在Java代码中去实现控件属性的设置
其实使用Java代码来设置控件的属性,无疑是简单的。因为它,只是需要对外封装出一些可用的接口即可。例如下面这样:
/**
* 设置背景色
* 2015-3-12
*/
private int mBackColor;
public void setBackColor(int color) {
mBackColor = color;
}
是不是很简单?
当前,Java代码在这方面与attrs相比,的确是要简单一些。不过,程序的关键还是要依靠Java代码来支撑的。例如下面这个代码片段:
private void showForeBackground() {
SpannableStringBuilder styleBuilder = new SpannableStringBuilder(mText); // 包装字体内容
if (mDecbackground) {
for (int i = 0; i < matchedText.size(); i++) {
styleBuilder.setSpan(new BackgroundColorSpan(mBackColor), matchedText.get(i).getStart(), matchedText.get(i).getEnd(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
if (mDecForeground) {
for (int i = 0; i < matchedText.size(); i++) {
styleBuilder.setSpan(new ForegroundColorSpan(mForeColor), matchedText.get(i).getStart(), matchedText.get(i).getEnd(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
setText(styleBuilder);
}
可能大家看到一些变量和函数名的时候已经猜测到了这个代码段的功能了。它就是设置我们需要对子字符串高亮的部分。这里有一个变量需要注意matchedText,它在代码中的定义如下:
private List<SubMatchModel> matchedText = null;
是的,这是一个List,那里面的SubMatchModel是什么呢?这是一个我们自己封装好了的Model。它的功能是记录我们在匹配字符串的过程中,遇到的字符集(当前一个Model也就只对记录一个子字符了,所以这里才会是List)。好了,那现在就让我们看看它里面的内容吧:
public class SubMatchModel {
private String subString;
private int start;
private int end;
public String getSubString() {
return subString;
}
public void setSubString(String subString) {
this.subString = subString;
}
public int getStart() {
return start;
}
public void setStart(int start) {
this.start = start;
}
public int getEnd() {
return end;
}
public void setEnd(int end) {
this.end = end;
}
}
这里只有三个成员变量
subString:记录匹配到的子字符串
start:这个子字符串的开始位置
end:这个子字符串的结束位置
下面就是一个基本匹配的过程,并使用List<SubMatchModel>来记录它:
/**
* 获得一个字符串中匹配正则的子字符集
* @author Q-WHai
* 2015-3-12
*/
public static List<SubMatchModel> getMatchString(String str, String exp) {
Pattern p = Pattern.compile(exp); // 在这里,编译 成一个正则
Matcher m;
m = p.matcher(str); // 获得匹配
List<SubMatchModel> models = new ArrayList<SubMatchModel>();
SubMatchModel model = null;
while(m.find()) {
model = new SubMatchModel();
model.setSubString(m.group());
model.setStart(m.start());
model.setEnd(m.end());
models.add(model);
}
return models;
}
使用示范:
private LightTextView mShowLightTextView = null;
private void resetWidgets() {
mShowLightTextView.setIsMatchOrNot(true);
mShowLightTextView.setText("我的邮箱地址是:abcdef@126.com,你的邮箱地址是:123548@qq.com");
mShowLightTextView.setRegularExp(Utils.getMatchEmailString());
mShowLightTextView.setBackColor(Color.BLUE);
mShowLightTextView.setForeColor(Color.RED);
mShowLightTextView.setDecbackground(true);
mShowLightTextView.setDecForeground(true);
mShowLightTextView.show();
}
大家可以看到上面最后一个方法,show(),是不是显示这个LigthTextView呢?不是的,它只是把之前对LightTextView的一些设置部署到这个LightTextView上来而已。
2.自定义属性attrs的使用
看了上面对LightTextView的一些属性设置和部署,是不是感觉有一点复杂?当然,这里要说它复杂和之前我说Java来写比较简单的说法不矛盾,因为这两者之间没有什么关系。如果非要说上一些关系,我想应该是得失平衡吧。就像我们要把程序写得简单了,那么用户那边可能就会比较复杂,如果想要让用户使用起来简单,那程序中就会使用以一些比较复杂的逻辑。这里也是这样的。如果想要让外部对其调用时比较简单,那么里面的设置肯定是比较繁琐的,如果想要让内部的代码简单一些,那么对外的内容就会比较繁琐了。说了这么多,那么使用attrs究竟是怎么样的呢?
例如我的代码就是这样的:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="LightTextView">
<attr name="text" format="string" />
<attr name="isMatch" format="boolean" />
<attr name="decForeground" format="boolean" />
<attr name="decbackground" format="boolean" />
<attr name="backgroundColor" format="reference|color" />
<attr name="foregroundColor" format="reference|color" />
</declare-styleable>
</resources>
注意事项:
这里有一些地方需要我们注意一下。
1.在Java代码的构造器中去实现以下方法:
private void init(TypedArray array) {
mText = array.getString(R.styleable.LightTextView_text);
mIsMatch = array.getBoolean(R.styleable.LightTextView_isMatch, true);
mDecForeground = array.getBoolean(R.styleable.LightTextView_decForeground, false);
mDecbackground = array.getBoolean(R.styleable.LightTextView_decbackground, false);
mForeColor = array.getColor(R.styleable.LightTextView_foregroundColor, Color.BLACK);
mBackColor = array.getColor(R.styleable.LightTextView_backgroundColor, Color.WHITE);
}
在构造器中调用示范:
public LightTextView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.LightTextView);
init(mTypedArray);
}
2.在xml文件中需要包含程序名:
一定要有上面的被红色框框出来的两个部分。在红色框中又被框出来两个部分也是需要注意的。前者为你后面要使用的自定义属性的名称,后者为你目前的程序包名,而不能使用你自定义控件所在的包。
源码下载:
以上就是全部的讲解,如果大家还有什么不太明白的地方,欢迎移步到我的源码地址进行下载。