Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add fading animation to HexagonLoadingView

I have used a hexagon loader as below based on library https://github.com/Agraphie/hexagonloadingview. But I need slightly different animation with the above library. Like https://codepen.io/wuser/pen/BgPMqE

Code for Hexagon View Animation

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;

public class HexagonLoadingView extends View {

public static final int NUMBER_OF_HEXAGONS = 7;
/**
 * Constants for changing the number of appearance/disappearance of the hexagons. {@link
 * HexagonLoadingView#HEXAGON_UPPER_LEFT_APPEARANCE_POSITION} = 0 means the hexagon will start
 * appearing in the upper left corner. {@link HexagonLoadingView#HEXAGON_MIDDLE_MIDDLE_APPEARANCE_POSITION}
 * = 6 means this hexagon will appear last and disappear first.
 */
public static final int HEXAGON_UPPER_LEFT_APPEARANCE_POSITION = 0;
public static final int HEXAGON_UPPER_RIGHT_APPEARANCE_POSITION = 1;
public static final int HEXAGON_MIDDLE_LEFT_APPEARANCE_POSITION = 5;
public static final int HEXAGON_MIDDLE_MIDDLE_APPEARANCE_POSITION = 6;
public static final int HEXAGON_MIDDLE_RIGHT_APPEARANCE_POSITION = 2;
public static final int HEXAGON_LOWER_RIGHT_APPEARANCE_POSITION = 3;
public static final int HEXAGON_LOWER_LEFT_APPEARANCE_POSITION = 4;
/**
 * Increase this for a slower animation i.e. decrease this for a faster animation.
 */
public static final int APPEARANCE_SPEED_COEFFICIENT = 10;

/**
 * The radius of each hexagon.
 */
private float mRadius;

/**
 * The width and height of each hexagon.
 */
private float mWidth, mHeight;

/**
 * The various hexagons as {@link Path} objects.
 */
private Path mHexagonUpperRight;
private Path mHexagonMiddleRight;
private Path mHexagonLowerRight;
private Path mHexagonLowerLeft;
private Path mHexagonMiddleLeft;
private Path mHexagonUpperLeft;
private Path mHexagonMiddleMiddle;

/**
 * The {@link Paint} objects for each hexagon. Every hexagon can have its own colour.
 */
private Paint mHexagonPaintUpperRight = new Paint();
private Paint mHexagonPaintMiddleRight = new Paint();
private Paint mHexagonPaintLowerRight = new Paint();
private Paint mHexagonPaintLowerLeft = new Paint();
private Paint mHexagonPaintMiddleLeft = new Paint();
private Paint mHexagonPaintUpperLeft = new Paint();
private Paint mHexagonPaintMiddleMiddle = new Paint();

/**
 * Field for identifying if hexagons should be currently set to the background colour or to
 * their given colour.
 */
private boolean displayHexagons = true;

private float mRadiusStep;
private float[] mHexagonRadius;

public HexagonLoadingView(Context context) {
    super(context);
}

public HexagonLoadingView(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public HexagonLoadingView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}

/**
 * Method for calculating the hexagons, taking into account the current radius of the specified
 * hexagon.
 */
private void calculateHexagons() {
    mHexagonPaintUpperRight.setShader(new LinearGradient(0, 0, 0, getHeight(),
            getContext().getResources().getColor(R.color.hex_loading_color_8),
            getContext().getResources().getColor(R.color.hex_loading_color_9),
            Shader.TileMode.MIRROR));
    mHexagonPaintMiddleRight.setShader(new LinearGradient(0, 0, 0, getHeight(),
            getContext().getResources().getColor(R.color.hex_loading_color_8),
            getContext().getResources().getColor(R.color.hex_loading_color_9),
            Shader.TileMode.MIRROR));
    mHexagonPaintLowerRight.setShader(new LinearGradient(0, 0, 0, getHeight(),
            getContext().getResources().getColor(R.color.hex_loading_color_8),
            getContext().getResources().getColor(R.color.hex_loading_color_9),
            Shader.TileMode.MIRROR));
    mHexagonPaintLowerLeft.setShader(new LinearGradient(0, 0, 0, getHeight(),
            getContext().getResources().getColor(R.color.hex_loading_color_8),
            getContext().getResources().getColor(R.color.hex_loading_color_9),
            Shader.TileMode.MIRROR));
    mHexagonPaintMiddleLeft.setShader(new LinearGradient(0, 0, 0, getHeight(),
            getContext().getResources().getColor(R.color.hex_loading_color_8),
            getContext().getResources().getColor(R.color.hex_loading_color_9),
            Shader.TileMode.MIRROR));
    mHexagonPaintUpperLeft.setShader(new LinearGradient(0, 0, 0, getHeight(),
            getContext().getResources().getColor(R.color.hex_loading_color_8),
            getContext().getResources().getColor(R.color.hex_loading_color_9),
            Shader.TileMode.MIRROR));
    mHexagonPaintMiddleMiddle.setShader(new LinearGradient(0, 0, 0, getHeight(),
            getContext().getResources().getColor(R.color.hex_loading_color_8),
            getContext().getResources().getColor(R.color.hex_loading_color_9),
            Shader.TileMode.MIRROR));

    mHexagonUpperLeft = calculatePath((int) -(mRadius), (int) -(mRadius * 1.7),
            mHexagonRadius[HEXAGON_UPPER_LEFT_APPEARANCE_POSITION]);
    mHexagonUpperRight = calculatePath((int) (mRadius), ((int) -(mRadius * 1.7)),
            mHexagonRadius[HEXAGON_UPPER_RIGHT_APPEARANCE_POSITION]);
    mHexagonMiddleLeft = calculatePath((int) (-1.95 * mRadius), 0,
            mHexagonRadius[HEXAGON_MIDDLE_LEFT_APPEARANCE_POSITION]);
    mHexagonMiddleMiddle = calculatePath(0, 0,
            mHexagonRadius[HEXAGON_MIDDLE_MIDDLE_APPEARANCE_POSITION]);
    mHexagonMiddleRight = calculatePath((int) (1.95 * mRadius), 0,
            mHexagonRadius[HEXAGON_MIDDLE_RIGHT_APPEARANCE_POSITION]);
    mHexagonLowerLeft = calculatePath((int) -(mRadius), (int) (mRadius * 1.7),
            mHexagonRadius[HEXAGON_LOWER_LEFT_APPEARANCE_POSITION]);
    mHexagonLowerRight = calculatePath((int) (mRadius), (int) (mRadius * 1.7),
            mHexagonRadius[HEXAGON_LOWER_RIGHT_APPEARANCE_POSITION]);
}

@Override
public void onDraw(Canvas c) {
    //Check if this is the first load, if so don't do anything and display only the background
    //for a while
    calculateHexagons();

    //Count the hexagons up i.e. down i.e. make them appear or disappear.
    //Increase always only one hexagon at a time which has not been fully drawn yet.
    //Also check which hexagons have been completed.
    int completedHexagons = 0;
    if (displayHexagons) {
        for (int i = 0; i < mHexagonRadius.length; i++) {
            if (mHexagonRadius[i] < mRadius) {
                mHexagonRadius[i] += mRadiusStep;
                break;
            }
            completedHexagons++;
        }
    } else {
        for (int i = 0; i < mHexagonRadius.length; i++) {
            if (mHexagonRadius[i] > 0) {
                mHexagonRadius[i] = (mHexagonRadius[i] + (mRadiusStep * -1) < 0) ? 0
                        : mHexagonRadius[i] + (mRadiusStep * -1);
                break;
            }
            completedHexagons++;
        }
    }

    checkDrawingMode(completedHexagons);

    //Now draw our hexagons
    c.drawPath(mHexagonUpperLeft, mHexagonPaintUpperLeft);
    c.drawPath(mHexagonUpperRight, mHexagonPaintUpperRight);
    c.drawPath(mHexagonMiddleRight, mHexagonPaintMiddleRight);
    c.drawPath(mHexagonLowerRight, mHexagonPaintLowerRight);
    c.drawPath(mHexagonLowerLeft, mHexagonPaintLowerLeft);
    c.drawPath(mHexagonMiddleLeft, mHexagonPaintMiddleLeft);
    c.drawPath(mHexagonMiddleMiddle, mHexagonPaintMiddleMiddle);
}

/**
 * Method for checking how many hexagons are completed in their drawing. If all hexagons are
 * completed (i.e. all appeared or disappeared), invert the drawing mode to the opposite of what
 * it was.
 */
private void checkDrawingMode(int completedHexagons) {
    if (completedHexagons == NUMBER_OF_HEXAGONS) {
        displayHexagons = !displayHexagons;
    }
}

@Override
protected void onAttachedToWindow() {
    super.onAttachedToWindow();

    mHexagonRadius = new float[NUMBER_OF_HEXAGONS];
    mHexagonRadius[HEXAGON_UPPER_LEFT_APPEARANCE_POSITION] = 0;
    mHexagonRadius[HEXAGON_UPPER_RIGHT_APPEARANCE_POSITION] = 0;
    mHexagonRadius[HEXAGON_MIDDLE_RIGHT_APPEARANCE_POSITION] = 0;
    mHexagonRadius[HEXAGON_LOWER_RIGHT_APPEARANCE_POSITION] = 0;
    mHexagonRadius[HEXAGON_LOWER_LEFT_APPEARANCE_POSITION] = 0;
    mHexagonRadius[HEXAGON_MIDDLE_LEFT_APPEARANCE_POSITION] = 0;
    mHexagonRadius[HEXAGON_MIDDLE_MIDDLE_APPEARANCE_POSITION] = 0;
}

@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    mWidth = MeasureSpec.getSize(widthMeasureSpec);
    mHeight = MeasureSpec.getSize(heightMeasureSpec);
    mRadius = mHeight / 6;
    mRadiusStep = mRadius / APPEARANCE_SPEED_COEFFICIENT;
}


