在逛UI中国时,看到一个阳光加载小动画,觉得好喜欢,于是就也想实现一下,并在我的App中使用:
于是就在想有什么方法可以画出螺旋线,起初我想通过螺旋线的参数方程进行实现,无奈在网上搜来搜去也只找到它的极坐标方程,且不容易转换成直角坐标方程,后来我又想到一个方法,就是通过drawArc()方法画若干个90°的圆弧进行实现,具体思路如图
后来发现这样子画出的螺旋线特别别扭,而且不知道在没去实现这样一个动画,就又想啦另外一种实现的思路,通过ObjectAnimator来进行实现,具体思路如下:
即通过ObjectAnimator同时扩大圆弧的起始角度和半径,这样子又可以实现动画,切画出的螺旋线也比较圆滑,之后即通过一定的三角函数来控制那5点阳光的位置,并把它画出来,大致的实现效果为(gif图制作有点粗糙)
具体代码如下:
自定View
SunnyLoad.java
public class SunnyLoad extends View { //View's width private int VIEW_WIDTH; //View's height private int VIEW_HEIGHT; //画笔 private Paint mPaint; //圆弧的半径 private float mRadius; //圆弧的起始角度 private float mStartAngel = 0f; //缓存位图 private Bitmap mCacheBitmap; //缓存画布 private Canvas mCacheCanvas; //是否为第一个动画 private boolean isFirstAnim = true; //最大半径 private float maxRadius = -1; //最小半径 private float minRadius = -1; //光线长度 private float mLineLength; //光线最大长度 private float maxLineLength = -1; //一次完整动画是否结束的标志 private boolean isFinished = false; private RectF mRectF; private int index = 0; private static final int INDEX_ONE = 0; private static final int INDEX_TWO = 1; private static final int INDEX_THREE = 2; private static final int INDEX_FOUR = 3; private static final int INDEX_FIVE = 4; private static final int[] INDEXES = new int[]{INDEX_ONE, INDEX_TWO, INDEX_THREE, INDEX_FOUR, INDEX_FIVE}; public SunnyLoad(Context context) { super(context); init(); } public SunnyLoad(Context context, AttributeSet attrs) { super(context, attrs); init(); } public SunnyLoad(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { mRectF = new RectF(); mPaint = new Paint(); mPaint.setColor(Color.YELLOW); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeCap(Paint.Cap.ROUND); mPaint.setAntiAlias(true); mPaint.setStrokeWidth(30f); postDelayed(new Runnable() { @Override public void run() { startFirstAnim(0); } },1000); invalidate(); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); VIEW_WIDTH = getWidth(); VIEW_HEIGHT = getHeight(); mCacheBitmap = Bitmap.createBitmap(VIEW_WIDTH, VIEW_HEIGHT, Bitmap.Config.ARGB_8888); mCacheCanvas = new Canvas(mCacheBitmap); } /** * 设置画笔的颜色和粗细 * * @param color 画笔颜色 * @param width 画笔粗细 */ public void setColorAndPaintWidth(int color, float width) { mPaint.setColor(color); mPaint.setStrokeWidth(width); invalidate(); } private void startFirstAnim(long delayed) { if (maxRadius == -1) maxRadius = this.VIEW_WIDTH * 0.1492f; if (minRadius == -1) minRadius = this.VIEW_WIDTH * 0.0333f; if (maxLineLength == -1) maxLineLength = this.maxRadius * 0.3333f; // System.out.println("FirstAnim"); AnimatorSet animatorSet = new AnimatorSet(); ObjectAnimator angel_animator = ObjectAnimator.ofFloat(this, "startAngel", -60, 450); ObjectAnimator radius_animator = ObjectAnimator.ofFloat(this, "radius", minRadius, maxRadius); animatorSet.playTogether(angel_animator, radius_animator); animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { isFirstAnim=false; index = INDEX_ONE; startSecondAnim(); super.onAnimationEnd(animation); } }); animatorSet.setDuration(2500); animatorSet.setStartDelay(delayed); animatorSet.start(); } private void startSecondAnim() { // System.out.println("SecondAnim"); ObjectAnimator length_animator = ObjectAnimator.ofFloat(this, "lineLength", 0, maxLineLength); length_animator.setDuration(200); length_animator.setInterpolator(new AccelerateInterpolator()); length_animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); if (index != INDEXES.length) { index++; startSecondAnim(); } else { isFirstAnim=true; isFinished =true; } } }); length_animator.start(); } @Override public void draw(Canvas canvas) { super.draw(canvas); if (isFinished) { //清除缓存 mCacheCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); startFirstAnim(1000); isFinished=false; return; } if (isFirstAnim) { mRectF.left = VIEW_WIDTH * 0.4854f - mRadius; mRectF.right = VIEW_WIDTH * 0.4854f + mRadius; mRectF.top = VIEW_HEIGHT * 0.5f - mRadius; mRectF.bottom = VIEW_HEIGHT * 0.5f + mRadius; mCacheCanvas.drawArc(mRectF, mStartAngel, 20, false, mPaint); canvas.drawBitmap(mCacheBitmap, 0, 0, mPaint); } if (!isFirstAnim) { switch (index) { case INDEX_ONE: mCacheCanvas.drawLine(VIEW_WIDTH * 0.5f - (maxRadius + VIEW_WIDTH * 0.04255f) * 0.9397f, VIEW_HEIGHT * 0.5f - (maxRadius + VIEW_WIDTH * 0.04255f) * 0.3420f, VIEW_WIDTH * 0.5f - ((maxRadius + VIEW_WIDTH * 0.04255f) + mLineLength) * 0.9397f, VIEW_HEIGHT * 0.5f - ((maxRadius + VIEW_WIDTH * 0.04255f) + mLineLength) * 0.3420f, mPaint); canvas.drawBitmap(mCacheBitmap, 0, 0, mPaint); break; case INDEX_TWO: mCacheCanvas.drawLine(VIEW_WIDTH * 0.5f - (maxRadius + VIEW_WIDTH * 0.04255f) * 0.5000f, VIEW_HEIGHT * 0.5f - (maxRadius + VIEW_WIDTH * 0.04255f) * 0.8660f, VIEW_WIDTH * 0.5f - ((maxRadius + VIEW_WIDTH * 0.04255f) + mLineLength) * 0.5000f, VIEW_HEIGHT * 0.5f - ((maxRadius + VIEW_WIDTH * 0.04255f) + mLineLength) * 0.8660f, mPaint); canvas.drawBitmap(mCacheBitmap, 0, 0, mPaint); break; case INDEX_THREE: mCacheCanvas.drawLine(VIEW_WIDTH * 0.5f + (maxRadius + VIEW_WIDTH * 0.04255f) * 0.1736f, VIEW_HEIGHT * 0.5f - (maxRadius + VIEW_WIDTH * 0.04255f) * 0.9848f, VIEW_WIDTH * 0.5f + ((maxRadius + VIEW_WIDTH * 0.04255f) + mLineLength) * 0.1736f, VIEW_HEIGHT * 0.5f - ((maxRadius + VIEW_WIDTH * 0.04255f) + mLineLength) * 0.9848f, mPaint); canvas.drawBitmap(mCacheBitmap, 0, 0, mPaint); break; case INDEX_FOUR: mCacheCanvas.drawLine(VIEW_WIDTH * 0.5f + (maxRadius + VIEW_WIDTH * 0.04255f) * 0.7660f, VIEW_HEIGHT * 0.5f - (maxRadius + VIEW_WIDTH * 0.04255f) * 0.6428f, VIEW_WIDTH * 0.5f + ((maxRadius + VIEW_WIDTH * 0.04255f) + mLineLength) * 0.7660f, VIEW_HEIGHT * 0.5f - ((maxRadius + VIEW_WIDTH * 0.04255f) + mLineLength) * 0.6428f, mPaint); canvas.drawBitmap(mCacheBitmap, 0, 0, mPaint); break; case INDEX_FIVE: mCacheCanvas.drawLine(VIEW_WIDTH * 0.5f + (maxRadius + VIEW_WIDTH * 0.04255f), VIEW_HEIGHT * 0.5f, VIEW_WIDTH * 0.5f + ((maxRadius + VIEW_WIDTH * 0.04255f) + mLineLength), VIEW_HEIGHT * 0.5f, mPaint); canvas.drawBitmap(mCacheBitmap, 0, 0, mPaint); break; } } } public void setMaxRaius(float maxRadius) { this.maxRadius = maxRadius; } public float getMaxRaius() { return maxRadius; } public float getStartAngel() { return mStartAngel; } public void setStartAngel(float startAngel) { mStartAngel = startAngel; invalidate(); } public float getLineLength() { return mLineLength; } public float getMaxLineLength() { return maxLineLength; } public void setMaxLineLength(float maxLineLength) { this.maxLineLength = maxLineLength; } public void setLineLength(float lineLength) { mLineLength = lineLength; invalidate(); } public float getRadius() { return mRadius; } public void setRadius(float radius) { mRadius = radius; invalidate(); } public Paint getPaint() { return mPaint; } public void setPaint(Paint paint) { mPaint = paint; } public Bitmap getCacheBitmap() { return mCacheBitmap; } public void setCacheBitmap(Bitmap cacheBitmap) { mCacheBitmap = cacheBitmap; } public Canvas getCacheCanvas() { return mCacheCanvas; } public void setCacheCanvas(Canvas cacheCanvas) { mCacheCanvas = cacheCanvas; } public boolean isFirstAnim() { return isFirstAnim; } private void setIsFirstAnim(boolean isFirstAnim) { this.isFirstAnim = isFirstAnim; } public float getMinRadius() { return minRadius; } public void setMinRadius(float minRadius) { this.minRadius = minRadius; } public float getMaxRadius() { return maxRadius; } public void setMaxRadius(float maxRadius) { this.maxRadius = maxRadius; } public boolean isFinished() { return isFinished; } private void setIsFinished(boolean isFinished) { this.isFinished = isFinished; } }
就这样,一个简单的小动画就做好啦,但是,我发现这个动画做啦大量的运算和重绘,性能不怎么好,希望有人可以指导一下我该怎么样做出优秀的动画,而且对于5个光线的伸缩动画我也实现不了,希望有人可以帮助我。
最后,我发现自定义个性能好有漂亮的控件真的很难,而且对数学的要求也很高,我还有很多地方要努力,加油!
完整源码地址:https://github.com/wuapnjie/SunnyLoad
点击查看更多内容
为 TA 点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