Copyright notice: This article is the blogger’s original article, shall not be reproduced without the permission of the blogger

Android Development from Scratch series

Source: AnliaLee/BookPage, welcome star

If you see any mistakes or have any good suggestions, please leave a comment

preface: This issue is implemented from scratchPage turning effect of booksFor the last issue of Principles, we’re going to take the rest ofFlip the shadowCompletion (the subsequent optimization will be updated irregularly in the form of the extras). Drawing shadow effect scheme is very much, close to the reality of light and shadow effect we need a little bit slowly debugging. Here I give my personal scheme and principle analysis, for your reference

This article only focuses on the ideas and implementation steps, some knowledge principles used in it will not be very detailed. If there are unclear APIS or methods, you can search the corresponding information on the Internet, there must be a god to explain very clearly, I will not present the ugly. In the spirit of serious and responsible, I will post the relevant knowledge of the blog links (in fact, is lazy do not want to write so much ha ha), you can send their own. For the benefit of those who are reading this series of blogs for the first time, this post contains some content that has been covered in the previous series of blogs

International convention, go up effect drawing first


Dividing the shaded area

Let’s start with a brief review of the location of each marker and the division of the area (see Android Custom View – From Scratch to achieve the page turning effect (a)). The location of each marker is as follows

When we turn the page, the View is divided into three areas, namely area A (current page), area B (next page) and area C (back of current page), as shown in the figure below

Then, based on the division of these three regions, the region where the shadow falls belongs to the shadow of the region, which can be divided into the left shadow region of A, the right shadow region of A, the shadow region of B and the shadow region of C. The specific shadow division is shown in the figure below

We will start with the simplest B region shadow and analyze the principle of drawing shadow effects


Draw a shadow for the next page (region B)

As required, when point F is located at the lower right corner, the shaded area starts from line CJ and draws a distance perpendicular to CJ to the lower right corner. The distance increases with the distance of AF, and the color gradually changes linearly from dark to light. We can use our knowledge of GradientDrawable to draw a linearly gradient rectangle, but the setBounds method for GradientDrawable can only set a rectangle with edges parallel or perpendicular to the XY axis, So we also need to use the canvas. Rotate method to rotate the axis of the canvas so that one side of the shaded rectangle is aligned with cj, as shown in the following figure (┑( ̄  ̄)┍)

The rectangle at the bottom left of the figure is our shaded rectangle, with the short side being a quarter of the length of AF (adjustable as needed) and the long side being the diagonal length of the entire View (since CJ cannot exceed this length). The color starts at CC ₁, with a linear gradient from dark to light in the negative X-axis. Then take point C as the center of the rotation Angle C ₁ Cj, so that CC ₁ and Cj coincide, we can draw the shadow area we want. How do we figure out the Angle at C ₁cj? From the figure, we can know that CC ₁ is rotating counterclockwise, and because Cj is parallel to EH, so Angle JCF is equal to Angle HEf, that is, Angle C ₁ Cj = 90° + Angle hef. We combine math. toDegrees and Math.atan2 to calculate this Angle. Since the rotation is counterclockwise, the rotation Angle is negative

Then the rotation Angle formula is

float rotateDegrees = (float) Math.toDegrees(Math.atan2(e.x- f.x, h.y - f.y));
Copy the code

Similarly, when f is in the upper right corner, the difference is that the left and right of the shaded rectangle will be different because the rotation Angle is 90 degrees less. You can draw your own diagram for the rotation process. Principle analysis finished, began to knock code. Modified BookPageView to add method to draw B area shadow

/** * Draw the content of area B *@param canvas
 * @param pathPaint
 * @param pathA
 */
private void drawPathBContent(Canvas canvas, Path pathA, Paint pathPaint){
	Bitmap contentBitmap = Bitmap.createBitmap(viewWidth, viewHeight, Bitmap.Config.RGB_565);
	Canvas contentCanvas = new Canvas(contentBitmap);

	// Start drawing the contents of the region...
	contentCanvas.drawPath(getPathB(),pathPaint);
	contentCanvas.drawText("This is the content in section B... BBBB", viewWidth-260, viewHeight-100, textPaint);

	// Finish drawing the contents of the area...

	canvas.save();
	canvas.clipPath(pathA);// Crop out the A area
	canvas.clipPath(getPathC(),Region.Op.UNION);// Crop out the complete set of regions A and C
	canvas.clipPath(getPathB(), Region.Op.REVERSE_DIFFERENCE);// Crop out the parts of the B region that are different from the AC region
	canvas.drawBitmap(contentBitmap, 0.0.null);

	// To get a better view of the extent of the shaded area without cropping, reset the canvas
	canvas.restore();
	canvas.save();

	drawPathBShadow(canvas);// Call the shadow drawing method
	canvas.restore();
}

