package org.aisen.weibo.sina.ui.widget; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Path.Direction; import android.graphics.Rect; import android.graphics.Region.Op; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.widget.ProgressBar; import org.aisen.android.common.context.GlobalContext; import org.aisen.android.common.utils.Utils; import org.aisen.weibo.sina.R; /** * Created by kai.wang on 6/17/14. */ public class WaveView extends View { private Path aboveWavePath = new Path(); private Path blowWavePath = new Path(); private Paint aboveWavePaint = new Paint(); private Paint blowWavePaint = new Paint(); private final int default_above_wave_alpha = 100; private final int default_blow_wave_alpha = 70; private final int default_progress = 0; private final int text_default_size; private int waveToTop; private int aboveWaveColor; private int blowWaveColor; private int mProgress; /** * wave length */ private final int x_zoom = 60; /** * wave crest */ private final int y_zoom = 10;// 控制上下起伏的高度 /** * offset of X */ private final float offset = 0.5f; private final float max_right = x_zoom * offset; // wave animation private float aboveOffset = 0.0f; private float blowOffset = 4.0f; /** * offset of Y */ // private float animOffset = 0.05f;// 控制上下起伏的频率 private float animOffset = 0.08f;// 控制上下起伏的频率 // refresh thread private RefreshProgressRunnable mRefreshProgressRunnable; private boolean isStop = false; private Rect r; private float density; public WaveView(Context context, AttributeSet attrs) { this(context, attrs, R.attr.waveViewStyle); // 关闭硬件加速 setLayerType(View.LAYER_TYPE_SOFTWARE, null); } public WaveView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); density = getDensity(context); aboveWaveColor = context.getResources().getColor(R.color.wave); blowWaveColor = context.getResources().getColor(R.color.wave); text_default_size = Utils.dip2px(GlobalContext.getInstance(), 14); setProgress(default_progress); initializePainters(); // 关闭硬件加速 setLayerType(View.LAYER_TYPE_SOFTWARE, null); } Paint paintR, paintT, paintP; Path path1, path2; int textBaseLine; public boolean needWave() { if (mProgress <= 0 || mProgress > 100) { return false; } return true; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int width = getWidth(); int height = getHeight(); // 切割画布 // 裁剪成圆区域 if (path1 == null) { path1 = new Path(); path2 = new Path(); path1.reset(); path1.addCircle(width / 2, height / 2, (float) (width / 2), Direction.CCW); path2.reset(); path2.addCircle(width / 2, height / 2, (float) (width / 2) + 3, Direction.CCW); } canvas.clipPath(path1, Op.REPLACE); if (needWave()) { canvas.drawPath(blowWavePath, blowWavePaint); canvas.drawPath(aboveWavePath, aboveWavePaint); } if (paintR == null) { // 定义画笔2 paintR = new Paint(); // 消除锯齿 paintR.setAntiAlias(true); // 设置画笔的颜色 paintR.setColor(Color.parseColor("#ffffff")); paintR.setStrokeWidth(Utils.dip2px(GlobalContext.getInstance(), 1.5f)); paintR.setStyle(Paint.Style.STROKE); paintR.setTextSize(text_default_size); } canvas.save(); canvas.clipPath(path2, Op.REPLACE); // 画一个空心圆 canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, getWidth() / 2f - paintR.getStrokeWidth() / 2, paintR); //画背景 if (pressed) { if (paintP == null) { paintP = new Paint(); paintP.setColor(Color.parseColor("#80ffffff")); } canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, getWidth() / 2f - paintR.getStrokeWidth() / 2, paintP); } if (getBackground() == null) { // 定义画笔2 if (paintT == null) { paintT = new Paint(); paintT.setAntiAlias(true); paintT.setColor(Color.parseColor("#ffffff")); paintT.setStrokeWidth(Utils.dip2px(GlobalContext.getInstance(), 1.5f)); paintT.setTextSize(text_default_size); Paint.FontMetricsInt fontMetrics = paintT.getFontMetricsInt(); textBaseLine = (getHeight() - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top; } canvas.drawText(mProgress + "%", width / 2 - paintT.measureText(mProgress + "%") / 2, textBaseLine, paintT); } } boolean pressed = false; @Override public boolean dispatchTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: pressed = true; invalidate(); break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: pressed = false; invalidate(); break; default: break; } return super.dispatchTouchEvent(event); } public static float getDensity(Context context) { return context.getResources().getDisplayMetrics().density; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(measure(widthMeasureSpec, true), measure(heightMeasureSpec, false)); } private int measure(int measureSpec, boolean isWidth) { int result; int mode = MeasureSpec.getMode(measureSpec); int size = MeasureSpec.getSize(measureSpec); int padding = isWidth ? getPaddingLeft() + getPaddingRight() : getPaddingTop() + getPaddingBottom(); if (mode == MeasureSpec.EXACTLY) { result = size; } else { result = isWidth ? getSuggestedMinimumWidth() : getSuggestedMinimumHeight(); result += padding; if (mode == MeasureSpec.AT_MOST) { if (isWidth) { result = Math.max(result, size); } else { result = Math.min(result, size); } } } return result; } private void initializePainters() { aboveWavePaint.setColor(aboveWaveColor); aboveWavePaint.setAlpha(default_above_wave_alpha); aboveWavePaint.setStyle(Paint.Style.FILL); aboveWavePaint.setAntiAlias(true); blowWavePaint.setColor(blowWaveColor); blowWavePaint.setAlpha(default_blow_wave_alpha); blowWavePaint.setStyle(Paint.Style.FILL); blowWavePaint.setAntiAlias(true); } /** * calculate wave track */ private void calculatePath() { aboveWavePath.reset(); blowWavePath.reset(); getWaveOffset(); aboveWavePath.moveTo(0, getHeight()); for (float i = 0; x_zoom * i <= getRight() + max_right; i += offset) { aboveWavePath.lineTo((x_zoom * i), (float) (y_zoom * Math.cos(i + aboveOffset)) + waveToTop); } aboveWavePath.lineTo(getRight(), getHeight()); blowWavePath.moveTo(0, getHeight()); for (float i = 0; x_zoom * i <= getRight() + max_right; i += offset) { blowWavePath.lineTo((x_zoom * i), (float) (y_zoom * Math.cos(i + blowOffset)) + waveToTop); } blowWavePath.lineTo(getRight(), getHeight()); } public void setProgress(int progress) { if (progress > 100) progress = 100; // else if (progress < 10) // progress = 10; this.mProgress = progress; } public void setProgress0() { this.mProgress = 0; } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); // LLog.e("waveview onAttachedToWindow()"); mRefreshProgressRunnable = new RefreshProgressRunnable(); post(mRefreshProgressRunnable); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); // LLog.e("waveview onDetachedFromWindow()"); removeCallbacks(mRefreshProgressRunnable); isStop = true; } private void getWaveOffset() { if (blowOffset > Float.MAX_VALUE - 100) { blowOffset = 0; } else { blowOffset += animOffset; } if (aboveOffset > Float.MAX_VALUE - 100) { aboveOffset = 0; } else { aboveOffset += animOffset; } } @Override public Parcelable onSaveInstanceState() { // Force our ancestor class to save its state Parcelable superState = super.onSaveInstanceState(); SavedState ss = new SavedState(superState); ss.progress = mProgress; return ss; } @Override public void onRestoreInstanceState(Parcelable state) { SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); setProgress(ss.progress); } private class RefreshProgressRunnable implements Runnable { public void run() { if (isStop) return; synchronized (WaveView.this) { waveToTop = (int) (getHeight() * (1f - mProgress / 100f)); if (needWave()) { calculatePath(); invalidate(); } postDelayed(this, 32); } } } private static class SavedState extends BaseSavedState { int progress; /** * Constructor called from {@link ProgressBar#onSaveInstanceState()} */ SavedState(Parcelable superState) { super(superState); } /** * Constructor called from {@link #CREATOR} */ private SavedState(Parcel in) { super(in); progress = in.readInt(); } @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); out.writeInt(progress); } public static final Creator<SavedState> CREATOR = new Creator<SavedState>() { public SavedState createFromParcel(Parcel in) { return new SavedState(in); } public SavedState[] newArray(int size) { return new SavedState[size]; } }; } private boolean running = false; // addbywangdan 2015-10-25 新增一个字段,标志当前的WaveView正在Progress // 根据原来已有代码逻辑添加 @Override public void setBackgroundResource(int resid) { super.setBackgroundResource(resid); running = false; } @Override public void setBackground(Drawable background) { super.setBackground(background); running = true; } public boolean isRunning() { return running; } }