Android 分享一个流量显示界面

版本:1.0 
日期:2014.8.9 2014.9.24
版权:© 2014 kince 转载注明出处  

  波形效果有几种不同的呈现形式,比如从中间向四周散开的波形,也就是熟知的水涟漪;还有上下波动的曲线,像五线谱等。英文中可以称作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下载地址:
                                                                                                                                                                





郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。