序言
看了很多Java设计模式的文章,不过看完了真心记不住,这也印证了一个道理,学习技术仔细看十遍不如自己真正写一遍,然后结合需求来操练一遍。言归正传,任何的设计模式都是针对问题解决问题运用出来的一种通用编码模式。今天我就结合需求来聊聊建造者(Builder)模式。
问题
平常编程的时候大家肯定用过富文本,也就是大家非常熟悉的SpannableString相关的了,对于这样的代码非常熟悉。
SpannableString spannableString = new SpannableString("【设计模式-建造者模式】是时候打造RichText了");
Drawable drawable = getResources().getDrawable(R.mipmap.a9c);
drawable.setBounds(0, 0, 42, 42);
ImageSpan imageSpan = new ImageSpan(drawable);
spannableString.setSpan(imageSpan, 0, 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
URLSpan urlSpan = new URLSpan("http://www.jianshu.com/users/");
spannableString.setSpan(urlSpan, 5, spannableString.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
StyleSpan styleSpan_B = new StyleSpan(Typeface.BOLD);
StyleSpan styleSpan_I = new StyleSpan(Typeface.ITALIC);
spannableString.setSpan(styleSpan_B, 5, 7, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString.setSpan(styleSpan_I, 8, 10, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.setHighlightColor(Color.parseColor("#36969696"));
textView.setText(spannableString);
像这样的代码,非常臃肿,看起来很难看,所以我们需要改造一下。对于这种参数需要自己配置的代码非常适合用建造者模式来写。
解决问题
看下面一段代码这样写是不是好很多,清晰,不容易出错。
String text = "【设计模式-建造者模式】是时候打造RichText了";
RichText.from(text)
.image(0,1, getResource().getDrawable(R.mipmap.a9c))
.URL(1, 3, "http://www.jianshu.com/users")
.style(3, 4, Typeface.BOLD)
.style(4, 5, Typeface.ITALIC)
.background(5,6, 0xff11eded)
.click(5,6, new View.OnClickListener(){
@Override
void onClick(View v){
}
.into(textView);
})
这就是建造者模式,结构清晰,代码简洁,Span随自己配置。
代码实战
这是是RichText的实现,基本上能用,还不够完美,还可以支持HTML,Image支持网络图片。后面我将会继续探索支持glide加载网络图片。
public class RichText {
public static Builder from(String text) {
return new Builder(text);
}
public static Builder from(Context context) {
return new Builder(context);
}
public static class Builder {
private final List<WeakReference<RichSpan>> textSpanOperations;
private Context context;
private String text;
Builder(String text) {
this.text = text;
textSpanOperations = new ArrayList<>();
}
Builder(Context context) {
this.context = context;
textSpanOperations = new ArrayList<>();
}
/**
* 设置文本
*/
public Builder text(String text) {
this.text = text;
return this;
}
/**
* 设置背景颜色
*/
public Builder backgroundColor(int start, int end, int color, int flag) {
BackgroundColorSpan backgroundColorSpan = new BackgroundColorSpan(color);
RichSpan richSpan = new RichSpan(start, end, flag, backgroundColorSpan);
textSpanOperations.add(new WeakReference<>(richSpan));
return this;
}
/**
* 设置背景颜色
*/
public Builder backgroundColor(int start, int end, int color) {
return backgroundColor(start, end, color, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
/**
* 设置前景颜色
*/
public Builder foregroundColor(int start, int end, int color, int flag) {
ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(color);
RichSpan richSpan = new RichSpan(start, end, flag, foregroundColorSpan);
textSpanOperations.add(new WeakReference<>(richSpan));
return this;
}
/**
* 设置前景颜色
*/
public Builder foregroundColor(int start, int end, int color) {
return foregroundColor(start, end, color, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
/**
* 设置图片
*/
public Builder image(int start, int end, Drawable drawable, Rect bounds, int flag) {
drawable.setBounds(bounds);
ImageSpan imageSpan = new ImageSpan(drawable);
textSpanOperations.add(new WeakReference<>(new RichSpan(start, end, flag, imageSpan)));
return this;
}
/**
* 设置图片
*/
public Builder image(int start, int end, Drawable drawable, Rect bounds) {
drawable.setBounds(bounds);
ImageSpan imageSpan = new ImageSpan(drawable);
textSpanOperations.add(new WeakReference<>(new RichSpan(start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE, imageSpan)));
return this;
}
/**
* 设置字体大小
*/
public Builder fontSize(int start, int end, int textSize, int flag) {
AbsoluteSizeSpan absoluteSizeSpan = new AbsoluteSizeSpan(textSize, true);
textSpanOperations.add(new WeakReference<>(new RichSpan(start, end, flag, absoluteSizeSpan)));
return this;
}
/**
* 设置字体大小
*/
public Builder fontSize(int start, int end, int textSize) {
AbsoluteSizeSpan absoluteSizeSpan = new AbsoluteSizeSpan(textSize, true);
textSpanOperations.add(new WeakReference<>(new RichSpan(start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE, absoluteSizeSpan)));
return this;
}
/**
* 添加下划线
*/
public Builder underline(int start, int end, int flag) {
UnderlineSpan underlineSpan = new UnderlineSpan();
textSpanOperations.add(new WeakReference<>(new RichSpan(start, end, flag, underlineSpan)));
return this;
}
/**
* 添加下划线
*/
public Builder underline(int start, int end) {
UnderlineSpan underlineSpan = new UnderlineSpan();
textSpanOperations.add(new WeakReference<>(new RichSpan(start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE, underlineSpan)));
return this;
}
/**
* 添加中划线
*/
public Builder strikethrough(int start, int end, int flag) {
StrikethroughSpan strikethroughSpan = new StrikethroughSpan();
textSpanOperations.add(new WeakReference<>(new RichSpan(start, end, flag, strikethroughSpan)));
return this;
}
/**
* 添加中划线
*/
public Builder strikethrough(int start, int end) {
StrikethroughSpan strikethroughSpan = new StrikethroughSpan();
textSpanOperations.add(new WeakReference<>(new RichSpan(start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE, strikethroughSpan)));
return this;
}
/**
* 添加样式
*/
public Builder textAppearance(int start, int end, @StyleRes int style) {
return textAppearance(start, end, style, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
/**
* 添加样式
*/
public Builder textAppearance(int start, int end, @StyleRes int style, int flag) {
Context context = this.context != null ? this.context : BaseApplication.getContext();
TextAppearanceSpan textAppearanceSpan = new TextAppearanceSpan(context, style);
textSpanOperations.add(new WeakReference<>(new RichSpan(start, end, flag, textAppearanceSpan)));
return this;
}
/**
* 增加其他不知道的span
*/
public Builder addRichSpan(int start, int end, int flag, Object span) {
textSpanOperations.add(new WeakReference<>(new RichSpan(start, end, flag, span)));
return this;
}
/**
* 增加其他不知道的span
*/
public Builder addRichSpan(int start, int end, Object span) {
return addRichSpan(start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE, span);
}
/**
* 添加点击事件
*/
public Builder onClick(int start, int end, final View.OnClickListener listener) {
return onClick(start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE, true, listener);
}
/**
* 添加点击事件
*/
public Builder onClick(int start, int end, int flag, final View.OnClickListener listener) {
return onClick(start, end, flag, true, listener);
}
/**
* 添加点击事件
*/
public Builder onClick(int start, int end, boolean underline, final View.OnClickListener listener) {
return onClick(start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE, underline, listener);
}
/**
* 添加点击事件
*/
public Builder onClick(int start, int end, int flag, final boolean underline, final View.OnClickListener listener) {
ClickableSpan clickableSpan = new ClickableSpan() {
@Override
public void onClick(View widget) {
if (listener != null) {
listener.onClick(widget);
}
}
@Override
public void updateDrawState(TextPaint ds) {
ds.setUnderlineText(underline);
}
};
textSpanOperations.add(new WeakReference<>(new RichSpan(start, end, flag, clickableSpan)));
return this;
}
public void into(TextView widget) {
SpannableString spannableString = new SpannableString(text);
for (WeakReference<RichSpan> richSpanWeakRef : textSpanOperations) {
if (richSpanWeakRef != null && richSpanWeakRef.get() != null) {
RichSpan richSpan = richSpanWeakRef.get();
if (richSpan.getSpan() instanceof ClickableSpan) {
widget.setMovementMethod(LinkMovementMethod.getInstance());
}
spannableString.setSpan(richSpan.getSpan(), richSpan.getStart(), richSpan.getEnd(), richSpan.getFlag());
}
}
widget.setText(spannableString);
}
}
}
总结
建造者模式能够让代码结构简洁,参数随意配置,可以不用考虑内部的实现,这是建造者模式带给我们的优势,也正是这样的优点也会有缺点,参数易丢。也可能会new 出不必要的对象,所以要对自己的需求要有比较好的理解。才能选择正确的设计模式。运用好了能事半功倍。
网友评论