Android 分享一个流量显示界面

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

  波形效果有几种不同的呈现形式,比如从中间向四周散开的波形,也就是熟知的水涟漪;还有上下波动的曲线,像五线谱等。英文中可以称作Wave或者Ripple,所以暂且叫它们WaveView、WaveLayout、RippleView、RippleLayout,接下来开始实现这些效果。
  首先看一下Solo 火爆足球动态壁纸,
Android 分享一个流量显示界面_第1张图片
  下面中间的按钮就是一个波形按钮,它会不断地向四周辐射,由于是静态图,如果想体验真实效果可以另行下载。这种效果的实现思路是不断绘制圆形,当然半径也要不断变化,透明度也是一样。代码如下:
[html] view plain copy
  1. /**  
  2. *  
  3. */  
  4. package com.kince.rippleview;  
  5.   
  6. import android.content.Context;  
  7. import android.graphics.Bitmap;  
  8. import android.graphics.BitmapFactory;  
  9. import android.graphics.Canvas;  
  10. import android.graphics.Color;  
  11. import android.graphics.Paint;  
  12. import android.graphics.RectF;  
  13. import android.os.Handler;  
  14. import android.os.Message;  
  15. import android.util.AttributeSet;  
  16. import android.view.View;  
  17.   
  18. /**  
  19. * @author kince  
  20. * @category 波纹  
  21. * @since 2014.8.9  
  22. * @version v1.0.0  
  23. *  
  24. */  
  25. public class RippleView extends View {  
  26.   
  27.      private int mScreenWidth;  
  28.      private int mScreenHeight;  
  29.   
  30.      private Bitmap mRippleBitmap;  
  31.      private Paint mRipplePaint = new Paint();  
  32.   
  33.      private int mBitmapWidth;  
  34.      private int mBitmapHeight;  
  35.   
  36.      private boolean isStartRipple;  
  37.   
  38.      private int heightPaddingTop;  
  39.      private int heightPaddingBottom;  
  40.      private int widthPaddingLeft;  
  41.      private int widthPaddingRight;  
  42.   
  43.      private RectF mRect = new RectF();  
  44.   
  45.      private int rippleFirstRadius = 0;  
  46.      private int rippleSecendRadius = -33;  
  47.      private int rippleThirdRadius = -66;  
  48.   
  49.      private Paint textPaint = new Paint();  
  50.     private String mText="点击我吧";  
  51.      
  52.      private Handler handler = new Handler() {  
  53.           @Override  
  54.           public void handleMessage(Message msg) {  
  55.                // TODO Auto-generated method stub  
  56.                super.handleMessage(msg);  
  57.                invalidate();  
  58.                if (isStartRipple) {  
  59.                     rippleFirstRadius++;  
  60.                     if (rippleFirstRadius > 100) {  
  61.                          rippleFirstRadius = 0;  
  62.                     }  
  63.                     rippleSecendRadius++;  
  64.                     if (rippleSecendRadius > 100) {  
  65.                          rippleSecendRadius = 0;  
  66.                     }  
  67.                     rippleThirdRadius++;  
  68.                     if (rippleThirdRadius > 100) {  
  69.                          rippleThirdRadius = 0;  
  70.                     }  
  71.                     sendEmptyMessageDelayed(0, 20);  
  72.                }  
  73.           }  
  74.      };  
  75.   
  76.      /**  
  77.      * @param context  
  78.      */  
  79.      public RippleView(Context context) {  
  80.           super(context);  
  81.           // TODO Auto-generated constructor stub  
  82.           init();  
  83.      }  
  84.   
  85.      /**  
  86.      * @param context  
  87.      * @param attrs  
  88.      */  
  89.      public RippleView(Context context, AttributeSet attrs) {  
  90.           super(context, attrs);  
  91.           // TODO Auto-generated constructor stub  
  92.           init();  
  93.      }  
  94.   
  95.      /**  
  96.      * @param context  
  97.      * @param attrs  
  98.      * @param defStyleAttr  
  99.      */  
  100.      public RippleView(Context context, AttributeSet attrs, int defStyleAttr) {  
  101.           super(context, attrs, defStyleAttr);  
  102.           // TODO Auto-generated constructor stub  
  103.           init();  
  104.      }  
  105.   
  106.      private void init() {  
  107.           mRipplePaint.setColor(4961729);  
  108.           mRipplePaint.setAntiAlias(true);  
  109.           mRipplePaint.setStyle(Paint.Style.FILL);  
  110.   
  111.           textPaint.setTextSize(26);  
  112.           textPaint.setAntiAlias(true);  
  113.           textPaint.setStyle(Paint.Style.FILL);  
  114.           textPaint.setColor(Color.WHITE);  
  115.   
  116.           mRippleBitmap = BitmapFactory.decodeStream(getResources()  
  117.                     .openRawResource(R.drawable.easy3d_ic_apply));  
  118.           mBitmapWidth = mRippleBitmap.getWidth();  
  119.           mBitmapHeight = mRippleBitmap.getHeight();  
  120.      }  
  121.   
  122.      @Override  
  123.      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  124.           // TODO Auto-generated method stub  
  125.           super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  126.           int mh = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();  
  127.           int mw = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();  
  128.           if (mBitmapWidth < 2 * mBitmapHeight) {  
  129.                mBitmapWidth = (2 * mBitmapHeight);  
  130.           }  
  131.           setMeasuredDimension(mBitmapWidth, mBitmapHeight);  
  132.      }  
  133.   
  134.      @Override  
  135.      protected void onDraw(Canvas canvas) {  
  136.           // TODO Auto-generated method stub  
  137.           super.onDraw(canvas);  
  138.           if (isStartRipple) {  
  139.                float f1 = 3 * mBitmapHeight / 10;  
  140.                mRipplePaint.setAlpha(255);  
  141.                canvas.drawCircle(mBitmapWidth / 2, mBitmapHeight,  
  142.                          7 * mBitmapHeight / 10, mRipplePaint);  
  143.                int i1 = (int) (220.0F - (220.0F - 0.0F) / 100.0F  
  144.                          * rippleFirstRadius);  
  145.                mRipplePaint.setAlpha(i1);  
  146.                canvas.drawCircle(mBitmapWidth / 2, mBitmapHeight, 7  
  147.                          * mBitmapHeight / 10 + f1 * rippleFirstRadius / 100.0F,  
  148.                          mRipplePaint);  
  149.                if (rippleSecendRadius >= 0) {  
  150.                     int i3 = (int) (220.0F - (220.0F - 0.0F) / 100.0F  
  151.                               * rippleSecendRadius);  
  152.                     mRipplePaint.setAlpha(i3);  
  153.                     canvas.drawCircle(mBitmapWidth / 2, mBitmapHeight,  
  154.                               7 * mBitmapHeight / 10 + f1 * rippleSecendRadius  
  155.                                         / 100.0F, mRipplePaint);  
  156.                }  
  157.                if (rippleThirdRadius >= 0) {  
  158.                     int i2 = (int) (220.0F - (220.0F - 0.0F) / 100.0F  
  159.                               * rippleThirdRadius);  
  160.                     mRipplePaint.setAlpha(i2);  
  161.                     canvas.drawCircle(mBitmapWidth / 2, mBitmapHeight, 7  
  162.                               * mBitmapHeight / 10 + f1 * rippleThirdRadius / 100.0F,  
  163.                               mRipplePaint);  
  164.                }  
  165.   
  166.           }  
  167.           mRipplePaint.setAlpha(30);  
  168.           canvas.drawCircle(mBitmapWidth / 2, mBitmapHeight, mBitmapHeight,  
  169.                     mRipplePaint);  
  170.           mRipplePaint.setAlpha(120);  
  171.           canvas.drawCircle(mBitmapWidth / 2, mBitmapHeight,  
  172.                     9 * mBitmapHeight / 10, mRipplePaint);  
  173.           mRipplePaint.setAlpha(180);  
  174.           canvas.drawCircle(mBitmapWidth / 2, mBitmapHeight,  
  175.                     8 * mBitmapHeight / 10, mRipplePaint);  
  176.           mRipplePaint.setAlpha(255);  
  177.           canvas.drawCircle(mBitmapWidth / 2, mBitmapHeight,  
  178.                     7 * mBitmapHeight / 10, mRipplePaint);  
  179.           float length = textPaint.measureText(mText);  
  180.           canvas.drawText(mText, (mBitmapWidth - length) / 2,  
  181.                     mBitmapHeight * 3 / 4, textPaint);  
  182.   
  183.      }  
  184.   
  185.      @Override  
  186.      protected void onSizeChanged(int w, int h, int oldw, int oldh) {  
  187.           // TODO Auto-generated method stub  
  188.           super.onSizeChanged(w, h, oldw, oldh);  
  189.           mScreenWidth = w;  
  190.           mScreenHeight = h;  
  191.           confirmSize();  
  192.           invalidate();  
  193.   
  194.      }  
  195.   
  196.      private void confirmSize() {  
  197.           int minScreenSize = Math.min(mScreenWidth, mScreenHeight);  
  198.           int widthOverSize = mScreenWidth - minScreenSize;  
  199.           int heightOverSize = mScreenHeight - minScreenSize;  
  200.           heightPaddingTop = (getPaddingTop() + heightOverSize / 2);  
  201.           heightPaddingBottom = (getPaddingBottom() + heightOverSize / 2);  
  202.           widthPaddingLeft = (getPaddingLeft() + widthOverSize / 2);  
  203.           widthPaddingRight = (getPaddingRight() + widthOverSize / 2);  
  204.   
  205.           int width = getWidth();  
  206.           int height = getHeight();  
  207.   
  208.           mRect = new RectF(widthPaddingLeft, heightPaddingTop, width  
  209.                     - widthPaddingRight, height * 2 - heightPaddingBottom);  
  210.   
  211.      }  
  212.   
  213.      public void stratRipple() {  
  214.           isStartRipple = true;  
  215.           handler.sendEmptyMessage(0);  
  216.      }  
  217.   
  218. }  
  下图是某个应用的流量显示界面,使用的是上面说的第二种形式。
