接上一篇
让内部气泡动起来
气泡内部的动画是水波的形式,这里画水波用的是二阶贝塞尔曲线,关于Android中贝塞尔曲线的知识可以参考这里。实现气泡内部水波效果的代码如下
/** * 核心代码,计算path * * @return */ private Path getPath() { int itemWidth = waveWidth / 2;//半个波长 Path mPath = new Path(); mPath.moveTo(-itemWidth * 3, baseLine);//起始坐标 Log.d(TAG, "getPath: " + baseLine); //核心的代码就是这里 for (int i = -3; i < 2; i++) { int startX = i * itemWidth; mPath.quadTo( startX + itemWidth / 2 + offset,//控制点的X,(起始点X + itemWidth/2 + offset) getWaveHeight(i),//控制点的Y startX + itemWidth + offset,//结束点的X baseLine//结束点的Y );//只需要处理完半个波长,剩下的有for循环自已就添加了。 } Log.d(TAG, "getPath: "); //下面这三句话是行程封闭的效果,不明白可以将下面3句代码注释看下效果的变化 mPath.lineTo(width, height); mPath.lineTo(0, height); mPath.close(); return mPath; } //奇数峰值是正的,偶数峰值是负数 private float getWaveHeight(int num) { if (num % 2 == 0) { return baseLine + waveHeight; } return baseLine - waveHeight; }
上面的代码画出的水波如下图
到这里已经画出了水波,但现在水波还是静止的,要让水波不停的移动,就要添加属性动画,添加动画的代码如下
/** * 不断的更新偏移量,并且循环。 */ public void updateXControl() { //设置一个波长的偏移 ValueAnimator mAnimator = ValueAnimator.ofFloat(0, waveWidth); mAnimator.setInterpolator(new LinearInterpolator()); mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float animatorValue = (float) animation.getAnimatedValue(); offset = animatorValue;//不断的设置偏移量,并重画 postInvalidate(); } }); mAnimator.setDuration(1800); mAnimator.setRepeatCount(ValueAnimator.INFINITE); mAnimator.start(); }
修改一下onDraw
中的代码,如下
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mBubblesPath.reset(); //设置渐变色 Shader shader = new LinearGradient(mResultWidth / 2, mResultWidth / 2 - mInnerRadius, mResultWidth / 2, mResultWidth / 2 + mInnerRadius, Color.parseColor("#9592FB"), Color.parseColor("#3831D4"), Shader.TileMode.CLAMP); mBubblesPaint.setShader(shader); //此处代码是下部尖角的path mBackgroundPath.moveTo(mResultWidth / 2 - mOutRadius / 2, mResultWidth / 2 + mOutRadius / 2); mBackgroundPath.lineTo(mResultWidth / 2, mResultWidth / 2 + mOutRadius + mOutRadius / 4); mBackgroundPath.lineTo(mResultWidth / 2 + mOutRadius / 2, mResultWidth / 2 + mOutRadius / 2); //内部气泡的尖角 mBubblesPath.moveTo(mResultWidth / 2 - mOutRadius / 2, mResultWidth / 2 + mOutRadius / 2 - dp2px(getContext(), 5)); mBubblesPath.lineTo(mResultWidth / 2, mResultWidth / 2 + mOutRadius + mOutRadius / 4 - dp2px(getContext(), 5)); mBubblesPath.lineTo(mResultWidth / 2 + mOutRadius / 2, mResultWidth / 2 + mOutRadius / 2 - dp2px(getContext(), 5)); //画外部背景 canvas.drawPath(mBackgroundPath, mBackgroundPaint); canvas.drawCircle(mResultWidth / 2, mResultWidth / 2, mOutRadius, mBackgroundPaint); Log.d(TAG, "cx: " + mResultWidth / 2); //画水波 mBubblesPath.addCircle(mResultWidth / 2, mResultWidth / 2, mInnerRadius, Path.Direction.CCW); canvas.drawPath(getPath(), mBubblesPaint); }
好了,现在水波已经可以移动了,看下效果
what!怎么成这个样子了呀,明显不是我想要的效果呀,肯定是哪里出错了,经过我仔细的推敲,总结了出现上面问题的原因,原因如下图
出现上面问题的原因就是因为下面三句代码
mPath.lineTo(width, height); mPath.lineTo(0, height); mPath.close();
知道是这三句代码的原因,那应该怎么修改呢?这三句代码好像不能动,不然就会出现波浪画的不完整的情况,额.....,那应该修改哪里呢?灵光一闪,不是可以裁剪画布嘛,只要将画布裁剪成想要的形状,然后在画波浪不久完美了。再修改onDraw
方法,修改后的代码如下
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mBubblesPath.reset(); //设置渐变色 Shader shader = new LinearGradient(mResultWidth / 2, mResultWidth / 2 - mInnerRadius, mResultWidth / 2, mResultWidth / 2 + mInnerRadius, Color.parseColor("#9592FB"), Color.parseColor("#3831D4"), Shader.TileMode.CLAMP); mBubblesPaint.setShader(shader); //此处代码是下部尖角的path mBackgroundPath.moveTo(mResultWidth / 2 - mOutRadius / 2, mResultWidth / 2 + mOutRadius / 2); mBackgroundPath.lineTo(mResultWidth / 2, mResultWidth / 2 + mOutRadius + mOutRadius / 4); mBackgroundPath.lineTo(mResultWidth / 2 + mOutRadius / 2, mResultWidth / 2 + mOutRadius / 2); //内部气泡的尖角 mBubblesPath.moveTo(mResultWidth / 2 - mOutRadius / 2, mResultWidth / 2 + mOutRadius / 2 - dp2px(getContext(), 5)); mBubblesPath.lineTo(mResultWidth / 2, mResultWidth / 2 + mOutRadius + mOutRadius / 4 - dp2px(getContext(), 5)); mBubblesPath.lineTo(mResultWidth / 2 + mOutRadius / 2, mResultWidth / 2 + mOutRadius / 2 - dp2px(getContext(), 5)); //画外部背景 canvas.drawPath(mBackgroundPath, mBackgroundPaint); canvas.drawCircle(mResultWidth / 2, mResultWidth / 2, mOutRadius, mBackgroundPaint); Log.d(TAG, "cx: " + mResultWidth / 2); //切割画布,画水波 canvas.save(); mBubblesPath.addCircle(mResultWidth / 2, mResultWidth / 2, mInnerRadius, Path.Direction.CCW); //将画布裁剪成内部气泡的样子 canvas.clipPath(mBubblesPath); canvas.drawPath(getPath(), mBubblesPaint); canvas.restore(); }
到这里已经实现了文章开始时的效果了,文章也该结束了。
结束语
本文主要是讲解怎样实现水波气泡,并没有讲到View的测量,贴出的也只是绘制气泡的代码,完整的代码可以点击这里获取。
虽然已经撸出了这个效果,但最后项目中并没有用这种动态的气泡,因为气泡多的时候是在是卡……。最后,喜欢此demo,就随手给个star吧!