Android Advanced Advanced - Introduction to Paint Commonly Used Functions

Opening:

After the first few articles, we have basically finished reading the basic usage of paint and canvas. Today we will make a big summary, list all the functions of a paint, and then go through them one by one. After these few articles, you will be able to learn the usage of all processing functions in paint.

1. Basic usage

  • 1. Overview
    Let 's first take a look at the functions of the basic settings in paint:
    • reset() : reset the brush
    • setColor(int color) : Set the color value for the brush
    • setARGB(int a, int r, int g, int b) : also set the color, but use ARGB to set it separately
    • setAlpha(int a) : Set the brush transparency
    • setStyle(Paint.Style style): Set the brush style, the values ​​are Paint.Style.FILL: fill inside, Paint.Style.FILL_AND_STROKE: fill inside and stroke, Paint.Style.STROKE: stroke only
    • setStrokeWidth(float width) : Set the brush width
    • setAntiAlias(boolean aa) : Set whether the brush is antialiasing


    The above functions have been described in detail in the previous articles, and the difficulty is not too big, so I won't go into details. We have not mentioned the following functions, and we will add them below.
  • setStrokeCap(Paint.Cap cap): Set the cap style, the values ​​are Cap.ROUND (round cap), Cap.SQUARE (square cap), Paint.Cap.BUTT (wireless cap)

  • setStrokeJoin(Paint.Join join) : Set the style of the line segment connection, the values ​​are: Join.MITER (the joint is an acute angle), Join.Round (the joint is an arc), Join.BEVEL (the joint is a straight line)
  • setStrokeMiter(float miter) : Set the inclination of the stroke, the style of the drawn lines must be different when the brush is held at 90 degrees and the brush is held at 30 degrees. (It turns out that there is no difference at all... 囧...)
  • setPathEffect(PathEffect effect) : Set the path style; the value type is all subclasses derived from PathEffect: ComposePathEffect, CornerPathEffect, DashPathEffect, DiscretePathEffect, PathDashPathEffect, SumPathEffect
Among these four functions, setStrokeMiter(float miter) will not be discussed anymore. I have done experiments and there is no change, that is, it is not useful... Let's take a look at the specific usage of the other three functions.
  • 2. setStrokeCap(Paint.Cap cap)
    sets the cap style, the values ​​are Cap.ROUND (round cap), Cap.SQUARE (square cap), Paint.Cap.BUTT (wireless cap)
    I won't talk about it first It's called line risk. Let's take a look at the following code and its effect:
    private void init() {
        //初始化画笔
        paint = new Paint();
        paint.setColor(Color.GREEN);
        paint.setStyle(Paint.Style.FILL_AND_STROKE);
        paint.setStrokeWidth(50);
        path = new Path();
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        paint.setStrokeCap(Paint.Cap.BUTT);
        canvas.drawLine(100, 100, 500, 100, paint);
        paint.setStrokeCap(Paint.Cap.SQUARE);
        canvas.drawLine(100, 300, 500, 300, paint);
        paint.setStrokeCap(Paint.Cap.ROUND);
        canvas.drawLine(100, 500, 500, 500, paint);

        paint.setStrokeWidth(3);
        paint.setColor(Color.RED);
        canvas.drawLine(100, 0, 100, 1000, paint);

    }

Here, we draw three lines horizontally, and their line cap types are Cap.BUTT (wireless cap), Cap.SQUARE (square cap), Cap.ROUND (round cap).
Finally, draw x= That starting line for 100:

image.png

It can be clearly seen from the renderings that the extra area that emerges from the wireless is the wire cap! It is equivalent to adding a hat to the original straight line, so it is called a line cap

  • 3、setStrokeJoin(Paint.Join join)

The parameter values ​​are:
- Join.MITER (the junction is an acute angle)
- Join.Round (the junction is an arc)
- Join.BEVEL (the junction is a straight line)

According to the Internet, the differences between the three of them are as follows:

image.png

But the effect I run is not the same. There is no obvious difference between Join.Round and Join.BEVEL:
we draw three paths with acute angles, and set different connection methods for these three Paths:

Jietu20180422-170426.jpg

  • 4. setPathEffect (PathEffect effect)
    sets the path style; the value type is all subclasses derived from PathEffect

image.png

Let's look at their effects one by one:

  • (1), CornerPathEffect - round corner effect
    Its function is to turn the original Path's blunt straight line corners into round corners:

image.png

The upper one is the default straight corner of Path, and the lower one is the rounded corner.
Its constructor is:

public CornerPathEffect(float radius)

It has only one parameter radius: the radius of the circle currently used to connect the two lines.

image.png

