Building a CustomView -TicTacToe

Part 3

krishan sharma
MindOrks
4 min readDec 12, 2017

--

This is the third post of this “Building a CustomView” series, please check out the first one and second one, if you haven’t already.

Draw a line over three same consecutive moves

In the last post, we detected the touch on individual squares and filled them with appropriate values(X or O). The TicTacToe engine takes care of providing the values (X or O) and deciding who wins by determining the winning move. In this post, we will be animating the winning move which is just a line over the three same moves in consecutive squares. Let’s see some code:

We have already drawn a line in previous posts using canvas.drawLine() but in this post, we are drawing it using Path. One good reason is that we can measure path’s length very easily.

The animateWin() method is called by an Activity or fragment with parameters as the coordinates of squares involved in the winning move. In case of the image 1 below, parameters will be 0,0,2,2.

Our path first moves to the centre of first square and from there we tell path to create a line to last square. Note that there is no drawing done, the drawing will happen when we provide this path to our canvas which we are doing it on onDraw(). We are calling invalidate() at the end. This creates a line without any animation.

image 1

Animating the Path

To animate our line we will be using two classes ValueAnimator and DashPathEffect and we will also have to measure our path which in our case is a line.

Measuring the length of the line that we have to draw is easy.

The path used above is the one that is holding our line draw call.

Let’s look at DashPathEffect separately. As the name suggests it is generally used to draw dashed path---- .

In the above code, the array of float numbers must be an even length and > = 2. In this code, it draws a solid line of length 1, then draw a space of length 2, and then draw a solid line of length 4, and then draw the length of 8 blank. We won’t need that as we don’t need a dashed line.

Last parameter (offset value) of our DashPathEffect is the key component in this animation. 1 is the offset of the starting position. That means the line will be drawn after skipping that offset value here 1. Varying the offset we can increase or reduce the line visible length.

But in our case, we don’t need a dashed line so to avoid that we will pass { length, length } as our float array. This way it cannot produce a dashed line as the second array element will not get the chance to draw as the first element is of complete line length.

What about the offset?This we will have to calculate in such a way that in the beginning of animation it is of path length. As it is of path length there will not be any line visible. At the end of animation, it should be 0 so that the whole line is visible.

Understanding the logic behind animation using DashPathEffect

The trick is simple. Instead of increasing the length of line we will be reducing the offset length. I will try to explain it with a diagram.

  1. In the figure in left, you can see the line growing from the 2nd coordinate towards 1st. 1st is the start coordinate of our path and 2nd coordinate is the end.
  2. In the beginning, when our offset =length of the path, clearly there will be no drawing for reasons explained above.
  3. At the end when our offset is 0, complete line will be drawn.
  4. Between those extreme ends, we will have to decrease our offset based on some time function and that will make our line look like growing in the reverse direction from 2nd coordinate to 1st coordinate.

What we need is a simple timing function based on that we can change our offset value.

For decreasing the offset value based on the time we will use the valueAnimator that will give reducing values from 1 to 0 based on animation time. This class is similar to ObjectAnimator that we use to animate our views.

In the above code onAnimationUpdate() will be called many times with animatedValue decreasing with time. Using the animatedValue we are reducing the offset with time and creating a new pathEffect with it. Then we set the pathEffect in paint and call invalidate() . That goes to onDraw() where the line of that particular animation frame gets drawn.

Check out the final result:

References:

  1. http://www.curious-creature.com/2013/12/21/android-recipe-4-path-tracing/
  2. http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0907/3429.html

Thank you so much for your time.Your comments and suggestions are welcome.

--

--