/** * Draw a shadow for region B, the shadow is deep on the left and light on the right@param canvas
 */
private void drawPathBShadow(Canvas canvas){
	int deepColor = 0xff111111;// In order to make the effect more obvious, use this color code, the specific can be adjusted according to the actual situation
// int deepColor = 0x55111111;
	int lightColor = 0x00111111;
	int[] gradientColors = new int[] {deepColor,lightColor};// Gradient color array

	int deepOffset = 0;// The offset of the dark end
	int lightOffset = 0;// The offset of the light end
	float aTof =(float) Math.hypot((a.x - f.x),(a.y - f.y));// Distance from a to f
	float viewDiagonalLength = (float) Math.hypot(viewWidth, viewHeight);// Diagonal length

	int left;
	int right;
	int top = (int) c.y;
	int bottom = (int) (viewDiagonalLength + c.y);
	GradientDrawable gradientDrawable;
	if(style.equals(STYLE_TOP_RIGHT)){// Point f is in the upper right corner
		// Linear gradient from left to right
		gradientDrawable =new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT,gradientColors);
		gradientDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);// Linear gradient

		left = (int) (c.x - deepOffset);// Point C is located in the upper left corner
		right = (int) (c.x + aTof/4 + lightOffset);
	}else {
		// Linear gradient from right to left
		gradientDrawable =new GradientDrawable(GradientDrawable.Orientation.RIGHT_LEFT,gradientColors);
		gradientDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);

		left = (int) (c.x - aTof/4 - lightOffset);// Point C is located in the lower left corner
		right = (int) (c.x + deepOffset);
	}
	gradientDrawable.setBounds(left,top,right,bottom);// Set the shaded rectangle

	float rotateDegrees = (float) Math.toDegrees(Math.atan2(e.x- f.x, h.y - f.y));// Rotate the Angle
	canvas.rotate(rotateDegrees, c.x, c.y);// Rotate around c
	gradientDrawable.draw(canvas);
}
Copy the code

The effect of not clipping the shaded area is shown

Comment out the code we used in the experiment

/** * Draw the content of area B *@param canvas
 * @param pathPaint
 * @param pathA
 */
private void drawPathBContent(Canvas canvas, Path pathA, Paint pathPaint){
	Bitmap contentBitmap = Bitmap.createBitmap(viewWidth, viewHeight, Bitmap.Config.RGB_565);
	Canvas contentCanvas = new Canvas(contentBitmap);

	// Start drawing the contents of the region...
	contentCanvas.drawPath(getPathB(),pathPaint);
	contentCanvas.drawText("This is the content in section B... BBBB", viewWidth-260, viewHeight-100, textPaint);

	// Finish drawing the contents of the area...

	canvas.save();
	canvas.clipPath(pathA);// Crop out the A area
	canvas.clipPath(getPathC(),Region.Op.UNION);// Crop out the complete set of regions A and C
	canvas.clipPath(getPathB(), Region.Op.REVERSE_DIFFERENCE);// Crop out the parts of the B region that are different from the AC region
	canvas.drawBitmap(contentBitmap, 0.0.null);

// canvas.restore();
// canvas.save();

	drawPathBShadow(canvas);
	canvas.restore();
}
Copy the code

The end result is as follows


Draws a shadow on the back of the current page (region C)

By this picture can see C area shadows before drawing process is similar to B area, different places is the color from deep to shallow gradient direction is vertical cj to point a direction, and the shadow length of the short side of the rectangle into with ce or jh (the) take short length, because principle, no longer here, Here is the code to draw the C region shadow (note the contrast analysis differs from the B region shadow method)

/** * Draw the content of area C *@param canvas
 * @param pathA
 * @param pathPaint
 */