The above picture clearly shows the use of a circle with a radius of R=50 to replace the angle between the original two straight lines.
Let's use the code to see the specific effect:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    path.moveTo(100, 700);
    path.lineTo(400, 100);
    path.lineTo(700, 900);

    paint.setColor(Color.RED);
    canvas.drawPath(path, paint);

    paint.setColor(Color.BLUE);
    paint.setPathEffect(new CornerPathEffect(100));
    canvas.drawPath(path, paint);

    paint.setColor(Color.YELLOW);
    paint.setPathEffect(new CornerPathEffect(200));
    canvas.drawPath(path, paint);
}

Here, I used Path to construct an included angle and drew it three times at the same position. The first pass did not add any PathEffect; in the second pass, the circle radius of CornerPathEffect was 100; in the third pass, the circle radius of CornerPathEffect was 200;

image.png

It can be clearly seen that the connection position is also different in the case of different radii

  • (2), DashPathEffect - dotted line effect
    This function can realize the effect of dotted line segment:

image.png

Its function declaration is as follows:

public DashPathEffect(float intervals[], float phase)

Among them:
intervals[]: Indicates the length of each line segment that makes up the dotted line; the entire dotted line is composed of these basic line segments in intervals[]. For example, we define new float[] {20,10}; then this dashed line segment is composed of two line segments, the first visible line segment has a length of 20, and every second line segment is invisible and has a length of 10;

image.png

There are two restrictions on the intervals[] array:

  • The length must be greater than or equal to 2; because there must be a solid line segment and an empty line segment to form a dashed line.
  • The number must be an even number. If it is a base number, the last number will be ignored; this is easy to understand, because a set of dotted lines must be composed of a solid line and an empty line in pairs.

Let's make another assumption, if we define intervals[] as new float[] {20, 10, 100, 100}; then this dashed line will be composed of four sub-line segment loops, the first solid line has a length of 20, and the second is empty The length of the line is 10, the length of the third solid line is 100, and the length of the fourth empty line is 100;
phase: the offset value to start drawing
Let 's take a look at the running effect of the code to verify whether what we think is correct:

The effect diagram is as follows:

image.png

Two points can be seen from this rendering:
First: the basic components of the blue line segment, which are composed of solid line segments and empty line segments with lengths of 20, 10, 100, 100 respectively.
Second : the bottom red line segment is displaced by 15, The effect is evident from the start. The original line segment of 20 has only 5 left, so it looks like a point. You can also use your imagination to set the offset using animation to make this dotted line segment move:

The effect diagram is as follows:
Jietu20180422-174024.gif

Code:

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        path.reset();
        paint.reset();
        init();
        path.moveTo(100, 700);
        path.lineTo(400, 100);
        path.lineTo(700, 900);

        paint.setColor(Color.RED);
        canvas.drawPath(path, paint);

        //使用DashPathEffect画线段
        paint.setColor(Color.BLUE);
        paint.setPathEffect(new DashPathEffect(new float[]{20, 10, 100, 100}, 0));
        canvas.translate(0, 100);
        canvas.drawPath(path, paint);

        //画同一条线段,偏移值为15
        paint.setColor(Color.RED);
        paint.setPathEffect(new DashPathEffect(new float[]{20, 10, 100, 100}, dx));
        canvas.translate(0, 100);
        canvas.drawPath(path, paint);
    }

    public void startAnim() {
        ValueAnimator animator = ValueAnimator.ofInt(0, 20 + 10 + 100 + 100);
        animator.setDuration(2000);
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.setInterpolator(new LinearInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                dx = (int) animation.getAnimatedValue();
                invalidate();
            }
        });
        animator.start();
    }
  • (3), DiscretePathEffect - discrete path effect
    image.png

Similarly, the first line in the figure is the original, and the second line is the style after adding the discrete path effect.
DiscretePathEffect divides the original path into fixed-length line segments, and then randomly offsets each line segment by a certain position. We can use it to simulate an effect similar to rusty iron wire;
its constructor is as follows:

public DiscretePathEffect(float segmentLength, float deviation)

The first parameter segmentLength: Indicates how long the original path is cut into segments. If the value is 2, then the path will be cut into small segments of length 2. So the smaller the value is, the more small line segments are cut; the larger the value is, the fewer small line segments are cut.
The second parameter deviation: represents the offset distance of each small line segment that is cut. The larger the value, the larger the offset distance of each line segment, the more messy it appears, and the smaller the value, the smaller the offset distance of each line segment.
Let's look at the code effect

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        path = getPath();
        //第一条原生Path
        canvas.drawPath(path, paint);

        //第二条Path
        canvas.translate(0, 200);
        paint.setPathEffect(new DiscretePathEffect(2, 5));
        canvas.drawPath(path, paint);
        //第三条Path
        canvas.translate(0, 200);
        paint.setPathEffect(new DiscretePathEffect(6, 5));
        canvas.drawPath(path, paint);
        //第四条Path
        canvas.translate(0, 200);
        paint.setPathEffect(new DiscretePathEffect(6, 15));
        canvas.drawPath(path, paint);

    }

    private Path getPath() {
        Path path = new Path();
        // 定义路径的起点
        path.moveTo(0, 0);

        // 定义路径的各个点
        for (int i = 0; i <= 40; i++) {
            path.lineTo(i * 35, (float) (Math.random() * 150));
        }
        return path;
    }

