Android Bezier curve practice -- wave motion

I. wave effect is as follows

There are many cases of Bessel curve custom wave effect, and the same method is very simple. Most of them use quadratic Bessel curve to achieve the same as this case. There is also a path measure method. Here we will add later, let's see the implementation of Bessel curve first.

 

II. Code implementation

Difficulties in this case:

① for the movement of the oscillogram, we need to draw a complete waveform in the negative x direction of the coordinate point. When the waveform moves to 0 point, it will automatically recover from the original position.

② gradient implementation. Here, LinearGradient is used, mainly for gradient direction. When the starting horizontal axis is 0 coordinate and the vertical axis is not 0, the gradient direction is vertical. For reference LinearGradient in Android >In.

The code is as follows:


public class WaveView extends View {

    private TextPaint mPaint;

    private float dx = 0;

    private float mfactory = 5f/4;  //Wave factor, used to determine the wavelength

    public WaveView(Context context) {
        this(context,null);
    }

    public WaveView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public WaveView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint();
    }

    private void initPaint() {
        // Instantiate brush and turn on antialiasing
        mPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG );
        mPaint.setAntiAlias(true);
        mPaint.setPathEffect(new CornerPathEffect(10)); //Set line type
        mPaint.setStrokeWidth(dip2px(1));
        mPaint.setTextSize(dip2px((12)));
        mPaint.setStyle(Paint.Style.STROKE);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);

        if(widthMode!=MeasureSpec.EXACTLY){
            width = (int) dip2px(300);
        }
        if(heightMode!=MeasureSpec.EXACTLY){
            height = (int) dip2px(100);
        }
        setMeasuredDimension(width,height);

    }

    public float dip2px(int dp){
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dp,getResources().getDisplayMetrics());
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if(getWidth()<1 || getHeight()<1) return;

        int saveCount = canvas.save();
        canvas.translate(0,getHeight()/2);
        drawWave(canvas);
        canvas.restoreToCount(saveCount);

    }


    private void drawWave(Canvas canvas){
        float waveLength = getWidth()*mfactory; //wavelength
        float top = -getHeight()/2f;
        float bottom = getHeight()/2f;

        int N = (int) Math.ceil(getWidth()/waveLength);
        //Maximum integer indicating the number of waves that can be accommodated in the view width

        if(N==0) return;
        Path path = new Path();
        for (int i=-1;i<N;i++) {
            buildWavePath(path,i, waveLength, top, bottom);
        }

        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(Color.CYAN);
        canvas.drawPath(path,mPaint);

        float startX = waveLength * (-1) + dx;
        float endX = waveLength  * N + dx;

        path.lineTo(endX,bottom);
        path.lineTo(startX,bottom);
        path.close();
        mPaint.setStyle(Paint.Style.FILL);
        LinearGradient linearGradient = new LinearGradient(0,top,0,bottom,new int[]{Color.BLUE,Color.CYAN},new float[]{0,0.9f}, Shader.TileMode.CLAMP);
        Shader shader = mPaint.getShader();

        mPaint.setShader(linearGradient);
        canvas.drawPath(path,mPaint);
        mPaint.setShader(shader);

    }

    private void buildWavePath(Path path,int ith,float waveLength,float top,float bottom) {

        float offsetLeft = waveLength * ith + dx;
        if(ith==-1) {
            path.moveTo(offsetLeft, 0);
        }
        path.quadTo(offsetLeft+waveLength/4f,top,offsetLeft+waveLength/2f,0);
        path.quadTo(offsetLeft+waveLength*3f/4f,bottom,offsetLeft+waveLength,0);

    }

    public void startAnim(){
        ValueAnimator animator = ValueAnimator.ofFloat(0,getWidth()*mfactory);
        animator.setDuration(2000);
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.setInterpolator(new LinearInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                dx = (float) animation.getAnimatedValue();
                postInvalidate();
            }
        });
        animator.start();
    }


}

Tags: Mobile Android

Posted on Tue, 03 Dec 2019 00:09:29 -0500 by timus