Android 波浪进度WaveView

CalWenger 8年前
   <p>刚好有注意到百度外卖以及淘宝个人中心,都用到了类似水波起伏的效果,于是就参照网上的资料然后自己整改,自定义了一个waveView ,原理么,首先就是自定义个 WaveView 继承 View ,然后再WaveView 内部实现代码逻辑:</p>    <p>① 水波就波嘛? sin函数? 贝塞尔曲线? 都行,这里就用二阶贝塞 尔曲线去画吧</p>    <p>② 波要动嘛,怎么动呢?线程? 好吧 这里用了个Handler。</p>    <p>③绘制波首先要找点,那么在onMeasure()里找出需要的点咯,这里就暂时展示一个波段吧,一个波长移动左边不就没了?OK 那就两个波吧,吼吼,两个波(猥琐男潜质表露无遗啊)。接下来就是Handler 结合 onDraw()绘制。OK,那就先看我Word绘制的粗瘪的波动图,请看VCR,oh,no... gif</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/7ae7d2dc39f6181cfba0cd02c50bf6d6.gif"></p>    <p style="text-align:center">wave.gif</p>    <p>意思就是波平移一个波长之后回到初始位置继续平移循环。</p>    <p>好吧,有人说了,这么简单的逻辑你要啰嗦那么多???</p>    <p>好吧,我承认,我有唐僧的潜质。。。</p>    <p>闲话就不说了,先上</p>    <h3>效果图</h3>    <p style="text-align:center"><img src="https://simg.open-open.com/show/ceb49a0262eb1c4b33a1138b6d0a67ce.gif"></p>    <p style="text-align:center">waveprogress.gif</p>    <h3>调用的Activity</h3>    <pre>  <code class="language-java">* Created by LiuDong on 2016/12/22.   * Email:15002102128@126.com   */    public class WaveActivity extends Activity {      LD_WaveView waveView;//方形      LD_WaveView waveCircleView;//圆形      private int progrees=0;//进度      private Handler mHandler=new Handler(){          @Override          public void handleMessage(Message msg) {              if (progrees==100) progrees=0;              Log.i("progress",progrees+"");              waveView.setmProgress(progrees++);              waveCircleView.setmProgress(progrees++);              mHandler.sendEmptyMessageDelayed(0,100);          }      };      @Override      protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(R.layout.activity_wave);          waveView= (LD_WaveView) findViewById(R.id.waveView);          waveCircleView= (LD_WaveView) findViewById(R.id.waveViewCircle);          mHandler.sendEmptyMessageDelayed(0,10);      }  }</code></pre>    <h3>xml布局</h3>    <pre>  <code class="language-java"><?xml version="1.0" encoding="utf-8"?>  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"      xmlns:app="http://schemas.android.com/apk/res-auto"      android:orientation="vertical" android:layout_width="match_parent"      android:background="@color/ld_White"      android:layout_height="match_parent">      <com.dadong.ld_tools.widget.LD_WaveView          android:id="@+id/waveViewCircle"          android:layout_marginTop="20dp"          android:layout_width="100dp"          android:layout_centerHorizontal="true"          android:layout_height="100dp"          app:wave_color="@color/ld_Black"          app:wave_circle="true"          />      <com.dadong.ld_tools.widget.LD_WaveView          android:id="@+id/waveView"          android:layout_width="100dp"          android:layout_height="100dp"          app:wave_color="@color/ld_Black"          app:wave_circle="false"          android:layout_centerInParent="true" />  </RelativeLayout></code></pre>    <h3>自定义WaveView</h3>    <pre>  <code class="language-java">/**   * Created by LiuDong on 2016/12/23.   * Email:15002102128@126.com   */    public class LD_WaveView extends View {        private int mProgress;//进度      private int mTimeStep = 10;//时间间隔      private int mSpeed = 5;//波单次移动的距离      private int mViewHeight;//视图宽高      private int mViewWidth;//视图宽度      private int mLevelLine;// 基准线          private int mWaveLength;//波长 暂定view宽度为一个波长      private int mStrokeWidth;//园的线宽      private RectF rectF;//圆环区域      private int mWaveHeight;//波峰高度      private int mLeftWaveMoveLength;//波平移的距离,用来控制波的起点位置      private int mWaveColor;//波的颜色      private Paint mPaint;//画笔      private Paint mCirclePaint;//圆环画笔      private Paint mBorderPaint;//边界画笔      private int   mBorderWidth=4;//边界宽度      private Paint mTextPaint;//文字画笔      private Path mPath;//绘画线      private List<Point> mPoints;//点的集合      private boolean isMeasure = false;//是否已测量过      private boolean isCircle=false;//是否圆形默认false,可属性代码设置      //处理消息      private Handler handler = new Handler() {          @Override          public void handleMessage(Message msg) {                initWaveMove();          }      };        /**       * 初始化波的移动       */      private void  initWaveMove(){          mLeftWaveMoveLength+=mSpeed;//波向右移动距离增加mSpeed;          if (mLeftWaveMoveLength>=mWaveLength){//当增加到一个波长时回复到0              mLeftWaveMoveLength=0;          }          invalidate();        }      public LD_WaveView(Context context) {          this(context, null);      }        public LD_WaveView(Context context, AttributeSet attrs) {          this(context, attrs, 0);      }        public LD_WaveView(Context context, AttributeSet attrs, int defStyleAttr) {          super(context, attrs, defStyleAttr);            getAttr(context, attrs, defStyleAttr);          init();        }        /**       * 初始化画笔       */      private void init() {          mPoints = new ArrayList<Point>();          //波浪轨迹画笔          mPaint = new Paint();          mPaint.setAntiAlias(true);          mPaint.setColor(mWaveColor);          mPaint.setStyle(Paint.Style.FILL_AND_STROKE);              mPath = new Path();              //文字画笔          mTextPaint=new Paint();          mTextPaint.setColor(Color.RED);          mTextPaint.setTextAlign(Paint.Align.CENTER);          mTextPaint.setTextSize(48);              //圆环画笔          mCirclePaint=new Paint();          mCirclePaint.setAntiAlias(true);          mCirclePaint.setColor(Color.WHITE);          mCirclePaint.setStyle(Paint.Style.STROKE);          //边界线画笔          mBorderPaint=new Paint();          mBorderPaint.setAntiAlias(true);          mBorderPaint.setColor(mWaveColor);          mBorderPaint.setStrokeWidth(mBorderWidth);          mBorderPaint.setStyle(Paint.Style.STROKE);          }        /**       * 获取自定义的属性值       *       * @param attrs       */      private void getAttr(Context context, AttributeSet attrs, int defStyle) {            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LD_WaveView, defStyle, 0);            mWaveColor = a.getColor(R.styleable.LD_WaveView_wave_color, Color.RED);          isCircle=a.getBoolean(R.styleable.LD_WaveView_wave_circle,false);          a.recycle();        }          /**       *       * @param widthMeasureSpec       * @param heightMeasureSpec       */      @Override      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {            super.onMeasure(widthMeasureSpec, heightMeasureSpec);          if (!isMeasure&&Math.abs(getMeasuredHeight()-getMeasuredWidth())<50) {//只计算一次就够了 ,relativelayout的时候要绘制两次 加个宽高判断              mViewHeight = getMeasuredHeight();              mViewWidth = getMeasuredWidth();              mLevelLine = mViewHeight;  //初始化波的准位线       起始位视图最底部              {                  mLevelLine = mViewHeight * (100-mProgress) / 100;                  if (mLevelLine < 0) mLevelLine = 0;              }              //计算波峰值              mWaveHeight = mViewHeight / 20;//波峰暂定为view高度的1/20,如果需要设置 可设置set方法赋值;              mWaveLength = getMeasuredWidth();                //计算所有的点 这里取宽度为整个波长  往左再延伸一个波长 两个波长则需要9个点              for (int i = 0; i < 9; i++) {                  int y = 0;                  switch (i % 4) {                      case 0:                          y = mViewHeight;                          break;                      case 1:                          y =mViewHeight+ mWaveHeight;                          break;                      case 2:                          y = mViewHeight;                          break;                      case 3:                          y = mViewHeight-mWaveHeight;                          break;                  }                  Point point = new Point(-mWaveLength + i * mWaveLength / 4, y);                  mPoints.add(point);              }              /**               * 计算圆环宽度               */              int mIncircleRadius=mViewHeight<mViewWidth?mViewHeight/2:mViewWidth/2;//内切圆半径                int mcircumcircleRadius= (int) (Math.sqrt((float)(Math.pow(mViewHeight/2,2)+Math.pow(mViewWidth/2,2)))+0.5);//外接圆半径              int radius=mcircumcircleRadius/2+mIncircleRadius/2;                rectF=new RectF(mViewWidth/2-radius,mViewHeight/2-radius,mViewWidth/2+radius,mViewHeight/2+radius);              mStrokeWidth=mcircumcircleRadius-mIncircleRadius;              mCirclePaint.setStrokeWidth(mStrokeWidth);//线是有宽度的  采用了这种方式画圆环              isMeasure = true;          }      }        @Override      protected void onDraw(Canvas canvas) {          super.onDraw(canvas);          /**           * 绘制线条           */          mPath.reset();          int i = 0;          mPath.moveTo(mPoints.get(0).getX()+mLeftWaveMoveLength, mPoints.get(0).getY()-mViewHeight*mProgress/100);          for (; i < mPoints.size() - 2; i += 2) {              mPath.quadTo(mPoints.get(i + 1).getX()+mLeftWaveMoveLength, mPoints.get(i + 1).getY()-mViewHeight*mProgress/100, mPoints.get(i + 2).getX()+mLeftWaveMoveLength, mPoints.get(i + 2).getY()-mViewHeight*mProgress/100);          }          mPath.lineTo(mPoints.get(i).getX()+mLeftWaveMoveLength, mViewHeight);          mPath.lineTo(mPoints.get(0).getX()+mLeftWaveMoveLength, mViewHeight);          mPath.close();          /**           * 绘制轨迹           */          canvas.drawPath(mPath,mPaint);          Rect rect = new Rect();            String progress=String.format("%d%%",mProgress);          mTextPaint.getTextBounds(progress,0,progress.length(), rect);          int textHeight = rect.height();          if (mProgress>=50)//如果进度达到50 颜色变为白色,没办法啊,进度在中间 不变颜色看不到              mTextPaint.setColor(Color.WHITE);          else          mTextPaint.setColor(mWaveColor);          canvas.drawText(progress,0,progress.length(),mViewWidth/2,mViewHeight/2+textHeight/2,mTextPaint);            if (isCircle) {              /**               * 绘制圆环               */                canvas.drawArc(rectF, 0, 360, true, mCirclePaint);              Paint circlePaint = new Paint();              circlePaint.setStrokeWidth(5);              circlePaint.setColor(Color.WHITE);              circlePaint.setAntiAlias(true);              circlePaint.setStyle(Paint.Style.STROKE);              canvas.drawCircle(mViewWidth / 2, mViewHeight / 2, mViewHeight / 2, circlePaint);              /**               * 绘制边界               */                mBorderPaint.setStrokeWidth(mBorderWidth/2);          canvas.drawCircle(mViewWidth/2,mViewHeight/2,mViewHeight/2-mBorderWidth/2,mBorderPaint);          }else {              /**               * 绘制矩形边框               */              canvas.drawRect(0,0,mViewWidth,mViewHeight,mBorderPaint);          }          //          handler.sendEmptyMessageDelayed(0,mTimeStep);      }        /**       * 设置进度  基准线       * @param mProgress       */      public void setmProgress(int mProgress) {          this.mProgress = mProgress;          mLevelLine=(100-mProgress)*mViewHeight/100;      }        /**       * 设置是否为圆形       * @param circle       */      public void setCircle(boolean circle) {          isCircle = circle;      }  }</code></pre>    <h3>自定义属性</h3>    <pre>  <code class="language-java"><?xml version="1.0" encoding="utf-8"?>  <resources>      <declare-styleable name="LD_WaveView">          <attr name="wave_color" format="color"></attr>          <attr name="wave_circle" format="boolean"></attr>      </declare-styleable>  </resources></code></pre>    <p>OK,没了,代码里备注应该还算比较清楚了,希望能对一些人有一些帮助,瑕疵不足之处欢迎指正,或者有好的建议。</p>    <p> </p>    <p>来自:http://www.jianshu.com/p/2384ccba5dc3</p>    <p> </p>