From 8609c6c2e07ece176f2a5c51da5cade0f9cdd1d4 Mon Sep 17 00:00:00 2001 From: xiaoyao Date: Fri, 16 Jun 2017 18:36:39 +0800 Subject: [PATCH 01/50] =?UTF-8?q?=E8=B4=9D=E5=A1=9E=E5=B0=94=E6=9B=B2?= =?UTF-8?q?=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/misc.xml | 2 +- .../activity/WaveByBezierActivity.java | 8 - .../widget/WaveViewByBezier.java | 148 ++++++++++++------ .../res/layout/activity_wave_by_bezier.xml | 13 +- 4 files changed, 102 insertions(+), 69 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index 54207cd..5d19981 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -37,7 +37,7 @@ - + diff --git a/app/src/main/java/com/allen/androidcustomview/activity/WaveByBezierActivity.java b/app/src/main/java/com/allen/androidcustomview/activity/WaveByBezierActivity.java index c96f0c4..fff4708 100644 --- a/app/src/main/java/com/allen/androidcustomview/activity/WaveByBezierActivity.java +++ b/app/src/main/java/com/allen/androidcustomview/activity/WaveByBezierActivity.java @@ -1,25 +1,19 @@ package com.allen.androidcustomview.activity; -import android.os.Build; -import android.support.annotation.RequiresApi; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import com.allen.androidcustomview.R; -import com.allen.androidcustomview.widget.WaveAnimatorView; import com.allen.androidcustomview.widget.WaveViewByBezier; public class WaveByBezierActivity extends AppCompatActivity { - private WaveAnimatorView waveAnimatorView; private WaveViewByBezier waveViewByBezier; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_wave_by_bezier); - waveAnimatorView = (WaveAnimatorView) findViewById(R.id.wave_animator); - waveViewByBezier = (WaveViewByBezier) findViewById(R.id.wave_bezier); } @@ -32,13 +26,11 @@ protected void onPause() { @Override protected void onResume() { super.onResume(); -// waveViewByBezier.resumeAnimation(); } @Override protected void onRestart() { super.onRestart(); - waveViewByBezier.resumeAnimation(); } @Override diff --git a/app/src/main/java/com/allen/androidcustomview/widget/WaveViewByBezier.java b/app/src/main/java/com/allen/androidcustomview/widget/WaveViewByBezier.java index 10b2de2..2d7219e 100644 --- a/app/src/main/java/com/allen/androidcustomview/widget/WaveViewByBezier.java +++ b/app/src/main/java/com/allen/androidcustomview/widget/WaveViewByBezier.java @@ -6,14 +6,11 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; -import android.os.Build; -import android.support.annotation.RequiresApi; import android.util.AttributeSet; -import android.util.Log; +import android.util.TypedValue; import android.view.View; import android.view.animation.LinearInterpolator; -import static android.content.ContentValues.TAG; /** * Created by allen on 2016/12/13. @@ -39,18 +36,8 @@ public class WaveViewByBezier extends View implements View.OnClickListener { /** * 一个周期波浪的长度 */ - private int mWaveLength = 1000; + private int mWaveLength; - - /** - * 屏幕宽度内波浪显示几个周期 - */ - private int mWaveCount; - - /** - * 波纹的中间轴(基准线) - */ - private int mCenterY; /** * 波浪的路径 */ @@ -64,33 +51,33 @@ public class WaveViewByBezier extends View implements View.OnClickListener { /** * 振幅 */ - private int mWaveAmplitude = 25; + private int mWaveAmplitude = 50; private ValueAnimator valueAnimator; - private boolean isStart = true; + private boolean isStart = false; public WaveViewByBezier(Context context) { - super(context); + this(context, null); } public WaveViewByBezier(Context context, AttributeSet attrs) { - super(context, attrs); - init(); + this(context, attrs, 0); } public WaveViewByBezier(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); + init(); } private void init() { + mWaveAmplitude = dp2px(15); initPaint(); - - initAnimation(); + setOnClickListener(this); } @@ -108,27 +95,61 @@ private void initPaint() { } - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - super.onSizeChanged(w, h, oldw, oldh); - mScreenHeight = h; - mScreenWidth = w; + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int widthMode = MeasureSpec.getMode(widthMeasureSpec); + int width = MeasureSpec.getSize(widthMeasureSpec); -// mWaveCount = (int) Math.round(mScreenWidth / mWaveLength + 1.5); - mWaveCount = 1; - Log.d(TAG, "onSizeChanged: =======" + mWaveCount); - mCenterY = mScreenHeight * 3 / 4; + int heightMode = MeasureSpec.getMode(heightMeasureSpec); + int height = MeasureSpec.getSize(heightMeasureSpec); - setOnClickListener(this); + setMeasuredDimension(measureWidth(widthMode, width), measureHeight(heightMode, height)); + } + /** + * 测量宽度 + * + * @param mode + * @param width + * @return + */ + private int measureWidth(int mode, int width) { + switch (mode) { + case MeasureSpec.UNSPECIFIED: + case MeasureSpec.AT_MOST: + break; + case MeasureSpec.EXACTLY: + mScreenWidth = width; + break; + } + return mScreenWidth; } + /** + * 测量高度 + * + * @param mode + * @param height + * @return + */ + private int measureHeight(int mode, int height) { + switch (mode) { + case MeasureSpec.UNSPECIFIED: + case MeasureSpec.AT_MOST: + break; + case MeasureSpec.EXACTLY: + mScreenHeight = height; + break; + } + return mScreenHeight; + } + + @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); - mWaveLength = getWidth(); + mWaveLength = mScreenWidth; mWavePath.reset(); // mWavePath.moveTo(-mWaveLength + mOffset, mCenterY); @@ -141,21 +162,36 @@ protected void onDraw(Canvas canvas) { // mWavePath.lineTo(0, mScreenHeight); // mWavePath.close(); - mWavePath.moveTo(mOffset, mCenterY); + mWavePath.moveTo(-mWaveLength + mOffset, mWaveAmplitude); - for (int i = 0; i < mWaveCount; i++) { - mWavePath.quadTo(mWaveLength /4+ mOffset, mCenterY + 60, mWaveLength / 2 + mOffset, mCenterY); - mWavePath.quadTo(mWaveLength *3/ 4 + mOffset, mCenterY - 60, + mOffset, mCenterY); - } - mWavePath.lineTo(mScreenWidth, mScreenHeight); - mWavePath.lineTo(0, mScreenHeight); + mWavePath.quadTo(-mWaveLength * 3 / 4 + mOffset, -mWaveAmplitude, -mWaveLength / 2 + mOffset, mWaveAmplitude); + + mWavePath.quadTo(-mWaveLength / 4 + mOffset, 3 * mWaveAmplitude, 0 + mOffset, mWaveAmplitude); + + mWavePath.quadTo(mWaveLength / 4 + mOffset, -mWaveAmplitude, mWaveLength / 2 + mOffset, mWaveAmplitude); + + mWavePath.quadTo(mWaveLength * 3 / 4 + mOffset, 3 * mWaveAmplitude, mWaveLength + mOffset, mWaveAmplitude); + + mWavePath.lineTo(getWidth(), getHeight()); + mWavePath.lineTo(0, getHeight()); mWavePath.close(); + + +// mWavePath.moveTo(mOffset, mCenterY); +// +// for (int i = 0; i < mWaveCount; i++) { +// mWavePath.quadTo(mWaveLength /4+ mOffset, mCenterY + 60, mWaveLength / 2 + mOffset, mCenterY); +// mWavePath.quadTo(mWaveLength *3/ 4 + mOffset, mCenterY - 60, + mOffset, mCenterY); +// } +// mWavePath.lineTo(mScreenWidth, mScreenHeight); +// mWavePath.lineTo(0, mScreenHeight); +// mWavePath.close(); canvas.drawPath(mWavePath, mWavePaint); } private void initAnimation() { - valueAnimator = ValueAnimator.ofInt(0, getWidth()); + valueAnimator = ValueAnimator.ofInt(0, mScreenWidth); valueAnimator.setDuration(3000); valueAnimator.setRepeatCount(ValueAnimator.INFINITE); valueAnimator.setInterpolator(new LinearInterpolator()); @@ -166,14 +202,13 @@ public void onAnimationUpdate(ValueAnimator animation) { postInvalidate(); } }); - valueAnimator.start(); } public void startAnimation() { isStart = true; if (valueAnimator != null) { - valueAnimator.reverse(); + valueAnimator.start(); } } @@ -184,6 +219,7 @@ public void stopAnimation() { this.clearAnimation(); } } + public void pauseAnimation() { isStart = false; if (valueAnimator != null) { @@ -195,17 +231,33 @@ public void resumeAnimation() { isStart = true; if (valueAnimator != null) { valueAnimator.resume(); - onAnimationStart(); } } @Override public void onClick(View v) { - if (isStart){ - pauseAnimation(); - }else { - resumeAnimation(); + initAnimation(); + if (valueAnimator != null) { + if (isStart) { + isStart = false; + valueAnimator.pause(); + } else { + isStart = true; + valueAnimator.start(); + } } + + } + + + /** + * dp 2 px + * + * @param dpVal + */ + protected int dp2px(int dpVal) { + return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, + dpVal, getResources().getDisplayMetrics()); } } diff --git a/app/src/main/res/layout/activity_wave_by_bezier.xml b/app/src/main/res/layout/activity_wave_by_bezier.xml index b886f00..ea3abad 100644 --- a/app/src/main/res/layout/activity_wave_by_bezier.xml +++ b/app/src/main/res/layout/activity_wave_by_bezier.xml @@ -1,15 +1,10 @@ + android:paddingTop="100dp"> - - From 49c635c79291f1e012571e7150faa03a1d7eec6d Mon Sep 17 00:00:00 2001 From: xiaoyao Date: Mon, 19 Jun 2017 18:15:46 +0800 Subject: [PATCH 02/50] =?UTF-8?q?=E8=B4=9D=E5=A1=9E=E5=B0=94=E6=9B=B2?= =?UTF-8?q?=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activity/WaveByBezierActivity.java | 7 +- .../androidcustomview/bean/WaveBean.java | 48 +++-- .../widget/WaveViewByBezier.java | 204 +++++++++--------- 3 files changed, 126 insertions(+), 133 deletions(-) diff --git a/app/src/main/java/com/allen/androidcustomview/activity/WaveByBezierActivity.java b/app/src/main/java/com/allen/androidcustomview/activity/WaveByBezierActivity.java index fff4708..47b9476 100644 --- a/app/src/main/java/com/allen/androidcustomview/activity/WaveByBezierActivity.java +++ b/app/src/main/java/com/allen/androidcustomview/activity/WaveByBezierActivity.java @@ -9,12 +9,14 @@ public class WaveByBezierActivity extends AppCompatActivity { private WaveViewByBezier waveViewByBezier; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_wave_by_bezier); waveViewByBezier = (WaveViewByBezier) findViewById(R.id.wave_bezier); + waveViewByBezier.startAnimation(); } @Override @@ -26,12 +28,9 @@ protected void onPause() { @Override protected void onResume() { super.onResume(); + waveViewByBezier.resumeAnimation(); } - @Override - protected void onRestart() { - super.onRestart(); - } @Override protected void onDestroy() { diff --git a/app/src/main/java/com/allen/androidcustomview/bean/WaveBean.java b/app/src/main/java/com/allen/androidcustomview/bean/WaveBean.java index 7185b61..4be915c 100644 --- a/app/src/main/java/com/allen/androidcustomview/bean/WaveBean.java +++ b/app/src/main/java/com/allen/androidcustomview/bean/WaveBean.java @@ -9,45 +9,51 @@ public class WaveBean { private int waveLength; - private int offset; - private int startOffset; + private int waveAmplitude; - private int waveRepeatCount; + private int waveColor; - public WaveBean(int waveLength, int startOffset) { + private int waveType; + + private int duration; + + public int getWaveLength() { + return waveLength; + } + + public void setWaveLength(int waveLength) { this.waveLength = waveLength; - this.startOffset = startOffset; } - public int getStartOffset() { - return startOffset; + public int getWaveAmplitude() { + return waveAmplitude; } - public void setStartOffset(int startOffset) { - this.startOffset = startOffset; + public void setWaveAmplitude(int waveAmplitude) { + this.waveAmplitude = waveAmplitude; } - public int getWaveRepeatCount() { - return waveRepeatCount; + public int getWaveColor() { + return waveColor; } - public void setWaveRepeatCount(int waveRepeatCount) { - this.waveRepeatCount = waveRepeatCount; + public void setWaveColor(int waveColor) { + this.waveColor = waveColor; } - public int getWaveLength() { - return waveLength; + public int getWaveType() { + return waveType; } - public void setWaveLength(int waveLength) { - this.waveLength = waveLength; + public void setWaveType(int waveType) { + this.waveType = waveType; } - public int getOffset() { - return offset; + public int getDuration() { + return duration; } - public void setOffset(int offset) { - this.offset = offset; + public void setDuration(int duration) { + this.duration = duration; } } diff --git a/app/src/main/java/com/allen/androidcustomview/widget/WaveViewByBezier.java b/app/src/main/java/com/allen/androidcustomview/widget/WaveViewByBezier.java index 2d7219e..82adaf4 100644 --- a/app/src/main/java/com/allen/androidcustomview/widget/WaveViewByBezier.java +++ b/app/src/main/java/com/allen/androidcustomview/widget/WaveViewByBezier.java @@ -3,7 +3,6 @@ import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.util.AttributeSet; @@ -11,14 +10,13 @@ import android.view.View; import android.view.animation.LinearInterpolator; - /** * Created by allen on 2016/12/13. *

* 波浪动画 */ -public class WaveViewByBezier extends View implements View.OnClickListener { +public class WaveViewByBezier extends View { /** * 屏幕高度 @@ -48,15 +46,29 @@ public class WaveViewByBezier extends View implements View.OnClickListener { */ private int mOffset; + /** + * 一个屏幕内显示几个周期 + */ + private int mWaveCount; + /** * 振幅 */ - private int mWaveAmplitude = 50; + private int mWaveAmplitude; - private ValueAnimator valueAnimator; + /** + * 波形的颜色 + */ + private int waveColor = 0xaaFF7E37; + private static final int SIN = 0; + private static final int COS = 1; + private static final int DEFAULT = SIN; + + private int waveType = DEFAULT; + + private ValueAnimator valueAnimator; - private boolean isStart = false; public WaveViewByBezier(Context context) { this(context, null); @@ -64,21 +76,15 @@ public WaveViewByBezier(Context context) { } public WaveViewByBezier(Context context, AttributeSet attrs) { - this(context, attrs, 0); - - } - - public WaveViewByBezier(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); + super(context, attrs); init(); } private void init() { - mWaveAmplitude = dp2px(15); + mWaveAmplitude = dp2px(10); + mWaveLength = dp2px(500); initPaint(); - setOnClickListener(this); - } @@ -89,168 +95,150 @@ private void initPaint() { mWavePath = new Path(); mWavePaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mWavePaint.setColor(Color.GRAY); + mWavePaint.setColor(waveColor); mWavePaint.setStyle(Paint.Style.FILL_AND_STROKE); mWavePaint.setAntiAlias(true); } + @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int widthMode = MeasureSpec.getMode(widthMeasureSpec); - int width = MeasureSpec.getSize(widthMeasureSpec); + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); - int heightMode = MeasureSpec.getMode(heightMeasureSpec); - int height = MeasureSpec.getSize(heightMeasureSpec); + mScreenHeight = h; + mScreenWidth = w; - setMeasuredDimension(measureWidth(widthMode, width), measureHeight(heightMode, height)); + /** + * 加上1.5是为了保证至少有两个波形(屏幕外边一个完整的波形,屏幕里边一个完整的波形) + */ + mWaveCount = (int) Math.round(mScreenWidth / mWaveLength + 1.5); } - /** - * 测量宽度 - * - * @param mode - * @param width - * @return - */ - private int measureWidth(int mode, int width) { - switch (mode) { - case MeasureSpec.UNSPECIFIED: - case MeasureSpec.AT_MOST: + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + switch (waveType) { + case SIN: + drawSinPath(canvas); break; - case MeasureSpec.EXACTLY: - mScreenWidth = width; + case COS: + drawCosPath(canvas); break; } - return mScreenWidth; + } /** - * 测量高度 + * sin函数图像的波形 * - * @param mode - * @param height - * @return + * @param canvas */ - private int measureHeight(int mode, int height) { - switch (mode) { - case MeasureSpec.UNSPECIFIED: - case MeasureSpec.AT_MOST: - break; - case MeasureSpec.EXACTLY: - mScreenHeight = height; - break; + private void drawSinPath(Canvas canvas) { + mWavePath.reset(); + + mWavePath.moveTo(-mWaveLength + mOffset, mWaveAmplitude); + + // TODO: 2017/6/19 //相信很多人会疑惑为什么控制点的纵坐标是以下值,是根据公式计算出来的,具体计算方法情况文章内容 + + for (int i = 0; i < mWaveCount; i++) { + + //第一个控制点的坐标为(-mWaveLength * 3 / 4,-mWaveAmplitude) + mWavePath.quadTo(-mWaveLength * 3 / 4 + mOffset + i * mWaveLength, + -mWaveAmplitude, + -mWaveLength / 2 + mOffset + i * mWaveLength, + mWaveAmplitude); + + //第二个控制点的坐标为(-mWaveLength / 4,3 * mWaveAmplitude) + mWavePath.quadTo(-mWaveLength / 4 + mOffset + i * mWaveLength, + 3 * mWaveAmplitude, + mOffset + i * mWaveLength, + mWaveAmplitude); } - return mScreenHeight; - } + mWavePath.lineTo(getWidth(), getHeight()); + mWavePath.lineTo(0, getHeight()); + mWavePath.close(); - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); + canvas.drawPath(mWavePath, mWavePaint); + } - mWaveLength = mScreenWidth; + /** + * cos函数图像的波形 + * + * @param canvas + */ + private void drawCosPath(Canvas canvas) { mWavePath.reset(); -// mWavePath.moveTo(-mWaveLength + mOffset, mCenterY); -// -// for (int i = 0; i < mWaveCount; i++) { -// mWavePath.quadTo(-mWaveLength * 3 / 4 + i * mWaveLength + mOffset, mCenterY + 60, -mWaveLength / 2 + i * mWaveLength + mOffset, mCenterY); -// mWavePath.quadTo(-mWaveLength / 4 + i * mWaveLength + mOffset, mCenterY - 60, i * mWaveLength + mOffset, mCenterY); -// } -// mWavePath.lineTo(mScreenWidth, mScreenHeight); -// mWavePath.lineTo(0, mScreenHeight); -// mWavePath.close(); - mWavePath.moveTo(-mWaveLength + mOffset, mWaveAmplitude); - mWavePath.quadTo(-mWaveLength * 3 / 4 + mOffset, -mWaveAmplitude, -mWaveLength / 2 + mOffset, mWaveAmplitude); - - mWavePath.quadTo(-mWaveLength / 4 + mOffset, 3 * mWaveAmplitude, 0 + mOffset, mWaveAmplitude); + for (int i = 0; i < mWaveCount; i++) { - mWavePath.quadTo(mWaveLength / 4 + mOffset, -mWaveAmplitude, mWaveLength / 2 + mOffset, mWaveAmplitude); + //第一个控制点的坐标为(-mWaveLength * 3 / 4,3 * mWaveAmplitude + mWavePath.quadTo(-mWaveLength * 3 / 4 + mOffset + i * mWaveLength, + 3 * mWaveAmplitude, + -mWaveLength / 2 + mOffset + i * mWaveLength, + mWaveAmplitude); - mWavePath.quadTo(mWaveLength * 3 / 4 + mOffset, 3 * mWaveAmplitude, mWaveLength + mOffset, mWaveAmplitude); + //第二个控制点的坐标为(-mWaveLength / 4,-mWaveAmplitude) + mWavePath.quadTo(-mWaveLength / 4 + mOffset + i * mWaveLength, + -mWaveAmplitude, + mOffset + i * mWaveLength, + mWaveAmplitude); + } mWavePath.lineTo(getWidth(), getHeight()); mWavePath.lineTo(0, getHeight()); mWavePath.close(); - -// mWavePath.moveTo(mOffset, mCenterY); -// -// for (int i = 0; i < mWaveCount; i++) { -// mWavePath.quadTo(mWaveLength /4+ mOffset, mCenterY + 60, mWaveLength / 2 + mOffset, mCenterY); -// mWavePath.quadTo(mWaveLength *3/ 4 + mOffset, mCenterY - 60, + mOffset, mCenterY); -// } -// mWavePath.lineTo(mScreenWidth, mScreenHeight); -// mWavePath.lineTo(0, mScreenHeight); -// mWavePath.close(); canvas.drawPath(mWavePath, mWavePaint); } + /** + * 波形动画 + */ private void initAnimation() { - valueAnimator = ValueAnimator.ofInt(0, mScreenWidth); - valueAnimator.setDuration(3000); + valueAnimator = ValueAnimator.ofInt(0, mWaveLength); + valueAnimator.setDuration(2000); + valueAnimator.setStartDelay(300); valueAnimator.setRepeatCount(ValueAnimator.INFINITE); valueAnimator.setInterpolator(new LinearInterpolator()); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mOffset = (int) animation.getAnimatedValue(); - postInvalidate(); + invalidate(); } }); + valueAnimator.start(); } public void startAnimation() { - isStart = true; - if (valueAnimator != null) { - - valueAnimator.start(); - } + initAnimation(); } public void stopAnimation() { - isStart = false; if (valueAnimator != null) { valueAnimator.cancel(); - this.clearAnimation(); } } public void pauseAnimation() { - isStart = false; if (valueAnimator != null) { valueAnimator.pause(); } } public void resumeAnimation() { - isStart = true; if (valueAnimator != null) { valueAnimator.resume(); } } - - @Override - public void onClick(View v) { - initAnimation(); - if (valueAnimator != null) { - if (isStart) { - isStart = false; - valueAnimator.pause(); - } else { - isStart = true; - valueAnimator.start(); - } - } - - } - - /** * dp 2 px * From d01c32f167c1f4faf5e1ae98f0b5032638fc7f46 Mon Sep 17 00:00:00 2001 From: xiaoyao Date: Tue, 20 Jun 2017 18:00:40 +0800 Subject: [PATCH 03/50] =?UTF-8?q?=E6=B0=B4=E6=B3=A2=E5=8A=A8=E7=94=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 + .../activity/BubbleViewActivity.java | 4 - .../activity/WaveByBezierActivity.java | 1 + .../widget/WaveAnimatorView.java | 303 ------------------ .../widget/WaveViewBySinCos.java | 269 +++++++++++----- .../main/res/layout/activity_bubble_view.xml | 22 -- .../res/layout/activity_wave_by_bezier.xml | 12 +- .../res/layout/activity_wave_by_sin_cos.xml | 70 +++- app/src/main/res/layout/wave_layout.xml | 49 --- app/src/main/res/values/attrs.xml | 41 ++- app/src/main/res/values/colors.xml | 2 +- 11 files changed, 294 insertions(+), 482 deletions(-) delete mode 100644 app/src/main/java/com/allen/androidcustomview/widget/WaveAnimatorView.java delete mode 100644 app/src/main/res/layout/wave_layout.xml diff --git a/README.md b/README.md index 11dcc27..cffa0d5 100644 --- a/README.md +++ b/README.md @@ -11,3 +11,6 @@ # [**TextView实现打印机逐个显示的效果**](http://www.jianshu.com/p/4d987769785c) ![FadeInTextView.gif](http://upload-images.jianshu.io/upload_images/2057501-a7a751b456b25494.gif?imageMogr2/auto-orient/strip) + +# [**T水波动画效果多种实现方式详解**](http://www.jianshu.com/p/0cd1c1d47f4a) +![wave.gif](http://upload-images.jianshu.io/upload_images/2057501-43358432099e1e71.gif?imageMogr2/auto-orient/strip) diff --git a/app/src/main/java/com/allen/androidcustomview/activity/BubbleViewActivity.java b/app/src/main/java/com/allen/androidcustomview/activity/BubbleViewActivity.java index dd50dee..f0ddf12 100644 --- a/app/src/main/java/com/allen/androidcustomview/activity/BubbleViewActivity.java +++ b/app/src/main/java/com/allen/androidcustomview/activity/BubbleViewActivity.java @@ -28,12 +28,10 @@ public class BubbleViewActivity extends AppCompatActivity { private List circleBeanList = new ArrayList<>(); - private WaveViewByBezier waveView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_bubble_view); - waveView = (WaveViewByBezier) findViewById(R.id.wave); hxbIv = (ImageView) findViewById(R.id.hxb_iv); hxbTv = (TextView) findViewById(R.id.center_tv); bezierView = (BubbleView) findViewById(R.id.circle_view); @@ -149,13 +147,11 @@ public int getStatusBarHeight() { protected void onResume() { super.onResume(); Log.d(TAG, "onResume: "); - waveView.startAnimation(); } @Override protected void onPause() { super.onPause(); Log.d(TAG, "onPause: "); - waveView.stopAnimation(); } } diff --git a/app/src/main/java/com/allen/androidcustomview/activity/WaveByBezierActivity.java b/app/src/main/java/com/allen/androidcustomview/activity/WaveByBezierActivity.java index 47b9476..3cd0a26 100644 --- a/app/src/main/java/com/allen/androidcustomview/activity/WaveByBezierActivity.java +++ b/app/src/main/java/com/allen/androidcustomview/activity/WaveByBezierActivity.java @@ -16,6 +16,7 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_wave_by_bezier); waveViewByBezier = (WaveViewByBezier) findViewById(R.id.wave_bezier); + waveViewByBezier.startAnimation(); } diff --git a/app/src/main/java/com/allen/androidcustomview/widget/WaveAnimatorView.java b/app/src/main/java/com/allen/androidcustomview/widget/WaveAnimatorView.java deleted file mode 100644 index 10ae3ea..0000000 --- a/app/src/main/java/com/allen/androidcustomview/widget/WaveAnimatorView.java +++ /dev/null @@ -1,303 +0,0 @@ -package com.allen.androidcustomview.widget; - -import android.animation.ValueAnimator; -import android.content.Context; -import android.content.res.TypedArray; -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.util.Log; -import android.view.View; -import android.view.animation.LinearInterpolator; - -import com.allen.androidcustomview.R; - -import static android.content.ContentValues.TAG; - -/** - * Created by allen on 2016/12/13. - *

- * 波浪动画 - */ - -public class WaveAnimatorView extends View { - - /** - * 上下文对象 - */ - private Context mContext; - /** - * 屏幕高度 - */ - private int mScreenHeight; - /** - * 屏幕宽度 - */ - private int mScreenWidth; - - /** - * 波浪的画笔 - */ - private Paint mWavePaint; - /** - * 一个周期波浪的长度 - */ - private int mWaveLength; - /** - * 一个周期波浪的默认长度 - */ - private int mdefaultWaveLength = 500; - - /** - * 屏幕宽度内波浪显示几个周期 - */ - private int mWaveCount; - - /** - * 波纹的中间轴(基准线) - */ - private int mCenterY; - /** - * 波浪的路径 - */ - Path mWavePath; - - /** - * 平移偏移量 - */ - private int mOffset; - - /** - * 渐变色shader - */ - private Shader mShader; - /** - * 渐变色shade配置参数 - */ - private int colors[] = new int[2]; - - /** - * 渐变波开始的颜色 - */ - private int mWaveStartColor; - - /** - * 渐变波结束的颜色 - */ - private int mWaveEndColor; - - /** - * 颜色渐变波默认开始颜色 - */ - private int mWaveDefaultStartColor; - /** - * 颜色渐变波默认结束颜色 - */ - private int mWaveDefaultEndColor; - - /** - * 振幅 - */ - private int mWaveAmplitude; - - /** - * 水波动画延时 - */ - private int mWaveDuration; - - private boolean mWaveFillTop; - - private boolean mWaveFillBottom; - - - private static final int SIN = 0; - private static final int COS = 1; - private static final int SIN_AND_COS = 2; - - /** - * 水波类型 sin或者cos - */ - private int mWaveType; - - public WaveAnimatorView(Context context) { - this(context, null); - } - - public WaveAnimatorView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public WaveAnimatorView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - mContext = context; - mWaveDefaultStartColor = getResources().getColor(R.color.wave_start); - mWaveDefaultEndColor = getResources().getColor(R.color.wave_end); - getAttr(attrs); - init(); - } - - private void getAttr(AttributeSet attrs) { - TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.WaveAnimatorView); - - mWaveLength = typedArray.getDimensionPixelOffset(R.styleable.WaveAnimatorView_waveLength, dip2px(mContext, mdefaultWaveLength)); - mWaveDuration = typedArray.getInt(R.styleable.WaveAnimatorView_waveDuration, 2000); - mWaveType = typedArray.getInt(R.styleable.WaveAnimatorView_waveType, SIN); - mWaveAmplitude = typedArray.getDimensionPixelOffset(R.styleable.WaveAnimatorView_waveAmplitude, dip2px(mContext, 20)); - mWaveStartColor = typedArray.getColor(R.styleable.WaveAnimatorView_waveStartColor, mWaveDefaultStartColor); - mWaveEndColor = typedArray.getColor(R.styleable.WaveAnimatorView_waveEndColor, mWaveDefaultEndColor); - - mWaveFillTop = typedArray.getBoolean(R.styleable.WaveAnimatorView_waveFillTop, true); - mWaveFillBottom = typedArray.getBoolean(R.styleable.WaveAnimatorView_waveFillBottom, false); - - typedArray.recycle(); - } - - private void init() { - initPaint(); - Animation(); - } - - - /** - * 初始化画笔 - */ - private void initPaint() { - - mWavePath = new Path(); - - colors[0] = mWaveStartColor; - colors[1] = mWaveEndColor; - - mWavePaint = new Paint(); - - mWavePaint.setStrokeWidth(1); - mWavePaint.setStyle(Paint.Style.FILL_AND_STROKE); - mWavePaint.setAntiAlias(true); - - } - - - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - super.onSizeChanged(w, h, oldw, oldh); - mScreenHeight = h; - mScreenWidth = w; - - mWaveCount = (int) Math.round(mScreenWidth / mWaveLength + 1.5); - Log.d(TAG, "onSizeChanged: ======="+mWaveCount); - mCenterY = mScreenHeight*3/4; - - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - - mShader = new LinearGradient(0, 0, getWidth(), 0, colors, null, Shader.TileMode.CLAMP); - mWavePaint.setShader(mShader); - - switch (mWaveType){ - case SIN: - drawWaveSin(canvas); - break; - case COS: - drawWaveCos(canvas); - break; - case SIN_AND_COS: - drawWaveSin(canvas); - drawWaveCos(canvas); - } - } - - /** - * 类似cos函数的波形 - * @param canvas 画笔 - */ - private void drawWaveCos(Canvas canvas) { - mWavePath.reset(); - mWavePath.moveTo(-mWaveLength + mOffset, mCenterY); - - for (int i = 0; i < mWaveCount; i++) { - mWavePath.quadTo((-mWaveLength * 3 / 4) + (i * mWaveLength) + mOffset, mCenterY + mWaveAmplitude, (-mWaveLength / 2) + (i * mWaveLength) + mOffset, mCenterY); - mWavePath.quadTo((-mWaveLength / 4) + (i * mWaveLength) + mOffset, mCenterY - mWaveAmplitude, i * mWaveLength + mOffset, mCenterY); - } - if (mWaveFillTop) { - fillTop(); - } - - canvas.drawPath(mWavePath, mWavePaint); - } - - /** - * 类似sin函数的波形 - * @param canvas 画笔 - */ - private void drawWaveSin(Canvas canvas) { - mWavePath.reset(); - mWavePath.moveTo(-mWaveLength + mOffset, mCenterY); - - for (int i = 0; i < mWaveCount; i++) { - mWavePath.quadTo((-mWaveLength * 3 / 4) + (i * mWaveLength) + mOffset, mCenterY - mWaveAmplitude, (-mWaveLength / 2) + (i * mWaveLength) + mOffset, mCenterY); - mWavePath.quadTo((-mWaveLength / 4) + (i * mWaveLength) + mOffset, mCenterY + mWaveAmplitude, i * mWaveLength + mOffset, mCenterY); - } - if (mWaveFillTop) { - fillTop(); - } - - canvas.drawPath(mWavePath, mWavePaint); - } - - - /** - * 填充波浪上面部分 - */ - private void fillTop() { - mWavePath.lineTo(mScreenWidth, 0); - mWavePath.lineTo(0, 0); - mWavePath.close(); - - } - - /** - * 填充波浪下面部分 - */ - private void fillBottom() { - //填充矩形 - mWavePath.lineTo(mScreenWidth, mScreenHeight); - mWavePath.lineTo(0, mScreenHeight); - mWavePath.close(); - - } - - private void Animation() { - - ValueAnimator waveAnimator = ValueAnimator.ofInt(0, mWaveLength); - waveAnimator.setDuration(mWaveDuration); - waveAnimator.setInterpolator(new LinearInterpolator()); - waveAnimator.setRepeatCount(ValueAnimator.INFINITE); - waveAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - mOffset = (int) animation.getAnimatedValue(); - invalidate(); - } - }); - waveAnimator.start(); - } - - /** - * 单位转换工具类 - * - * @param context 上下文对象 - * @param dipValue 值 - * @return 返回值 - */ - public int dip2px(Context context, float dipValue) { - final float scale = context.getResources().getDisplayMetrics().density; - return (int) (dipValue * scale + 0.5f); - } - -} diff --git a/app/src/main/java/com/allen/androidcustomview/widget/WaveViewBySinCos.java b/app/src/main/java/com/allen/androidcustomview/widget/WaveViewBySinCos.java index 6d72633..8e44c0b 100644 --- a/app/src/main/java/com/allen/androidcustomview/widget/WaveViewBySinCos.java +++ b/app/src/main/java/com/allen/androidcustomview/widget/WaveViewBySinCos.java @@ -2,24 +2,22 @@ import android.animation.ValueAnimator; import android.content.Context; +import android.content.res.TypedArray; 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.util.TypedValue; import android.view.View; import android.view.animation.LinearInterpolator; import com.allen.androidcustomview.R; -import java.util.ArrayList; -import java.util.List; /** - * Created by allen on 2016/12/16. + * Created by allen on 2017/6/20. * /** - * y=Asin(ωx+φ)+k + * y=A*sin(ωx+φ)+k *

* A—振幅越大,波形在y轴上最大与最小值的差值越大 * ω—角速度, 控制正弦周期(单位角度内震动的次数) @@ -29,141 +27,248 @@ public class WaveViewBySinCos extends View { + private Context mContext; + /** + * 振幅 + */ + private int A; + /** + * 偏距 + */ + private int K; /** - * 屏幕高度 + * 波形的颜色 */ - private int mScreenHeight; + private int waveColor = 0xaaFF7E37; + /** - * 屏幕宽度 + * 初相 */ - private int mScreenWidth; + private float φ; /** - * 波纹的中间轴(基准线) + * 波形移动的速度 */ - private int mCenterY; + private float waveSpeed = 3f; - private int mWaveLength = 1000; + /** + * 角速度 + */ + private double ω; - private int mWaveCount; + /** + * 开始位置相差多少个周期 + */ + private double startPeriod; - private int mOffset; + /** + * 是否直接开启波形 + */ + private boolean waveStart; - private int mWaveNum = 4; + private Path path; + private Paint paint; - private int A = 30; - private int K = 100; + private static final int SIN = 0; + private static final int COS = 1; - private List mPaths = new ArrayList<>(); - private List mPaints = new ArrayList<>(); + private int waveType; - private float φ; - double ω; + private static final int TOP = 0; + private static final int BOTTOM = 1; - /** - * 渐变色shader - */ - private Shader mShader1, mShader2; - /** - * 渐变色shade配置参数 - */ - private int colors1[] = new int[2]; - private int colors2[] = new int[2]; + private int waveFillType; + private ValueAnimator valueAnimator; public WaveViewBySinCos(Context context, AttributeSet attrs) { super(context, attrs); - initPath(); + mContext = context; + getAttr(attrs); + K = A; initPaint(); - initAnimation(); } - private void initPath() { - for (int i = 0; i < mWaveNum; i++) { - Path path = new Path(); - mPaths.add(path); - } - } - - private void initPaint() { + private void getAttr(AttributeSet attrs) { + TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.WaveView); - colors1[0] = getResources().getColor(R.color.wave_start1); - colors1[1] = getResources().getColor(R.color.wave_end1); + waveType = typedArray.getInt(R.styleable.WaveView_waveType, SIN); + waveFillType = typedArray.getInt(R.styleable.WaveView_waveFillType, BOTTOM); + A = typedArray.getDimensionPixelOffset(R.styleable.WaveView_waveAmplitude, dp2px(10)); + waveColor = typedArray.getColor(R.styleable.WaveView_waveColor, waveColor); + waveSpeed = typedArray.getFloat(R.styleable.WaveView_waveSpeed, waveSpeed); + startPeriod = typedArray.getFloat(R.styleable.WaveView_waveStartPeriod, 0); + waveStart = typedArray.getBoolean(R.styleable.WaveView_waveStart, false); - colors2[0] = getResources().getColor(R.color.wave_start2); - colors2[1] = getResources().getColor(R.color.wave_end2); + typedArray.recycle(); + } - for (int i = 0; i < mWaveNum; i++) { - Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); - paint.setAntiAlias(true); - paint.setStyle(Paint.Style.FILL_AND_STROKE); - mPaints.add(paint); - } + private void initPaint() { + path = new Path(); + paint = new Paint(Paint.ANTI_ALIAS_FLAG); + paint.setAntiAlias(true); + paint.setStyle(Paint.Style.FILL_AND_STROKE); + paint.setColor(waveColor); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); - mScreenHeight = h; - mScreenWidth = w; - - mWaveCount = (int) Math.round(mScreenWidth / mWaveLength + 1.5); - ω = 2 * Math.PI / getWidth(); - } @Override protected void onDraw(Canvas canvas) { - if (mShader1==null){ - mShader1 = new LinearGradient(0, 0, getWidth(), 0, colors1, null, Shader.TileMode.CLAMP); + + switch (waveType) { + case SIN: + drawSin(canvas); + break; + case COS: + drawCos(canvas); + break; } - if (mShader2==null){ - mShader2 = new LinearGradient(0, 0, getWidth(), 0, colors2, null, Shader.TileMode.CLAMP); + + } + + + /** + * 根据cos函数绘制波形 + * + * @param canvas + */ + private void drawCos(Canvas canvas) { + + switch (waveFillType) { + case TOP: + fillTop(canvas); + break; + case BOTTOM: + fillBottom(canvas); + break; + } + } + + /** + * 根据sin函数绘制波形 + * + * @param canvas + */ + private void drawSin(Canvas canvas) { + + switch (waveFillType) { + case TOP: + fillTop(canvas); + break; + case BOTTOM: + fillBottom(canvas); + break; } -// φ -= 0.1f; - φ -= 0.03f; + + } + + /** + * 填充波浪上面部分 + */ + private void fillTop(Canvas canvas) { + + φ -= waveSpeed / 100; float y; - for (int i = 0; i < mWaveNum; i++) { + path.reset(); + path.moveTo(0, getHeight()); - mPaths.get(i).reset(); + for (float x = 0; x <= getWidth(); x += 20) { + y = (float) (A * Math.sin(ω * x + φ + Math.PI * startPeriod) + K); + path.lineTo(x, getHeight() - y); + } - mPaths.get(i).moveTo(0, 0); + path.lineTo(getWidth(), 0); + path.lineTo(0, 0); + path.close(); - for (float x = 0; x <= getWidth(); x += 20) { - if (i % 2 == 0) { - y = (float) (A* Math.cos(ω * x + φ) + K + 20*(i+1)); - } else { - y = (float) ((A+(2*(i+1))) * Math.sin(ω * x + φ) + K); - } - mPaths.get(i).lineTo(x+mOffset, y); - } + canvas.drawPath(path, paint); + + } + + /** + * 填充波浪下面部分 + */ + private void fillBottom(Canvas canvas) { + + φ -= waveSpeed / 100; + float y; + + path.reset(); + path.moveTo(0, 0); - mPaints.get(i).setShader(mShader1); - mPaths.get(i).lineTo(getWidth(), 0); - mPaths.get(i).close(); - canvas.drawPath(mPaths.get(i), mPaints.get(i)); + for (float x = 0; x <= getWidth(); x += 20) { + y = (float) (A * Math.sin(ω * x + φ + Math.PI * startPeriod) + K); + path.lineTo(x, y); } + //填充矩形 + path.lineTo(getWidth(), getHeight()); + path.lineTo(0, getHeight()); + path.close(); + + canvas.drawPath(path, paint); + } private void initAnimation() { - ValueAnimator valueAnimator = ValueAnimator.ofInt(0, getWidth()); + valueAnimator = ValueAnimator.ofInt(0, getWidth()); valueAnimator.setDuration(1000); valueAnimator.setRepeatCount(ValueAnimator.INFINITE); valueAnimator.setInterpolator(new LinearInterpolator()); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { - mOffset = (int) animation.getAnimatedValue(); - postInvalidate(); + /** + * 刷新页面调取onDraw方法,通过变更φ 达到移动效果 + */ + invalidate(); } }); - valueAnimator.start(); + if (waveStart) { + valueAnimator.start(); + } + } + + public void startAnimation() { + if (valueAnimator != null) { + valueAnimator.start(); + } + } + + public void stopAnimation() { + if (valueAnimator != null) { + valueAnimator.cancel(); + } } + public void pauseAnimation() { + if (valueAnimator != null) { + valueAnimator.pause(); + } + } + + public void resumeAnimation() { + if (valueAnimator != null) { + valueAnimator.resume(); + } + } + + /** + * dp 2 px + * + * @param dpVal + */ + protected int dp2px(int dpVal) { + return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, + dpVal, getResources().getDisplayMetrics()); + } } diff --git a/app/src/main/res/layout/activity_bubble_view.xml b/app/src/main/res/layout/activity_bubble_view.xml index adfdbc0..1ab8904 100644 --- a/app/src/main/res/layout/activity_bubble_view.xml +++ b/app/src/main/res/layout/activity_bubble_view.xml @@ -5,28 +5,6 @@ android:layout_height="match_parent" android:orientation="vertical"> - - - - - - - - - - - -