private void drawPathCContent(Canvas canvas, Path pathA, Paint pathPaint){
	Bitmap contentBitmap = Bitmap.createBitmap(viewWidth, viewHeight, Bitmap.Config.RGB_565);
	Canvas contentCanvas = new Canvas(contentBitmap);

	// Start drawing the contents of the region...
	contentCanvas.drawPath(getPathB(),pathPaint);// Draw a background with path B
	contentCanvas.drawText("Here's what's in section A... AAAA", viewWidth-260, viewHeight-100, textPaint);

	// Finish drawing the contents of the area...

	canvas.save();
	canvas.clipPath(pathA);
	canvas.clipPath(getPathC(), Region.Op.REVERSE_DIFFERENCE);// Crop out the parts of region C that differ from region A

	float eh = (float) Math.hypot(f.x - e.x,h.y - f.y);
	float sin0 = (f.x - e.x) / eh;
	float cos0 = (h.y - f.y) / eh;
	// Set the rollover and rotation matrices
	float[] mMatrixArray = { 0.0.0.0.0.0.0.0.1.0 f };
	mMatrixArray[0] = - (1-2 * sin0 * sin0);
	mMatrixArray[1] = 2 * sin0 * cos0;
	mMatrixArray[3] = 2 * sin0 * cos0;
	mMatrixArray[4] = 1 - 2 * sin0 * sin0;

	Matrix mMatrix = new Matrix();
	mMatrix.reset();
	mMatrix.setValues(mMatrixArray);// Flip and rotate
	mMatrix.preTranslate(-e.x, -e.y);// A₃B₃C₃D₃ was formed along the negative XY axis
	mMatrix.postTranslate(e.x, e.y);// create a rectangle A4, B4, C4, D4
	canvas.drawBitmap(contentBitmap, mMatrix, null);

	drawPathCShadow(canvas);// Call the draw shadow method
	canvas.restore();
}

/** * Draw a shadow on the C region, the shadow is light on the left and deep on the right *@param canvas
 */
private void drawPathCShadow(Canvas canvas){
	int deepColor = 0xff111111;// In order to make the effect more obvious, use this color code, the specific can be adjusted according to the actual situation
// int deepColor = 0x55333333;
	int lightColor = 0x00333333;
	int[] gradientColors = {lightColor,deepColor};// Gradient color array

	int deepOffset = 1;// The offset of the dark end
	int lightOffset = -30;// The offset of the light end
	float viewDiagonalLength = (float) Math.hypot(viewWidth, viewHeight);//view diagonal length
	int midpoint_ce = (int) (c.x + e.x) / 2;/ / ce midpoint
	int midpoint_jh = (int) (j.y + h.y) / 2;/ / jh midpoint
	float minDisToControlPoint = Math.min(Math.abs(midpoint_ce - e.x), Math.abs(midpoint_jh - h.y));// The minimum value from midpoint to control point

	int left;
	int right;
	int top = (int) c.y;
	int bottom = (int) (viewDiagonalLength + c.y);
	GradientDrawable gradientDrawable;
	if (style.equals(STYLE_TOP_RIGHT)) {
		gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, gradientColors);
		gradientDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);

		left = (int) (c.x - lightOffset);
		right = (int) (c.x + minDisToControlPoint + deepOffset);
	} else {
		gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.RIGHT_LEFT, gradientColors);
		gradientDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);

		left = (int) (c.x - minDisToControlPoint - deepOffset);
		right = (int) (c.x + lightOffset);
	}
	gradientDrawable.setBounds(left,top,right,bottom);

	float mDegrees = (float) Math.toDegrees(Math.atan2(e.x- f.x, h.y - f.y));
	canvas.rotate(mDegrees, c.x, c.y);
	gradientDrawable.draw(canvas);
}
Copy the code

The effect is shown in figure


Draws A shadow for the current page (region A)

When the page is turned from the top right or bottom right, the shadow of region A is made up of two parts, which have been marked in the previous figure. According to the needs of shadow effect, the complexity of drawing varies with different schemes. Here, I will take my own scheme as an example to explain

