• android用canvas绘制两种波纹效果


     波形效果有几种不同的呈现形式,比如从中间向四周散开的波形,也就是熟知的水涟漪;还有上下波动的曲线,像五线谱等。英文中可以称作Wave或者Ripple,所以暂且叫它们WaveView、WaveLayout、RippleView、RippleLayout,接下来开始实现这些效果。

     首先看一下Solo 火爆足球动态壁纸,

     
     下面中间的按钮就是一个波形按钮,它会不断地向四周辐射,由于是静态图,如果想体验真实效果可以另行下载。这种效果的实现思路是不断绘制圆形,当然半径也要不断变化,透明度也是一样。代码如下:
    /**
    *
    */
    package com.kince.rippleview;
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.RectF;
    import android.os.Handler;
    import android.os.Message;
    import android.util.AttributeSet;
    import android.view.View;
    /**
    * @author kince
    * @category 波纹
    * @since 2014.8.9
    * @version v1.0.0
    *
    */
    public class RippleView extends View {
         private int mScreenWidth;
         private int mScreenHeight;
         private Bitmap mRippleBitmap;
         private Paint mRipplePaint = new Paint();
         private int mBitmapWidth;
         private int mBitmapHeight;
         private boolean isStartRipple;
         private int heightPaddingTop;
         private int heightPaddingBottom;
         private int widthPaddingLeft;
         private int widthPaddingRight;
         private RectF mRect = new RectF();
         private int rippleFirstRadius = 0;
         private int rippleSecendRadius = -33;
         private int rippleThirdRadius = -66;
         private Paint textPaint = new Paint();
        private String mText="点击我吧";
                                                                             
         private Handler handler = new Handler() {
              @Override
              public void handleMessage(Message msg) {
                   // TODO Auto-generated method stub
                   super.handleMessage(msg);
                   invalidate();
                   if (isStartRipple) {
                        rippleFirstRadius++;
                        if (rippleFirstRadius > 100) {
                             rippleFirstRadius = 0;
                        }
                        rippleSecendRadius++;
                        if (rippleSecendRadius > 100) {
                             rippleSecendRadius = 0;
                        }
                        rippleThirdRadius++;
                        if (rippleThirdRadius > 100) {
                             rippleThirdRadius = 0;
                        }
                        sendEmptyMessageDelayed(0, 20);
                   }
              }
         };
         /**
         * @param context
         */
         public RippleView(Context context) {
              super(context);
              // TODO Auto-generated constructor stub
              init();
         }
         /**
         * @param context
         * @param attrs
         */
         public RippleView(Context context, AttributeSet attrs) {
              super(context, attrs);
              // TODO Auto-generated constructor stub
              init();
         }
         /**
         * @param context
         * @param attrs
         * @param defStyleAttr
         */
         public RippleView(Context context, AttributeSet attrs, int defStyleAttr) {
              super(context, attrs, defStyleAttr);
              // TODO Auto-generated constructor stub
              init();
         }
         private void init() {
              mRipplePaint.setColor(4961729);
              mRipplePaint.setAntiAlias(true);
              mRipplePaint.setStyle(Paint.Style.FILL);
              textPaint.setTextSize(26);
              textPaint.setAntiAlias(true);
              textPaint.setStyle(Paint.Style.FILL);
              textPaint.setColor(Color.WHITE);
              mRippleBitmap = BitmapFactory.decodeStream(getResources()
                        .openRawResource(R.drawable.easy3d_ic_apply));
              mBitmapWidth = mRippleBitmap.getWidth();
              mBitmapHeight = mRippleBitmap.getHeight();
         }
         @Override
         protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
              // TODO Auto-generated method stub
              super.onMeasure(widthMeasureSpec, heightMeasureSpec);
              int mh = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
              int mw = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
              if (mBitmapWidth < 2 * mBitmapHeight) {
                   mBitmapWidth = (2 * mBitmapHeight);
              }
              setMeasuredDimension(mBitmapWidth, mBitmapHeight);
         }
         @Override
         protected void onDraw(Canvas canvas) {
              // TODO Auto-generated method stub
              super.onDraw(canvas);
              if (isStartRipple) {
                   float f1 = 3 * mBitmapHeight / 10;
                   mRipplePaint.setAlpha(255);
                   canvas.drawCircle(mBitmapWidth / 2, mBitmapHeight,
                             7 * mBitmapHeight / 10, mRipplePaint);
                   int i1 = (int) (220.0F - (220.0F - 0.0F) / 100.0F
                             * rippleFirstRadius);
                   mRipplePaint.setAlpha(i1);
                   canvas.drawCircle(mBitmapWidth / 2, mBitmapHeight, 7
                             * mBitmapHeight / 10 + f1 * rippleFirstRadius / 100.0F,
                             mRipplePaint);
                   if (rippleSecendRadius >= 0) {
                        int i3 = (int) (220.0F - (220.0F - 0.0F) / 100.0F
                                  * rippleSecendRadius);
                        mRipplePaint.setAlpha(i3);
                        canvas.drawCircle(mBitmapWidth / 2, mBitmapHeight,
                                  7 * mBitmapHeight / 10 + f1 * rippleSecendRadius
                                            / 100.0F, mRipplePaint);
                   }
                   if (rippleThirdRadius >= 0) {
                        int i2 = (int) (220.0F - (220.0F - 0.0F) / 100.0F
                                  * rippleThirdRadius);
                        mRipplePaint.setAlpha(i2);
                        canvas.drawCircle(mBitmapWidth / 2, mBitmapHeight, 7
                                  * mBitmapHeight / 10 + f1 * rippleThirdRadius / 100.0F,
                                  mRipplePaint);
                   }
              }
              mRipplePaint.setAlpha(30);
              canvas.drawCircle(mBitmapWidth / 2, mBitmapHeight, mBitmapHeight,
                        mRipplePaint);
              mRipplePaint.setAlpha(120);
              canvas.drawCircle(mBitmapWidth / 2, mBitmapHeight,
                        9 * mBitmapHeight / 10, mRipplePaint);
              mRipplePaint.setAlpha(180);
              canvas.drawCircle(mBitmapWidth / 2, mBitmapHeight,
                        8 * mBitmapHeight / 10, mRipplePaint);
              mRipplePaint.setAlpha(255);
              canvas.drawCircle(mBitmapWidth / 2, mBitmapHeight,
                        7 * mBitmapHeight / 10, mRipplePaint);
              float length = textPaint.measureText(mText);
              canvas.drawText(mText, (mBitmapWidth - length) / 2,
                        mBitmapHeight * 3 / 4, textPaint);
         }
         @Override
         protected void onSizeChanged(int w, int h, int oldw, int oldh) {
              // TODO Auto-generated method stub
              super.onSizeChanged(w, h, oldw, oldh);
              mScreenWidth = w;
              mScreenHeight = h;
              confirmSize();
              invalidate();
         }
         private void confirmSize() {
              int minScreenSize = Math.min(mScreenWidth, mScreenHeight);
              int widthOverSize = mScreenWidth - minScreenSize;
              int heightOverSize = mScreenHeight - minScreenSize;
              heightPaddingTop = (getPaddingTop() + heightOverSize / 2);
              heightPaddingBottom = (getPaddingBottom() + heightOverSize / 2);
              widthPaddingLeft = (getPaddingLeft() + widthOverSize / 2);
              widthPaddingRight = (getPaddingRight() + widthOverSize / 2);
              int width = getWidth();
              int height = getHeight();
              mRect = new RectF(widthPaddingLeft, heightPaddingTop, width
                        - widthPaddingRight, height * 2 - heightPaddingBottom);
         }
         public void stratRipple() {
              isStartRipple = true;
              handler.sendEmptyMessage(0);
         }
    }

    下图是某个应用的流量显示界面,使用的是上面说的第二种形式。

     
     实现上面效果的思路是使用正弦或者余弦曲线,代码如下:
    /**
    *
    */
    package com.kince.waveview;
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.Path;
    import android.graphics.RectF;
    import android.os.Handler;
    import android.os.Parcel;
    import android.os.Parcelable;
    import android.util.AttributeSet;
    import android.view.View;
    import android.widget.ProgressBar;
    /**
    * @author kince
    * @category View必须是正方形
    *
    */
    public class WaterWaveView extends View {
         private Context mContext;
         private int mScreenWidth;
         private int mScreenHeight;
         private Paint mRingPaint;
         private Paint mCirclePaint;
         private Paint mWavePaint;
         private Paint linePaint;
         private Paint flowPaint;
         private Paint leftPaint;
         private int mRingSTROKEWidth = 15;
         private int mCircleSTROKEWidth = 2;
         private int mLineSTROKEWidth = 1;
         private int mCircleColor = Color.WHITE;
         private int mRingColor = Color.WHITE;
         private int mWaveColor = Color.WHITE;
         private Handler mHandler;
         private long c = 0L;
         private boolean mStarted = false;
         private final float f = 0.033F;
         private int mAlpha = 50;// 透明度
         private float mAmplitude = 10.0F; // 振幅
         private float mWateLevel = 0.5F;// 水高(0~1)
         private Path mPath;
         private String flowNum = "1024M";
         private String flowLeft = "还剩余";
         /**
         * @param context
         */
         public WaterWaveView(Context context) {
              super(context);
              // TODO Auto-generated constructor stub
              mContext = context;
              init(mContext);
         }
         /**
         * @param context
         * @param attrs
         */
         public WaterWaveView(Context context, AttributeSet attrs) {
              super(context, attrs);
              // TODO Auto-generated constructor stub
              mContext = context;
              init(mContext);
         }
         /**
         * @param context
         * @param attrs
         * @param defStyleAttr
         */
         public WaterWaveView(Context context, AttributeSet attrs, int defStyleAttr) {
              super(context, attrs, defStyleAttr);
              // TODO Auto-generated constructor stub
              mContext = context;
              init(mContext);
         }
         private void init(Context context) {
              mRingPaint = new Paint();
              mRingPaint.setColor(mRingColor);
              mRingPaint.setAlpha(50);
              mRingPaint.setStyle(Paint.Style.STROKE);
              mRingPaint.setAntiAlias(true);
              mRingPaint.setStrokeWidth(mRingSTROKEWidth);
              mCirclePaint = new Paint();
              mCirclePaint.setColor(mCircleColor);
              mCirclePaint.setStyle(Paint.Style.STROKE);
              mCirclePaint.setAntiAlias(true);
              mCirclePaint.setStrokeWidth(mCircleSTROKEWidth);
              linePaint = new Paint();
              linePaint.setColor(mCircleColor);
              linePaint.setStyle(Paint.Style.STROKE);
              linePaint.setAntiAlias(true);
              linePaint.setStrokeWidth(mLineSTROKEWidth);
              flowPaint = new Paint();
              flowPaint.setColor(mCircleColor);
              flowPaint.setStyle(Paint.Style.FILL);
              flowPaint.setAntiAlias(true);
              flowPaint.setTextSize(36);
              leftPaint = new Paint();
              leftPaint.setColor(mCircleColor);
              leftPaint.setStyle(Paint.Style.FILL);
              leftPaint.setAntiAlias(true);
              leftPaint.setTextSize(18);
              mWavePaint = new Paint();
              mWavePaint.setStrokeWidth(1.0F);
              mWavePaint.setColor(mWaveColor);
              mWavePaint.setAlpha(mAlpha);
              mPath = new Path();
              mHandler = new Handler() {
                   @Override
                   public void handleMessage(android.os.Message msg) {
                        if (msg.what == 0) {
                             invalidate();
                             if (mStarted) {
                                  // 不断发消息给自己,使自己不断被重绘
                                  mHandler.sendEmptyMessageDelayed(0, 60L);
                             }
                        }
                   }
              };
         }
         @Override
         protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
              int width = measure(widthMeasureSpec, true);
              int height = measure(heightMeasureSpec, false);
              if (width < height) {
                   setMeasuredDimension(width, width);
              else {
                   setMeasuredDimension(height, height);
              }
         }
         /**
         * @category 测量
         * @param measureSpec
         * @param isWidth
         * @return
         */
         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;
         }
         @Override
         protected void onSizeChanged(int w, int h, int oldw, int oldh) {
              // TODO Auto-generated method stub
              super.onSizeChanged(w, h, oldw, oldh);
              mScreenWidth = w;
              mScreenHeight = h;
         }
         @Override
         protected void onDraw(Canvas canvas) {
              // TODO Auto-generated method stub
              super.onDraw(canvas);
              // 得到控件的宽高
              int width = getWidth();
              int height = getHeight();
              setBackgroundColor(mContext.getResources().getColor(
                        R.color.holo_purple2));
              canvas.drawCircle(mScreenWidth / 2, mScreenHeight / 2,
                        mScreenWidth / 4, mRingPaint);
              canvas.drawCircle(mScreenWidth / 2, mScreenHeight / 2, mScreenWidth / 4
                        - mRingSTROKEWidth / 2, mCirclePaint);
              canvas.drawLine(mScreenWidth * 3 / 8, mScreenHeight * 5 / 8,
                        mScreenWidth * 5 / 8, mScreenHeight * 5 / 8, linePaint);
              float num = flowPaint.measureText(flowNum);
              canvas.drawText(flowNum, mScreenWidth * 4 / 8 - num / 2,
                        mScreenHeight * 4 / 8, flowPaint);
              float left = leftPaint.measureText(flowLeft);
              canvas.drawText(flowLeft, mScreenWidth * 4 / 8 - left / 2,
                        mScreenHeight * 3 / 8, leftPaint);
              // 如果未开始(未调用startWave方法),绘制一个扇形
              if ((!mStarted) || (mScreenWidth == 0) || (mScreenHeight == 0)) {
                   RectF oval = new RectF(mScreenWidth / 4 + mRingSTROKEWidth / 2,
                             mScreenHeight / 4 + mRingSTROKEWidth / 2, mScreenWidth * 3
                                       / 4 - mRingSTROKEWidth / 2, mScreenHeight * 3 / 4
                                       - mRingSTROKEWidth / 2);// 设置个新的长方形,扫描测量
                   canvas.drawArc(oval, 0, 180, true, mWavePaint);
                   return;
              }
              // 绘制,即水面静止时的高度
              RectF oval = new RectF(mScreenWidth / 4 + mRingSTROKEWidth / 2,
                        mScreenHeight / 4 + mRingSTROKEWidth / 2 + mAmplitude * 2,
                        mScreenWidth * 3 / 4 - mRingSTROKEWidth / 2, mScreenHeight * 3
                                  / 4 - mRingSTROKEWidth / 2);// 设置个新的长方形,扫描测量
              canvas.drawArc(oval, 0, 180, true, mWavePaint);
              if (this.c >= 8388607L) {
                   this.c = 0L;
              }
              // 每次onDraw时c都会自增
              c = (1L + c);
              float f1 = mScreenHeight * (1.0F - mWateLevel);
              int top = (int) (f1 + mAmplitude);
              mPath.reset();
              int startX = mScreenWidth / 2 - mScreenWidth / 4 + mRingSTROKEWidth / 2;
              // 波浪效果
              while (startX < mScreenWidth / 2 + mScreenWidth / 4 - mRingSTROKEWidth
                        / 2) {
                   int startY = (int) (f1 - mAmplitude
                             * Math.sin(Math.PI
                                       * (2.0F * (startX + this.c * width * this.f))
                                       / width));
                   canvas.drawLine(startX, startY, startX, top, mWavePaint);
                   startX++;
              }
              canvas.restore();
         }
         @Override
         public Parcelable onSaveInstanceState() {
              // Force our ancestor class to save its state
              Parcelable superState = super.onSaveInstanceState();
              SavedState ss = new SavedState(superState);
              ss.progress = (int) c;
              return ss;
         }
         @Override
         public void onRestoreInstanceState(Parcelable state) {
              SavedState ss = (SavedState) state;
              super.onRestoreInstanceState(ss.getSuperState());
              c = ss.progress;
         }
         @Override
         protected void onAttachedToWindow() {
              super.onAttachedToWindow();
              // 关闭硬件加速,防止异常unsupported operation exception
              this.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
         }
         @Override
         protected void onDetachedFromWindow() {
              super.onDetachedFromWindow();
         }
         /**
         * @category 开始波动
         */
         public void startWave() {
              if (!mStarted) {
                   this.c = 0L;
                   mStarted = true;
                   this.mHandler.sendEmptyMessage(0);
              }
         }
         /**
         * @category 停止波动
         */
         public void stopWave() {
              if (mStarted) {
                   this.c = 0L;
                   mStarted = false;
                   this.mHandler.removeMessages(0);
              }
         }
         /**
         * @category 保存状态
         */
         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 Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
                   public SavedState createFromParcel(Parcel in) {
                        return new SavedState(in);
                   }
                   public SavedState[] newArray(int size) {
                        return new SavedState[size];
                   }
              };
         }
    }

    github下载地址:

  • 相关阅读:
    fork操作
    PHP操作Memcached
    对nginx进行平滑升级
    Codeforces Round #457 (Div. 2) B
    codeforces Educational Codeforces Round 39 (Rated for Div. 2) D
    矩阵相乘
    求组合数板子
    斯特林(Stirling)公式 求大数阶乘的位数
    codeforces Gym 101572 I 有向图最小环路径
    Floyd算法——保存路径——输出路径 HDU1385
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/4775588.html
Copyright © 2020-2023  润新知