Android 分享一个流量显示界面_第2张图片
  实现上面效果的思路是使用正弦或者余弦曲线,代码如下:
[html] view plain copy
  1. /**  
  2. *  
  3. */  
  4. package com.kince.waveview;  
  5.   
  6. import android.content.Context;  
  7. import android.graphics.Canvas;  
  8. import android.graphics.Color;  
  9. import android.graphics.Paint;  
  10. import android.graphics.Path;  
  11. import android.graphics.RectF;  
  12. import android.os.Handler;  
  13. import android.os.Parcel;  
  14. import android.os.Parcelable;  
  15. import android.util.AttributeSet;  
  16. import android.view.View;  
  17. import android.widget.ProgressBar;  
  18.   
  19. /**  
  20. * @author kince  
  21. * @category View必须是正方形  
  22. *  
  23. */  
  24. public class WaterWaveView extends View {  
  25.   
  26.      private Context mContext;  
  27.   
  28.      private int mScreenWidth;  
  29.      private int mScreenHeight;  
  30.   
  31.      private Paint mRingPaint;  
  32.      private Paint mCirclePaint;  
  33.      private Paint mWavePaint;  
  34.      private Paint linePaint;  
  35.      private Paint flowPaint;  
  36.      private Paint leftPaint;  
  37.   
  38.      private int mRingSTROKEWidth = 15;  
  39.      private int mCircleSTROKEWidth = 2;  
  40.      private int mLineSTROKEWidth = 1;  
  41.   
  42.      private int mCircleColor = Color.WHITE;  
  43.      private int mRingColor = Color.WHITE;  
  44.      private int mWaveColor = Color.WHITE;  
  45.   
  46.      private Handler mHandler;  
  47.      private long c = 0L;  
  48.      private boolean mStarted = false;  
  49.      private final float f = 0.033F;  
  50.      private int mAlpha = 50;// 透明度  
  51.      private float mAmplitude = 10.0F; // 振幅  
  52.      private float mWateLevel = 0.5F;// 水高(0~1)  
  53.      private Path mPath;  
  54.   
  55.      private String flowNum = "1024M";  
  56.      private String flowLeft = "还剩余";  
  57.   
  58.      /**  
  59.      * @param context  
  60.      */  
  61.      public WaterWaveView(Context context) {  
  62.           super(context);  
  63.           // TODO Auto-generated constructor stub  
  64.           mContext = context;  
  65.           init(mContext);  
  66.      }  
  67.   
  68.      /**  
  69.      * @param context  
  70.      * @param attrs  
  71.      */  
  72.      public WaterWaveView(Context context, AttributeSet attrs) {  
  73.           super(context, attrs);  
  74.           // TODO Auto-generated constructor stub  
  75.           mContext = context;  
  76.           init(mContext);  
  77.      }  
  78.   
  79.      /**  
  80.      * @param context  
  81.      * @param attrs  
  82.      * @param defStyleAttr  
  83.      */  
  84.      public WaterWaveView(Context context, AttributeSet attrs, int defStyleAttr) {  
  85.           super(context, attrs, defStyleAttr);  
  86.           // TODO Auto-generated constructor stub  
  87.           mContext = context;  
  88.           init(mContext);  
  89.      }  
  90.   
  91.      private void init(Context context) {  
  92.           mRingPaint = new Paint();  
  93.           mRingPaint.setColor(mRingColor);  
  94.           mRingPaint.setAlpha(50);  
  95.           mRingPaint.setStyle(Paint.Style.STROKE);  
  96.           mRingPaint.setAntiAlias(true);  
  97.           mRingPaint.setStrokeWidth(mRingSTROKEWidth);  
  98.   
  99.           mCirclePaint = new Paint();  
  100.           mCirclePaint.setColor(mCircleColor);  
  101.           mCirclePaint.setStyle(Paint.Style.STROKE);  
  102.           mCirclePaint.setAntiAlias(true);  
  103.           mCirclePaint.setStrokeWidth(mCircleSTROKEWidth);  
  104.   
  105.           linePaint = new Paint();  
  106.           linePaint.setColor(mCircleColor);  
  107.           linePaint.setStyle(Paint.Style.STROKE);  
  108.           linePaint.setAntiAlias(true);  
  109.           linePaint.setStrokeWidth(mLineSTROKEWidth);  
  110.   
  111.           flowPaint = new Paint();  
  112.           flowPaint.setColor(mCircleColor);  
  113.           flowPaint.setStyle(Paint.Style.FILL);  
  114.           flowPaint.setAntiAlias(true);  
  115.           flowPaint.setTextSize(36);  
  116.   
  117.           leftPaint = new Paint();  
  118.           leftPaint.setColor(mCircleColor);  
  119.           leftPaint.setStyle(Paint.Style.FILL);  
  120.           leftPaint.setAntiAlias(true);  
  121.           leftPaint.setTextSize(18);  
  122.   
  123.           mWavePaint = new Paint();  
  124.           mWavePaint.setStrokeWidth(1.0F);  
  125.           mWavePaint.setColor(mWaveColor);  
  126.           mWavePaint.setAlpha(mAlpha);  
  127.           mPath = new Path();  
  128.   
  129.           mHandler = new Handler() {  
  130.                @Override  
  131.                public void handleMessage(android.os.Message msg) {  
  132.                     if (msg.what == 0) {  
  133.                          invalidate();  
  134.                          if (mStarted) {  
  135.                               // 不断发消息给自己,使自己不断被重绘  
  136.                               mHandler.sendEmptyMessageDelayed(0, 60L);  
  137.                          }  
  138.                     }  
  139.                }  
  140.           };  
  141.      }  
  142.   
  143.      @Override  
  144.      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  145.           int width = measure(widthMeasureSpec, true);  
  146.           int height = measure(heightMeasureSpec, false);  
  147.           if (width < height) {  
  148.                setMeasuredDimension(width, width);  
  149.           } else {  
  150.                setMeasuredDimension(height, height);  
  151.           }  
  152.   
  153.      }  
  154.   
  155.      /**  
  156.      * @category 测量  
  157.      * @param measureSpec  
  158.      * @param isWidth  
  159.      * @return  
  160.      */  
  161.      private int measure(int measureSpec, boolean isWidth) {  
  162.           int result;  
  163.           int mode = MeasureSpec.getMode(measureSpec);  
  164.           int size = MeasureSpec.getSize(measureSpec);  
  165.           int padding = isWidth ? getPaddingLeft() + getPaddingRight()  
  166.                     : getPaddingTop() + getPaddingBottom();  
  167.           if (mode == MeasureSpec.EXACTLY) {  
  168.                result = size;  
  169.           } else {  
  170.                result = isWidth ? getSuggestedMinimumWidth()  
  171.                          : getSuggestedMinimumHeight();  
  172.                result += padding;  
  173.                if (mode == MeasureSpec.AT_MOST) {  
  174.                     if (isWidth) {  
  175.                          result = Math.max(result, size);  
  176.                     } else {  
  177.                          result = Math.min(result, size);  
  178.                     }  
  179.                }  
  180.           }  
  181.           return result;  
  182.      }  
  183.   
  184.      @Override  
  185.      protected void onSizeChanged(int w, int h, int oldw, int oldh) {  
  186.           // TODO Auto-generated method stub  
  187.           super.onSizeChanged(w, h, oldw, oldh);  
  188.           mScreenWidth = w;  
  189.           mScreenHeight = h;  
  190.      }  
  191.   
  192.      @Override  
  193.      protected void onDraw(Canvas canvas) {  
  194.           // TODO Auto-generated method stub  
  195.           super.onDraw(canvas);  
  196.           // 得到控件的宽高  
  197.           int width = getWidth();  
  198.           int height = getHeight();  
  199.           setBackgroundColor(mContext.getResources().getColor(  
  200.                     R.color.holo_purple2));  
  201.   
  202.           canvas.drawCircle(mScreenWidth / 2, mScreenHeight / 2,  
  203.                     mScreenWidth / 4, mRingPaint);  
  204.   
  205.           canvas.drawCircle(mScreenWidth / 2, mScreenHeight / 2, mScreenWidth / 4  
  206.                     - mRingSTROKEWidth / 2, mCirclePaint);  
  207.           canvas.drawLine(mScreenWidth * 3 / 8, mScreenHeight * 5 / 8,  
  208.                     mScreenWidth * 5 / 8, mScreenHeight * 5 / 8, linePaint);  
  209.           float num = flowPaint.measureText(flowNum);  
  210.           canvas.drawText(flowNum, mScreenWidth * 4 / 8 - num / 2,  
  211.                     mScreenHeight * 4 / 8, flowPaint);  
  212.           float left = leftPaint.measureText(flowLeft);  
  213.           canvas.drawText(flowLeft, mScreenWidth * 4 / 8 - left / 2,  
  214.                     mScreenHeight * 3 / 8, leftPaint);  
  215.   
  216.           // 如果未开始(未调用startWave方法),绘制一个扇形  
  217.           if ((!mStarted) || (mScreenWidth == 0) || (mScreenHeight == 0)) {  
  218.                RectF oval = new RectF(mScreenWidth / 4 + mRingSTROKEWidth / 2,  
  219.                          mScreenHeight / 4 + mRingSTROKEWidth / 2, mScreenWidth * 3  
  220.                                    / 4 - mRingSTROKEWidth / 2, mScreenHeight * 3 / 4  
  221.                                    - mRingSTROKEWidth / 2);// 设置个新的长方形,扫描测量  
  222.                canvas.drawArc(oval, 0, 180, true, mWavePaint);  
  223.                return;  
  224.           }  
  225.           // 绘制,即水面静止时的高度  
  226.           RectF oval = new RectF(mScreenWidth / 4 + mRingSTROKEWidth / 2,  
  227.                     mScreenHeight / 4 + mRingSTROKEWidth / 2 + mAmplitude * 2,  
  228.                     mScreenWidth * 3 / 4 - mRingSTROKEWidth / 2, mScreenHeight * 3  
  229.                               / 4 - mRingSTROKEWidth / 2);// 设置个新的长方形,扫描测量  
  230.           canvas.drawArc(oval, 0, 180, true, mWavePaint);  
  231.   
  232.           if (this.c >= 8388607L) {  
  233.                this.c = 0L;  
  234.           }  
  235.           // 每次onDraw时c都会自增  
  236.           c = (1L + c);  
  237.           float f1 = mScreenHeight * (1.0F - mWateLevel);  
  238.           int top = (int) (f1 + mAmplitude);  
  239.           mPath.reset();  
  240.           int startX = mScreenWidth / 2 - mScreenWidth / 4 + mRingSTROKEWidth / 2;  
  241.           // 波浪效果  
  242.           while (startX < mScreenWidth / 2 + mScreenWidth / 4 - mRingSTROKEWidth  
  243.                     / 2) {  
  244.                int startY = (int) (f1 - mAmplitude  
  245.                          * Math.sin(Math.PI  
  246.                                    * (2.0F * (startX + this.c * width * this.f))  
  247.                                    / width));  
  248.                canvas.drawLine(startX, startY, startX, top, mWavePaint);  
  249.                startX++;  
  250.           }  
  251.           canvas.restore();  
  252.      }  
  253.   
  254.      @Override  
  255.      public Parcelable onSaveInstanceState() {  
  256.           // Force our ancestor class to save its state  
  257.           Parcelable superState = super.onSaveInstanceState();  
  258.           SavedState ss = new SavedState(superState);  
  259.           ss.progress = (int) c;  
  260.           return ss;  
  261.      }  
  262.   
  263.      @Override  
  264.      public void onRestoreInstanceState(Parcelable state) {  
  265.           SavedState ss = (SavedState) state;  
  266.           super.onRestoreInstanceState(ss.getSuperState());  
  267.           c = ss.progress;  
  268.      }  
  269.   
  270.      @Override  
  271.      protected void onAttachedToWindow() {  
  272.           super.onAttachedToWindow();  
  273.           // 关闭硬件加速,防止异常unsupported operation exception  
  274.           this.setLayerType(View.LAYER_TYPE_SOFTWARE, null);  
  275.      }  
  276.   
  277.      @Override  
  278.      protected void onDetachedFromWindow() {  
  279.           super.onDetachedFromWindow();  
  280.      }  
  281.   
  282.      /**  
  283.      * @category 开始波动  
  284.      */  
  285.      public void startWave() {  
  286.           if (!mStarted) {  
  287.                this.c = 0L;  
  288.                mStarted = true;  
  289.                this.mHandler.sendEmptyMessage(0);  
  290.           }  
  291.      }  
  292.   
  293.      /**  
  294.      * @category 停止波动  
  295.      */  
  296.      public void stopWave() {  
  297.           if (mStarted) {  
  298.                this.c = 0L;  
  299.                mStarted = false;  
  300.                this.mHandler.removeMessages(0);  
  301.           }  
  302.      }  
  303.   
  304.      /**  
  305.      * @category 保存状态  
  306.      */  
  307.      static class SavedState extends BaseSavedState {  
  308.           int progress;  
  309.   
  310.           /**  
  311.           * Constructor called from {@link ProgressBar#onSaveInstanceState()}  
  312.           */  
  313.           SavedState(Parcelable superState) {  
  314.                super(superState);  
  315.           }  
  316.   
  317.           /**  
  318.           * Constructor called from {@link #CREATOR}  
  319.           */  
  320.           private SavedState(Parcel in) {  
  321.                super(in);  
  322.                progress = in.readInt();  
  323.           }  
  324.   
  325.           @Override  
  326.           public void writeToParcel(Parcel out, int flags) {  
  327.                super.writeToParcel(out, flags);  
  328.                out.writeInt(progress);  
  329.           }  
  330.   
  331.           public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {  
  332.                public SavedState createFromParcel(Parcel in) {  
  333.                     return new SavedState(in);  
  334.                }  
  335.   
  336.                public SavedState[] newArray(int size) {  
  337.                     return new SavedState[size];  
  338.                }  
  339.           };  
  340.      }  
  341.   
  342. }  

github下载地址:
https://github.com/wangjinyu501/RippleView
https://github.com/wangjinyu501/WaterWaveView/

你可能感兴趣的:(Android 分享一个流量显示界面)