attr属性声明文件:
<declare-styleable name="WaveView">
<attr name="wave_text_color" format="color|reference"></attr>
<attr name="wave_text_size" format="dimension"></attr>
<attr name="wave_width" format="dimension"></attr>
<attr name="wave_high" format="dimension"></attr>
<attr name="wave_color" format="color|reference"></attr>
</declare-styleable>
自定WaveView类:
package com.baiboss.test.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
import com.baiboss.test.R;
/**
* Created by zq on 2018/1/22.
*/
public class WaveView extends View {
private int mWidth;
private int mHeight;
private Path mPath;
private Paint mPathPaint;
private Paint mTextPaint;
private static final int WAVE_SPEED = 3;//移动速度
private int mWaveHigh = 30;// 振幅
private int mWaveWidth = 100;// 波长
private float maxProgress = 1000;
private float currentProgress = 0;
private float currentY;
private int distance;
private Bitmap backgroundBitmap;
public WaveView(Context context) {
this(context, null, 0);
}
public WaveView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public WaveView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.WaveView);
int indexCount = typedArray.getIndexCount();
for (int i = 0; i < indexCount; i++) {
int index = typedArray.getIndex(i);
switch (index) {
case R.styleable.WaveView_wave_text_color:
mTextPaint.setColor(typedArray.getColor(index, Color.BLACK));
break;
case R.styleable.WaveView_wave_text_size:
mTextPaint.setTextSize(typedArray.getDimension(index, 40));
break;
case R.styleable.WaveView_wave_color:
mPathPaint.setColor(typedArray.getColor(index, Color.GREEN));
break;
case R.styleable.WaveView_wave_high:
mWaveHigh = typedArray.getDimensionPixelSize(index, 30);
break;
case R.styleable.WaveView_wave_width:
mWaveWidth = typedArray.getDimensionPixelSize(index, 100);
break;
}
}
typedArray.recycle();
}
public void setMaxProgress(int maxProgress) {
this.maxProgress = maxProgress;
}
public void setCurrentProgress(int currentProgress) {
this.currentProgress = currentProgress;
if (currentProgress <= maxProgress) {
invalidate();
}
}
private void init() {
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPath = new Path();
mPathPaint = new Paint();
mPathPaint.setAntiAlias(true);
mPathPaint.setStyle(Paint.Style.FILL);
backgroundBitmap = getBackgroundBitmap();
}
private Bitmap getBackgroundBitmap() {
Drawable background = getBackground();
if (background != null) {
if (background instanceof BitmapDrawable) {
return ((BitmapDrawable) background).getBitmap();
}
int intrinsicWidth = background.getIntrinsicWidth();
int intrinsicHeight = background.getIntrinsicHeight();
Bitmap bitmap = Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
background.setBounds(0, 0, intrinsicWidth, intrinsicHeight);
background.draw(canvas);
return bitmap;
}
return null;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = MeasureSpec.getSize(widthMeasureSpec);
mHeight = MeasureSpec.getSize(heightMeasureSpec);
}
private Bitmap createBitmap() {
Bitmap bitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
/**
* currentMidY与currentY的关系:
* currentMidY变化区间为[mHeight,0]
* currentY变化区间为[mHeight + mWaveHigh / 2,-mWaveHigh / 2]
*/
float currentMidY = mHeight * (maxProgress - currentProgress) / maxProgress;
currentY = currentMidY + currentMidY * mWaveHigh / mHeight - mWaveHigh / 2;
/**
* 当mWidth不是波长的整数倍时,需要+1
* 当波纹平移时,会额外需要一个波长作为过渡,再+1
*/
int waveNum = mWidth / mWaveWidth + 2;
mPath.reset();
mPath.moveTo(0 - distance, currentY);
int num = 0;
for (int i = 0; i < waveNum; i++) {
mPath.quadTo(mWaveWidth * (num + 1) / 4 - distance, currentY - mWaveHigh, mWaveWidth * (num + 2) / 4 - distance, currentY);
mPath.quadTo(mWaveWidth * (num + 3) / 4 - distance, currentY + mWaveHigh, mWaveWidth * (num + 4) / 4 - distance, currentY);
num += 4;
}
distance += WAVE_SPEED;
distance = distance % mWaveWidth;
mPath.lineTo(mWidth, mHeight);
mPath.lineTo(0, mHeight);
mPath.close();
canvas.drawPath(mPath, mPathPaint);
if (backgroundBitmap != null) {
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
Bitmap scale = Bitmap.createScaledBitmap(backgroundBitmap, this.mWidth, mHeight, false);
canvas.drawBitmap(scale, 0, 0, paint);
} else {
throw new RuntimeException("background is null");
}
String percent = currentProgress * 100f / maxProgress + "%";
float textwidth = mTextPaint.measureText(percent);
Paint.FontMetricsInt fontMetricsInt = mTextPaint.getFontMetricsInt();
float y = (mHeight - (fontMetricsInt.bottom - fontMetricsInt.top)) / 2 - fontMetricsInt.top;
canvas.drawText(percent, (mWidth - textwidth) / 2, y, mTextPaint);
return bitmap;
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(createBitmap(), 0, 0, null);
}
}
layout布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<com.baiboss.test.widget.WaveView
android:id="@+id/waveview"
android:layout_width="200dp"
android:layout_height="200dp"
android:background="@drawable/wave_bg"
app:wave_color="@color/wave_color"
app:wave_high="@dimen/wave_high"
app:wave_text_color="@color/login_text_color"
app:wave_text_size="@dimen/verification_code_size"
app:wave_width="@dimen/wave_width"/>
</LinearLayout>
MainActivity主要部分:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
handler.postDelayed(new Runnable() {
@Override
public void run() {
mWaveView.setCurrentProgress(progress++);
handler.postDelayed(this, 100);
}
}, 100);
}
效果: