android - 向 HexagonLoadingView 添加渐变动画
问题描述
我基于库https://github.com/Agraphie/hexagonloadingview使用了如下六边形加载器。但我需要与上述库略有不同的动画。喜欢https://codepen.io/wuser/pen/BgPMqE
六边形视图动画代码
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;
}
}
我怎样才能将动画修改为完全一样:https ://codepen.io/wuser/pen/BgPMqE
解决方案
首先,在 onDraw 方法中计算每个六边形对于图形性能来说确实很昂贵,因为它会为每个六边形创建一个着色器,因为 GC 会导致阻塞主线程。您可以在 onSizeChanged 回调中计算您的六边形。然后您可以使用 canvas:canvas.translate()
和canvas.scale()
. 每个六边形的平移和缩放由动画师根据经过的时间计算。对于这样的动画案例,您可以使用 TimerAnimator。在视图中启动 TimerAnimator 后,它会每 16 毫秒调用一次回调。在此回调中,您可以调用invalidate()
为了你的看法。为了计算每个六边形的 alpha 值,您还应该使用经过时间。对于这个视图,只有一个 Paint 就足够了。另外不要忘记在视图变为不可见或消失状态时停止 TimerAnimator。
推荐阅读
- python - Pygame 多子弹问题
- python - 使用 LSTM 预训练 Word2Vec,预测句子中的下一个单词
- vue.js - 获取参数的方法在 Ajax 请求中发送
- haskell - 为什么它没有显示为已评估?
- javascript - 如何组合来自不同承诺的响应并将其作为单个 json 对象返回给客户端
- javascript - 如何在某些元素具有设定宽度后平均间隔 div
- c++ - 比较 gtest 中的 ptr 和 nullptr
- c# - 如何将 PictureBox 和 TextBox 保存在一张图像中?
- r - 一次处理多个 csv 表并将结果粘贴到新列上
- python - 使用“下一步”按钮单击 Selenium Web 抓取会导致重复值