First look at the shadow area on the left. According to the previous analysis of the shadow drawing process, we can directly substitute in the relevant conditions. The short side of the shaded rectangle is 1/2 the distance from point D to ae, the rotation center is E, and the rotation Angle is math.toDegrees (math.atan2 (e.x-a.x, a.y-e.y))

public class BookPageView extends View {
	// omit some code...
    float lPathAShadowDis = 0;// reference value of short edge length of left shaded rectangle in region A

    /** * Draw the contents of area A *@param canvas
     * @param pathA
     * @param pathPaint
     */
    private void drawPathAContent(Canvas canvas, Path pathA, Paint pathPaint){
        Bitmap contentBitmap = Bitmap.createBitmap(viewWidth, viewHeight, Bitmap.Config.RGB_565);
        Canvas contentCanvas = new Canvas(contentBitmap);

        // Start drawing the contents of the region...
        contentCanvas.drawPath(pathA,pathPaint);
        contentCanvas.drawText("Here's what's in section A... AAAA", viewWidth-260, viewHeight-100, textPaint);

        // Finish drawing the contents of the area...

        canvas.save();
        canvas.clipPath(pathA, Region.Op.INTERSECT);// Clipping the drawing content, take the intersection with A region
        canvas.drawBitmap(contentBitmap, 0.0.null);

        drawPathALeftShadow(canvas,pathA);
        canvas.restore();
    }

    /** * Draw the left shadow of region A *@param canvas
     */
    private void drawPathALeftShadow(Canvas canvas, Path pathA){
        canvas.restore();
        canvas.save();

        int deepColor = 0x33333333;
        int lightColor = 0x01333333;
        int[] gradientColors = {lightColor,deepColor};// Gradient color array

        int left;
        int right;
        int top = (int) e.y;
        int bottom = (int) (e.y+viewHeight);

        GradientDrawable gradientDrawable;
        if (style.equals(STYLE_TOP_RIGHT)) {
            gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, gradientColors);
            gradientDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);

            left = (int) (e.x - lPathAShadowDis /2);
            right = (int) (e.x);
        } else {
            gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.RIGHT_LEFT, gradientColors);
            gradientDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);

            left = (int) (e.x);
            right = (int) (e.x + lPathAShadowDis /2);
        }
		gradientDrawable.setBounds(left,top,right,bottom);

        float mDegrees = (float) Math.toDegrees(Math.atan2(e.x-a.x, a.y-e.y));
        canvas.rotate(mDegrees, e.x, e.y);
        gradientDrawable.draw(canvas);
    }

    /** * calculate the coordinates of each point *@param a
     * @param f
     */
    private void calcPointsXY(MyPoint a, MyPoint f){
		// omit some code...
        // Calculate the distance between d and ae
        float lA = a.y-e.y;
        float lB = e.x-a.x;
        float lC = a.x*e.y-e.x*a.y;
        lPathAShadowDis = Math.abs((lA*d.x+lB*d.y+lC)/(float) Math.hypot(lA,lB)); }}Copy the code

The effect is shown in figure

Similarly, the shadow on the right is drawn in the same way: The short side of the shaded rectangle is 1/2 the distance from point I to line ah, the rotation center is h, and the rotation Angle is math.toDegrees (math.atan2 (a.y-h.y, a.x-h.x)). Modify BookPageView according to the conditions

public class BookPageView extends View {
	// omit some code...
    float rPathAShadowDis = 0;// the reference value of the length of the short side of the right-shaded rectangle in area A
    /** * Draw the contents of area A *@param canvas
     * @param pathA
     * @param pathPaint
     */
    private void drawPathAContent(Canvas canvas, Path pathA, Paint pathPaint){
        Bitmap contentBitmap = Bitmap.createBitmap(viewWidth, viewHeight, Bitmap.Config.RGB_565);
        Canvas contentCanvas = new Canvas(contentBitmap);

        // Start drawing the contents of the region...
        contentCanvas.drawPath(pathA,pathPaint);
        contentCanvas.drawText("Here's what's in section A... AAAA", viewWidth-260, viewHeight-100, textPaint);

        // Finish drawing the contents of the area...

        canvas.save();
        canvas.clipPath(pathA, Region.Op.INTERSECT);// Clipping the drawing content, take the intersection with A region
        canvas.drawBitmap(contentBitmap, 0.0.null);

        drawPathALeftShadow(canvas,pathA);
        drawPathARightShadow(canvas,pathA);
        canvas.restore();
    }