/**
 * Calculate the path for a hexagon.
 *
 * @param xCenterScale The offset in the x direction.
 * @param yCenterScale The offset in the y direction.
 * @return The calculated hexagon as {@link Path} object.
 */
private Path calculatePath(int xCenterScale, int yCenterScale, float radius) {
    float triangleHeight = (float) (Math.sqrt(3) * radius / 2);
    float centerX = (mWidth / 2) + xCenterScale;
    float centerY = (mHeight / 2) + yCenterScale;
    Path hexagonPath = new Path();

    hexagonPath.moveTo(centerX, centerY + radius);
    hexagonPath.lineTo(centerX - triangleHeight, centerY + radius / 2);
    hexagonPath.lineTo(centerX - triangleHeight, centerY - radius / 2);
    hexagonPath.lineTo(centerX, centerY - radius);
    hexagonPath.lineTo(centerX + triangleHeight, centerY - radius / 2);
    hexagonPath.lineTo(centerX + triangleHeight, centerY + radius / 2);
    hexagonPath.moveTo(centerX, centerY + radius);

    invalidate();

    return hexagonPath;
  }


} 

How can i modify the animation to exactly like this : https://codepen.io/wuser/pen/BgPMqE

like image 659
SRBhagwat Avatar asked Jul 05 '19 09:07

SRBhagwat


1 Answers

Firstly, calculating each hexagonal at onDraw method is really expensive for the graphic performance because it creates a shader for each hexagonal it causes to block the main thread because of GC. You can calculate your hexagonals at the onSizeChanged callback. Then you can draw each hexagonal by using methods of canvas: canvas.translate() and canvas.scale(). The translation and scaling of each hexagonal are calculated according to elapsed time by the animator. For such a animation case you can use TimerAnimator. Once you start the TimerAnimator in your view it calls you callback for each 16ms. In this callback you can call invalidate() for your view. In order to calculate an alpha value for each hexagonal you should also use elapsed time. Only one Paint is enough for this view. Additional don't forget to stop TimerAnimator when the view turns to invisible or gone state.

like image 135
M.ekici Avatar answered Sep 19 '22 11:09

M.ekici