剖析项目名称: TextDrawable
剖析原项目地址:https://github.com/amulyakhare/TextDrawable
剖析理由:只知其然而不知其所以然,如此不好。想要快速的进阶,不走寻常路,剖析开源项目,深入理解扩展知识,仅仅这样还不够,还需要如此:左手爱哥的设计模式,右手重构改善既有设计,如此漫长打坐,回过头再看来时的路,书已成山,相信翔哥说的,量变引起质变
先看整体效果图:
这个轻量级的库提供了矩形、圆角、圆形的文本图供ImageView使用。Android Studio项目导入:
repositories{ maven { url 'http://dl.bintray.com/amulyakhare/maven' }
}
dependencies { compile 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1' }
xml引用实例(指定ImageView宽度/高度和可拉的自动扩展以适应大小。):
<ImageView android:layout_width="60dp" android:layout_height="60dp" android:id="@+id/image_view"/>
代码调用实例和效果:
TextDrawable drawable = TextDrawable.builder()
.buildRect("A", Color.RED);
ImageView image = (ImageView) findViewById(R.id.image_view);
image.setImageDrawable(drawable);
以上实例才用的是默认图形,我们还可以选择圆角或圆形,可以设置边框,代码调用实例和效果图如下:
TextDrawable drawable1 = TextDrawable.builder()
.buildRoundRect("A", Color.RED, 10); // radius in px
TextDrawable drawable2 = TextDrawable.builder()
.buildRound("A", Color.RED);
TextDrawable drawable = TextDrawable.builder()
.beginConfig()
.withBorder(4) /* thickness in px */
.endConfig()
.buildRoundRect("A", Color.RED, 10);
修改字体样式代码调用实例:
TextDrawable drawable = TextDrawable.builder()
.beginConfig()
.textColor(Color.BLACK)
.useFont(Typeface.DEFAULT)
.fontSize(30) /* size in px */
.bold()
.toUpperCase()
.endConfig()
.buildRect("a", Color.RED)
颜色辅助类调用实例:
ColorGenerator generator = ColorGenerator.MATERIAL; // or use DEFAULT
// generate random color
int color1 = generator.getRandomColor();
// generate color based on a key (same key returns the same color), useful for list/grid views
int color2 = generator.getColor("[email protected]")
// declare the builder object once.
TextDrawable.IBuilder builder = TextDrawable.builder()
.beginConfig()
.withBorder(4)
.endConfig()
.rect();
// reuse the builder specs to create multiple drawables
TextDrawable ic1 = builder.build("A", color1);
TextDrawable ic2 = builder.build("B", color2);
大概聊了整个项目后,发现需要我们有两个类:ColorGenerator 和 TextDrawable ,ColorGenerator 内部预定义了一些了的色值存放到集合,然后需要用时再从里面取出,这里就不细说了,重点放在TextDrawable
,该类运用builder模式,设置相关属性,构建TextDrawable,先来看下主体结构:
/** * @author amulya * @datetime 14 Oct 2014, 3:53 PM */
public class TextDrawable extends ShapeDrawable {
private TextDrawable(Builder builder) {
//调用父类构造含shape
super(builder.shape);
// 把Builder属性引用到ShapeDrawable
shape = builder.shape;
height = builder.height;
width = builder.width;
radius = builder.radius;
//...............此处略......................
// 初始化画笔
Paint paint = getPaint();
paint.setColor(color);
}
public static interface IBuilder {...}
public static interface IShapeBuilder {...}
public interface IConfigBuilder {...}
public static class Builder implements IConfigBuilder, IShapeBuilder, IBuilder {...}
}
TextDrawable的构造函数调用的父类的含shape构造方法,而这里的传入Builder类在初始化的时候就初始化了shape的子类RectShape:
private Builder() {
//初始化基本属性
text = "";
color = Color.GRAY;
shape = new RectShape();
//..........此处略............
}
改变图形本质通过改变Shape,ondraw绘制达到目的,实例如下:
@Override
public IBuilder roundRect(int radius) {
this.radius = radius;
float[] radii = {radius, radius, radius, radius, radius, radius, radius, radius};
this.shape = new RoundRectShape(radii, null, null);
return this;
}
这里的Builder模式运用让我眼前一亮,原来还可以这样O(∩_∩)O哈哈~,一个类实现三个借口,每个借口的函数返回可以是不同的接口,这样有个好处就是当你.xx()列出来的方法只会列出接口对应的和Builder自定义的方法,而不会完全列出builder含有的方法
public static interface IShapeBuilder {
//...........此处略...........
public IConfigBuilder beginConfig();
}
public interface IConfigBuilder {
//...........此处略...........
public IShapeBuilder endConfig();
}
public static class Builder implements IConfigBuilder, IShapeBuilder, IBuilder {
//...............此处略........................
@Override
public IConfigBuilder beginConfig() {
return this;
}
@Override
public IShapeBuilder endConfig() {
return this;
}
}
接着再来看TextDrawable的onDraw绘制方法:
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
Rect r = getBounds();
// 如果边框大小大于0则绘制边框
if (borderThickness > 0) {
drawBorder(canvas);
}
int count = canvas.save();
canvas.translate(r.left, r.top);
// 绘制文字
int width = this.width < 0 ? r.width() : this.width;
int height = this.height < 0 ? r.height() : this.height;
int fontSize = this.fontSize < 0 ? (Math.min(width, height) / 2) : this.fontSize;
textPaint.setTextSize(fontSize);
canvas.drawText(text, width / 2, height / 2 - ((textPaint.descent() + textPaint.ascent()) / 2), textPaint);
canvas.restoreToCount(count);
}
基本的绘制方法就不提了,这里值得一说也就文字绘制的位置计算,当你了解了Paint的一下几个属性,想必你就能更容易理解了。
1.基准点是baseline(单行)
2.Paint.ascent:是baseline之上至字符最高处的距离
3.Paint.descent:是baseline之下至字符最低处的距离
4.Paint.leading:是上一行字符的descent到下一行的ascent之间的距离,也就是相邻行间的空白距离
5.Paint.top:是指的是最高字符到baseline的值,即ascent的最大值
6.Paint.bottom:是指最低字符到baseline的值,即descent的最大值
小结:看完这个项目,学到了两点知识:builder模式的另一种优雅实现和画笔绘制计算中心点
参考资料:http://www.zybang.com/question/bd3d73c504008384be0ec0d1daa33bec.html