深度剖析之 TextDrawable

剖析项目名称: TextDrawable
剖析原项目地址:https://github.com/amulyakhare/TextDrawable
剖析理由:只知其然而不知其所以然,如此不好。想要快速的进阶,不走寻常路,剖析开源项目,深入理解扩展知识,仅仅这样还不够,还需要如此:左手爱哥的设计模式,右手重构改善既有设计,如此漫长打坐,回过头再看来时的路,书已成山,相信翔哥说的,量变引起质变


先看整体效果图:
深度剖析之 TextDrawable_第1张图片
这个轻量级的库提供了矩形、圆角、圆形的文本图供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_第2张图片

TextDrawable drawable = TextDrawable.builder()
                .buildRect("A", Color.RED);

ImageView image = (ImageView) findViewById(R.id.image_view);
image.setImageDrawable(drawable);

以上实例才用的是默认图形,我们还可以选择圆角或圆形,可以设置边框,代码调用实例和效果图如下:
深度剖析之 TextDrawable_第3张图片深度剖析之 TextDrawable_第4张图片

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

你可能感兴趣的:(Android)