Effect picture:
image.png

Comparing the second and third lines, it can be clearly seen that when only the segmentLength is increased, the sub-line segment cut by the third line segment is obviously larger, so it is not as much as the second curve. The vertices generated by the intersection of the line segments are smoother than the second one. Of course, the rust effect is not as obvious as the second one.
Compared with the third item, when the segmentLength is 6, only the deviation parameter (offset distance) is increased in the fourth item, and it can be clearly seen from the rendering that each sub-line segment is offset outward. The distance moved has also increased.
It is obvious from the effect diagram that the role of each parameter can be seen, so I won't talk about it here.

  • (4), PathDashPathEffect——stamp path effect
    This name is taken by myself..., because this function is equivalent to the stamp function in Photoshop.
    Its function is to use another path pattern as a stamp and cover it one by one along the specified path.
    Let's first look at the declaration of PathDashPathEffect:

public PathDashPathEffect(Path shape, float advance, float phase,Style style)

Among them:
- Path shape: indicates the path of the seal, such as the triangle in our example below plus a point in the upper right corner;
- float advance: indicates the distance between two seal paths, which is easy to understand, the greater the distance between the seals, the greater the distance .
- float phase: Path drawing offset distance, which has the same meaning as the float phase parameter in DashPathEffect above
- Style style: Indicates how to operate the stamp to make the corner transition smoothly when encountering a corner. The values ​​are: Style.ROTATE, Style. MORPH, Style.TRANSLATE; Style.ROTATE means transitioning the corner by rotating the stamp; Style.MORPH means transitioning the corner by deforming the stamp; Style.TRANSLATE means transitioning the corner by displacement. The specific meanings of these three effects will be explained separately through specific examples above.

Then let's see how to achieve the following effect:
image.png

As shown above, the stamp path is a triangle with a point in the upper right corner. Then take this stamp path and stamp it every certain distance on the original path.
The corresponding code for the above figure is:

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //画出原始路径
        Path path = new Path();
        path.moveTo(100, 600);
        path.lineTo(400, 100);
        path.lineTo(700, 900);
        canvas.drawPath(path, paint);

        //构建印章路径
        Path stampPath = new Path();
        stampPath.moveTo(0, 20);
        stampPath.lineTo(10, 0);
        stampPath.lineTo(20, 20);
        stampPath.close();
        stampPath.addCircle(0, 0, 3, Path.Direction.CCW);

        //使用印章路径效果
        canvas.translate(0, 200);
        paint.setPathEffect(new PathDashPathEffect(stampPath, 35, 0, PathDashPathEffect.Style.TRANSLATE));
        canvas.drawPath(path, paint);

    }

In the example, we have two paths. You must distinguish the path from the stamp path. One path is the path, which is the original path; the other path is the stampPath, which is the stamp path. The stamp path is used to construct the paint, and the original path is painted with this paint.
Let's take a look at how to deal with the corners when the Style is different:

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawPathDashPathEffect(canvas);

    }

    private Path getPath() {
        Path path = new Path();
        // 定义路径的起点
        path.moveTo(0, 0);

        // 定义路径的各个点
        for (int i = 0; i <= 40; i++) {
            path.lineTo(i * 35, (float) (Math.random() * 150));
        }
        return path;
    }

    private void drawPathDashPathEffect(Canvas canvas) {
        Path path = getPath();
        canvas.drawPath(path, paint);

        canvas.translate(0, 200);
        paint.setPathEffect(new PathDashPathEffect(getStampPath(), 35, 0, PathDashPathEffect.Style.MORPH));
        canvas.drawPath(path, paint);

        canvas.translate(0, 200);
        paint.setPathEffect(new PathDashPathEffect(getStampPath(), 35, 0, PathDashPathEffect.Style.ROTATE));
        canvas.drawPath(path, paint);

        canvas.translate(0, 200);
        paint.setPathEffect(new PathDashPathEffect(getStampPath(), 35, 0, PathDashPathEffect.Style.TRANSLATE));
        canvas.drawPath(path, paint);
    }

    private Path getStampPath() {
        Path path = new Path();
        path.moveTo(0, 20);
        path.lineTo(10, 0);
        path.lineTo(20, 20);
        path.close();

        path.addCircle(0, 0, 3, Path.Direction.CCW);
        return path;
    }