    /** * Draw A region right shadow *@param canvas
     */
    private void drawPathARightShadow(Canvas canvas, Path pathA){
        canvas.restore();
        canvas.save();

        int deepColor = 0x33333333;
        int lightColor = 0x01333333;
        int[] gradientColors = {deepColor,lightColor,lightColor};// Gradient color array

        float viewDiagonalLength = (float) Math.hypot(viewWidth, viewHeight);//view diagonal length
        int left = (int) h.x;
        int right = (int) (h.x + viewDiagonalLength*10);// The length should be long enough
        int top;
        int bottom;

        GradientDrawable gradientDrawable;
        if (style.equals(STYLE_TOP_RIGHT)) {
            gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.BOTTOM_TOP, gradientColors);
            gradientDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);

            top = (int) (h.y- rPathAShadowDis /2);
            bottom = (int) h.y;
        } else {
            gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, gradientColors);
            gradientDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);

            top = (int) h.y;
            bottom = (int) (h.y+ rPathAShadowDis /2);
        }
        gradientDrawable.setBounds(left,top,right,bottom);

        float mDegrees = (float) Math.toDegrees(Math.atan2(a.y-h.y, a.x-h.x));
        canvas.rotate(mDegrees, h.x, h.y);
        gradientDrawable.draw(canvas);
    }

    /** * calculate the coordinates of each point *@param a
     * @param f
     */
    private void calcPointsXY(MyPoint a, MyPoint f){
		// omit some code...
        // Calculate the distance from I to ah
        float rA = a.y-h.y;
        float rB = h.x-a.x;
        float rC = a.x*h.y-h.x*a.y;
        rPathAShadowDis = Math.abs((rA*i.x+rB*i.y+rC)/(float) Math.hypot(rA,rB)); }}Copy the code

The effect is shown in figure

Finally, crop out the area we want (see code for the clipping area) and modify BookPageView

/** * Draw the left shadow of region A *@param canvas
 */
private void drawPathALeftShadow(Canvas canvas, Path pathA){
	canvas.restore();
	canvas.save();

	int deepColor = 0x33333333;
	int lightColor = 0x01333333;
	int[] gradientColors = {lightColor,deepColor};// Gradient color array

	int left;
	int right;
	int top = (int) e.y;
	int bottom = (int) (e.y+viewHeight);

	GradientDrawable gradientDrawable;
	if (style.equals(STYLE_TOP_RIGHT)) {
		gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, gradientColors);
		gradientDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);

		left = (int) (e.x - lPathAShadowDis /2);
		right = (int) (e.x);
	} else {
		gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.RIGHT_LEFT, gradientColors);
		gradientDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);

		left = (int) (e.x);
		right = (int) (e.x + lPathAShadowDis /2);
	}

	// Crop out the area we need
	Path mPath = new Path();
	mPath.moveTo(a.x- Math.max(rPathAShadowDis, lPathAShadowDis) /2,a.y);
	mPath.lineTo(d.x,d.y);
	mPath.lineTo(e.x,e.y);
	mPath.lineTo(a.x,a.y);
	mPath.close();
	canvas.clipPath(pathA);
	canvas.clipPath(mPath, Region.Op.INTERSECT);

	float mDegrees = (float) Math.toDegrees(Math.atan2(e.x-a.x, a.y-e.y));
	canvas.rotate(mDegrees, e.x, e.y);

	gradientDrawable.setBounds(left,top,right,bottom);
	gradientDrawable.draw(canvas);
}

/** * Draw A region right shadow *@param canvas
 */
