AndroidAndroid开发自定义控件

android 波浪进度WaveView

2016-12-27  本文已影响2455人  知识酷教育大东

刚好有注意到百度外卖以及淘宝个人中心,都用到了类似水波起伏的效果,于是就参照网上的资料然后自己整改,自定义了一个waveView ,原理么,首先就是自定义个WaveView 继承View,然后再WaveView 内部实现代码逻辑:
<h4>① 水波就波嘛? sin函数? 贝塞尔曲线? 都行,这里就用二阶贝塞 尔曲线去画吧
② 波要动嘛,怎么动呢?线程? 好吧 这里用了个Handler。
③绘制波首先要找点,那么在onMeasure()里找出需要的点咯,这里就暂时展示一个波段吧,一个波长移动左边不就没了?OK 那就两个波吧,吼吼,两个波(猥琐男潜质表露无遗啊)。接下来就是Handler 结合 onDraw()绘制。OK,那就先看我Word绘制的粗瘪的波动图,请看VCR,oh,no... gif

wave.gif

</h3>
意思就是波平移一个波长之后回到初始位置继续平移循环。
好吧,有人说了,这么简单的逻辑你要啰嗦那么多???
好吧,我承认,我有唐僧的潜质。。。
闲话就不说了,先上
<h3>效果图</h3>

waveprogress.gif

<h3>调用的Activity</h3>

 * 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);
    }
}```
<h3>xml布局</h3>

<?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>```
<h3>自定义WaveView</h3>

/**
 * 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;
    }
}

<h3>自定义属性</h3>

<?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>```
OK,没了,代码里备注应该还算比较清楚了,希望能对一些人有一些帮助,瑕疵不足之处欢迎指正,或者有好的建议。
上一篇 下一篇

猜你喜欢

热点阅读