This code randomly generates a path through the getPath() function, and draws the original path and the path of each Style. The first is the original path, the second is Style.MORPH, the third is Style.ROTATE, and the fourth is Style.TRANSLATE;

image.png

At first glance, everyone is probably dazzled. Take a closer look at the triangle pattern at the blue line.

image.png

The same is the corner, as shown in the figure above, when Style.MORPH, the corner is transitioned by deforming the stamp

image.png

When Style is Style.ROTATE, it depends on rotating the stamp angle to transition the corner.

image.png

When the Style is Style.TRANSLATE, it will neither deform the stamp nor rotate the stamp angle, but just change the position of the stamp to transition
one of the parameters, float phase, which indicates the path drawing offset distance. We haven't talked about it yet. This has the same meaning as the offset distance above, and animation can also be achieved by changing the offset distance.

  • (5), ComposePathEffect and SumPathEffect

Both of these are used to combine two effects. But there are differences between them:

public ComposePathEffect(PathEffect outerpe, PathEffect innerpe)

ComposePathEffect merges two special effects in sequence. It will first apply the special effect of the PathEffect innerpe of the second parameter to the path, and then apply the second special effect on the path where the special effect is added.

public SumPathEffect(PathEffect first, PathEffect second)

The SumPathEffect is to apply the first effect and the second effect to the original path respectively. The two paths are then combined for the final result.
Let's take a look at the effect:

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //画原始路径
        Path path = getPath();
        canvas.drawPath(path, paint);

        //仅应用圆角特效的路径
        canvas.translate(0, 200);
        CornerPathEffect cornerPathEffect = new CornerPathEffect(100);
        paint.setPathEffect(cornerPathEffect);
        canvas.drawPath(path, paint);

        //仅应用虚线特效的路径
        canvas.translate(0, 200);
        DashPathEffect dashPathEffect = new DashPathEffect(new float[]{2, 5, 10, 10}, 0);
        paint.setPathEffect(dashPathEffect);
        canvas.drawPath(path, paint);

        //利用ComposePathEffect先应用圆角特效,再应用虚线特效
        canvas.translate(0, 200);
        ComposePathEffect composePathEffect = new ComposePathEffect(dashPathEffect, cornerPathEffect);
        paint.setPathEffect(composePathEffect);
        canvas.drawPath(path, paint);

        //利用SumPathEffect,分别将圆角特效应用于原始路径,然后将生成的两条特效路径合并
        canvas.translate(0, 200);
        paint.setStyle(Paint.Style.STROKE);
        SumPathEffect sumPathEffect = new SumPathEffect(cornerPathEffect, dashPathEffect);
        paint.setPathEffect(sumPathEffect);
        canvas.drawPath(path, paint);
    }

The code is very simple, just draw a few special effects paths, and the renderings are as follows:

image.png

Pay special attention to the 4th and 5th paths:
the 4th path is generated by:

ComposePathEffect(dashPathEffect,cornerPathEffect);

Indicates that the rounded corner effect is applied to the original path first to obtain the second path, and then the dotted line effect is applied on the basis of the second path to obtain the final effect, the fourth path;

The generation method of the fifth path is relatively mentally retarded:

SumPathEffect(cornerPathEffect,dashPathEffect);

Its principle is to first apply the rounded corner effect to the original path to obtain the second path, and then apply the dotted line effect to the original path to obtain the third path. Then combine the second and third paths ( That is
to

2. Font related

  • setTextSize(float textSize) : Set the text size
  • setFakeBoldText(boolean fakeBoldText) : Set whether to bold text
  • setStrikeThruText(boolean strikeThruText) : set with strikethrough effect
  • setUnderlineText(boolean underlineText) : set the underline
  • setTextAlign(Paint.Align align) : Set the start drawing point position
  • setTextScaleX(float scaleX) : Horizontal stretch setting
  • setTextSkewX(float skewX) : Set the horizontal inclination of the font, the normal italic font is -0.25, and it can be seen that it is skewed to the right
  • setTypeface(Typeface typeface) : font style

Other functions:

  • setLinearText(boolean linearText)

Set whether to turn on the linear text mark; because the text wants to be drawn quickly, it must be cached in the video memory in advance. Generally speaking, each text needs a size of one byte to store it (of course, how many bytes and encoding methods are needed). related), if it is a long article, you can imagine the required size. We can tell Android that we don't need such a text cache with setLinearText(true). But if we don't use the text cache, we can save some memory space, but at the cost of display speed.
Since this is a function of API 1, and the memory size of android phones at that time is still very small, minimizing memory usage is the top priority of every application, and this function is still very useful in the environment at that time.
But today, the memory is always more than 4G, and the memory occupied by the text cache is insignificant. No APP will sacrifice performance to reduce this memory usage, so this function is basically useless.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326556530&siteId=291194637