private void drawPathARightShadow(Canvas canvas, Path pathA){
	canvas.restore();
	canvas.save();

	int deepColor = 0x33333333;
	int lightColor = 0x01333333;
	int[] gradientColors = {deepColor,lightColor,lightColor};// Gradient color array

	float viewDiagonalLength = (float) Math.hypot(viewWidth, viewHeight);//view diagonal length
	int left = (int) h.x;
	int right = (int) (h.x + viewDiagonalLength*10);// The length should be long enough
	int top;
	int bottom;

	GradientDrawable gradientDrawable;
	if (style.equals(STYLE_TOP_RIGHT)) {
		gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.BOTTOM_TOP, gradientColors);
		gradientDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);

		top = (int) (h.y- rPathAShadowDis /2);
		bottom = (int) h.y;
	} else {
		gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, gradientColors);
		gradientDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);

		top = (int) h.y;
		bottom = (int) (h.y+ rPathAShadowDis /2);
	}
	gradientDrawable.setBounds(left,top,right,bottom);

	// Crop out the area we need
	Path mPath = new Path();
	mPath.moveTo(a.x- Math.max(rPathAShadowDis, lPathAShadowDis) /2,a.y);
// mPath.lineTo(i.x,i.y);
	mPath.lineTo(h.x,h.y);
	mPath.lineTo(a.x,a.y);
	mPath.close();
	canvas.clipPath(pathA);
	canvas.clipPath(mPath, Region.Op.INTERSECT);

	float mDegrees = (float) Math.toDegrees(Math.atan2(a.y-h.y, a.x-h.x));
	canvas.rotate(mDegrees, h.x, h.y);
	gradientDrawable.draw(canvas);
}
Copy the code

The effect is shown in figure

Finally, the shadow of horizontal page turning belongs to the special case of the right shadow in area A, and its drawing conditions are as follows: Shaded rectangle with short side length 1/2 the distance from point I to line ah (maximum 30, maximum can be adjusted as required), rotate center a, rotate Angle math.toDegrees (math.atan2 (f.x-a.x, F.Y-H.Y)), modify BookPageView according to conditions

/** * Draw the contents of area A *@param canvas
 * @param pathA
 * @param pathPaint
 */
private void drawPathAContent(Canvas canvas, Path pathA, Paint pathPaint){
	Bitmap contentBitmap = Bitmap.createBitmap(viewWidth, viewHeight, Bitmap.Config.RGB_565);
	Canvas contentCanvas = new Canvas(contentBitmap);

	// Start drawing the contents of the region...
	contentCanvas.drawPath(pathA,pathPaint);
	contentCanvas.drawText("Here's what's in section A... AAAA", viewWidth-260, viewHeight-100, textPaint);

	// Finish drawing the contents of the area...

	canvas.save();
	canvas.clipPath(pathA, Region.Op.INTERSECT);// Clipping the drawing content, take the intersection with A region
	canvas.drawBitmap(contentBitmap, 0.0.null);

	if(style.equals(STYLE_LEFT) || style.equals(STYLE_RIGHT)){// Turn the page horizontally
		drawPathAHorizontalShadow(canvas,pathA);
	}else {// Page up and down
		drawPathALeftShadow(canvas,pathA);
		drawPathARightShadow(canvas,pathA);
	}
	canvas.restore();
}

/** * Draw A horizontal flip shadow for area A *@param canvas
 */
private void drawPathAHorizontalShadow(Canvas canvas, Path pathA){
	canvas.restore();
	canvas.save();

	int deepColor = 0x44333333;
	int lightColor = 0x01333333;
	int[] gradientColors = {lightColor,deepColor};// Gradient color array

	GradientDrawable gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, gradientColors);
	gradientDrawable.setGradientType(GradientDrawable.LINEAR_GRADIENT);

	int maxShadowWidth = 30;// The maximum width of the shaded rectangle
	int left = (int) (a.x - Math.min(maxShadowWidth,(rPathAShadowDis/2)));
	int right = (int) (a.x);
	int top = 0;
	int bottom = viewHeight;
	gradientDrawable.setBounds(left,top,right,bottom);

	canvas.clipPath(pathA, Region.Op.INTERSECT);

	float mDegrees = (float) Math.toDegrees(Math.atan2(f.x-a.x,f.y-h.y));
	canvas.rotate(mDegrees, a.x, a.y);
	gradientDrawable.draw(canvas);
}
Copy the code

The effect is shown in figure

Finally adjust the color and have a look at the final image

This tutorial ends here. We have analyzed and implemented each part of the page-turning effect one by one. After the “house” is built, the next natural step is to “decorate the house”. In the next period, we will optimize the performance of this View (~ ▽ ~). If you feel good, please give me a thumbs up, your support is my biggest motivation ~