Android 绘制波浪曲线1_第1张图片

import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Path;import android.graphics.PixelFormat;import android.graphics.PorterDuff;import android.util.AttributeSet;import android.util.SparseArray;import java.util.ArrayList;import java.util.List;public class WaveLineView extends RenderView {    private final static int DEFAULT_SAMPLING_SIZE = 64;    private final static float DEFAULT_OFFSET_SPEED = 250F;    private final static int DEFAULT_SENSIBILITY = 5;    //采样点的数量,越高越精细,但是高于一定限度肉眼很难分辨,越高绘制效率越低    private int samplingSize;    //控制向右偏移速度,越小偏移速度越快    private float offsetSpeed;    //平滑改变的音量值    private float volume = 0;    //用户设置的音量,[0,100]    private int targetVolume = 50;    //每次平滑改变的音量单元    private float perVolume;    //灵敏度,越大越灵敏[1,10]    private int sensibility;    //背景色    private int backGroundColor = Color.WHITE;    //波浪线颜色    private int lineColor;    //粗线宽度    private int thickLineWidth;    //细线宽度    private int fineLineWidth;    private final Paint paint = new Paint();    {        //防抖动        paint.setDither(true);        //抗锯齿,降低分辨率,提高绘制效率        paint.setAntiAlias(true);    }    private List paths = new ArrayList<>();    {        for (int i = 0; i < 4; i++) {            paths.add(new Path());        }    }    //不同函数曲线系数    private float[] pathFuncs = {            0.6f, 0.35f, 0.1f, -0.1f    };    //采样点X坐标    private float[] samplingX;    //采样点位置映射到[-2,2]之间    private float[] mapX;    //画布宽高    private int width, height;    //画布中心的高度    private int centerHeight;    //振幅    private float amplitude;    //存储衰变系数    private SparseArray recessionFuncs = new SparseArray<>();    //连线动画结束标记    private boolean isPrepareLineAnimEnd = false;    //连线动画位移    private int lineAnimX = 0;    //渐入动画结束标记    private boolean isPrepareAlphaAnimEnd = false;    //渐入动画百分比值[0,1f]    private float prepareAlpha = 0f;    //是否开启准备动画    private boolean isOpenPrepareAnim = false;    private boolean isTransparentMode = false;    public WaveLineView(Context context) {        this(context, null);    }    public WaveLineView(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public WaveLineView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        initAttr(attrs);    }    private void initAttr(AttributeSet attrs) {        TypedArray t = getContext().obtainStyledAttributes(attrs, R.styleable.WaveLineView);        backGroundColor = t.getColor(R.styleable.WaveLineView_wlvBackgroundColor, Color.TRANSPARENT);        samplingSize = t.getInt(R.styleable.WaveLineView_wlvSamplingSize, DEFAULT_SAMPLING_SIZE);        lineColor = t.getColor(R.styleable.WaveLineView_wlvLineColor, Color.parseColor("#2ED184"));        thickLineWidth = (int) t.getDimension(R.styleable.WaveLineView_wlvThickLineWidth, 6);        fineLineWidth = (int) t.getDimension(R.styleable.WaveLineView_wlvFineLineWidth, 2);        offsetSpeed = t.getFloat(R.styleable.WaveLineView_wlvMoveSpeed, DEFAULT_OFFSET_SPEED);        sensibility = t.getInt(R.styleable.WaveLineView_wlvSensibility, DEFAULT_SENSIBILITY);        isTransparentMode = backGroundColor == Color.TRANSPARENT;        t.recycle();        checkVolumeValue();        checkSensibilityValue();        //将RenderView放到最顶层        setZOrderOnTop(true);        if (getHolder() != null) {            //使窗口支持透明度            getHolder().setFormat(PixelFormat.TRANSLUCENT);        }    }    @Override    protected void doDrawBackground(Canvas canvas) {        //绘制背景        if (isTransparentMode) {            //启用CLEAR模式,所绘制内容不会提交到画布上。            canvas.drawColor(backGroundColor, PorterDuff.Mode.CLEAR);        } else {            canvas.drawColor(backGroundColor);        }    }    private boolean isParametersNull() {        if (null == samplingX || null == mapX || null == pathFuncs) {            return true;        }        return false;    }    @Override    protected void onRender(Canvas canvas, long millisPassed) {        float offset = millisPassed / offsetSpeed;        if (isParametersNull()) {            initDraw(canvas);        }        if (lineAnim(canvas)) {            resetPaths();            softerChangeVolume();            //波形函数的值            float curY;            for (int i = 0; i <= samplingSize; i++) {                //双重判断确保必要参数正常                if (isParametersNull()) {                    initDraw(canvas);                    if (isParametersNull()) {                        return;                    }                }                float x = samplingX[i];                curY = (float) (amplitude * calcValue(mapX[i], offset));                for (int n = 0; n < paths.size(); n++) {                    //四条线分别乘以不同的函数系数                    float realY = curY * pathFuncs[n] * volume * 0.01f;                    paths.get(n).lineTo(x, centerHeight + realY);                }            }            //连线至终点            for (int i = 0; i < paths.size(); i++) {                paths.get(i).moveTo(width, centerHeight);            }            //绘制曲线            for (int n = 0; n < paths.size(); n++) {                if (n == 0) {                    paint.setStrokeWidth(thickLineWidth);                    paint.setAlpha((int) (255 * alphaInAnim()));                } else {                    paint.setStrokeWidth(fineLineWidth);                    paint.setAlpha((int) (100 * alphaInAnim()));                }                canvas.drawPath(paths.get(n), paint);            }        }    }    //检查音量是否合法    private void checkVolumeValue() {        if (targetVolume > 100) targetVolume = 100;    }    //检查灵敏度值是否合法    private void checkSensibilityValue() {        if (sensibility > 10) sensibility = 10;        if (sensibility < 1) sensibility = 1;    }    /**     * 使曲线振幅有较大改变时动画过渡自然     */    private void softerChangeVolume() {        //这里减去perVolume是为了防止volume频繁在targetVolume上下抖动        if (volume < targetVolume - perVolume) {            volume += perVolume;        } else if (volume > targetVolume + perVolume) {            if (volume < perVolume * 2) {                volume = perVolume * 2;            } else {                volume -= perVolume;            }        } else {            volume = targetVolume;        }    }    /**     * 渐入动画     *     * @return progress of animation     */    private float alphaInAnim() {        if (!isOpenPrepareAnim) return 1;        if (prepareAlpha < 1f) {            prepareAlpha += 0.02f;        } else {            prepareAlpha = 1;        }        return prepareAlpha;    }    /**     * 连线动画     *     * @param canvas     * @return whether animation is end     */    private boolean lineAnim(Canvas canvas) {        if (isPrepareLineAnimEnd || !isOpenPrepareAnim) return true;        paths.get(0).moveTo(0, centerHeight);        paths.get(1).moveTo(width, centerHeight);        for (int i = 1; i <= samplingSize; i++) {            float x = 1f * i * lineAnimX / samplingSize;            paths.get(0).lineTo(x, centerHeight);            paths.get(1).lineTo(width - x, centerHeight);        }        paths.get(0).moveTo(width / 2f, centerHeight);        paths.get(1).moveTo(width / 2f, centerHeight);        lineAnimX += width / 60;        canvas.drawPath(paths.get(0), paint);        canvas.drawPath(paths.get(1), paint);        if (lineAnimX > width / 2) {            isPrepareLineAnimEnd = true;            return true;        }        return false;    }    /**     * 重置path     */    private void resetPaths() {        for (int i = 0; i < paths.size(); i++) {            paths.get(i).rewind();            paths.get(i).moveTo(0, centerHeight);        }    }    //初始化参数    private void initParameters() {        lineAnimX = 0;        prepareAlpha = 0f;        isPrepareLineAnimEnd = false;        isPrepareAlphaAnimEnd = false;        samplingX = null;    }    @Override    public void startAnim() {        initParameters();        super.startAnim();    }    @Override    public void stopAnim() {        super.stopAnim();        clearDraw();    }    //清空画布所有内容    public void clearDraw() {        Canvas canvas = null;        try {            canvas = getHolder().lockCanvas(null);            canvas.drawColor(backGroundColor);            resetPaths();            for (int i = 0; i < paths.size(); i++) {                canvas.drawPath(paths.get(i), paint);            }        } catch (Exception e) {        } finally {            if (canvas != null) {                getHolder().unlockCanvasAndPost(canvas);            }        }    }    //初始化绘制参数    private void initDraw(Canvas canvas) {        width = canvas.getWidth();        height = canvas.getHeight();        if (width == 0 || height == 0 || samplingSize == 0) return;        centerHeight = height >> 1;        //振幅为高度的1/4        amplitude = height / 3.0f;        //适合View的理论最大音量值,和音量不属于同一概念        perVolume = sensibility * 0.35f;        //初始化采样点及映射        //这里因为包括起点和终点,所以需要+1        samplingX = new float[samplingSize + 1];        mapX = new float[samplingSize + 1];        //确定采样点之间的间距        float gap = width / (float) samplingSize;        //采样点的位置        float x;        for (int i = 0; i <= samplingSize; i++) {            x = i * gap;            samplingX[i] = x;            //将采样点映射到[-2,2]            mapX[i] = (x / (float) width) * 4 - 2;        }        paint.setStyle(Paint.Style.STROKE);        paint.setColor(lineColor);        paint.setStrokeWidth(thickLineWidth);    }    /**     * 计算波形函数中x对应的y值     * 

* 使用稀疏矩阵进行暂存计算好的衰减系数值,下次使用时直接查找,减少计算量 * * @param mapX 换算到[-2,2]之间的x值 * @param offset 偏移量 * @return [-1, 1] */ private double calcValue(float mapX, float offset) { int keyX = (int) (mapX * 1000); offset %= 2; double sinFunc = Math.sin(Math.PI * mapX - offset * Math.PI); double recessionFunc; if (recessionFuncs.indexOfKey(keyX) >= 0) { recessionFunc = recessionFuncs.get(keyX); } else { recessionFunc = 4 / (4 + Math.pow(mapX, 4)); recessionFuncs.put(keyX, recessionFunc); } return sinFunc * recessionFunc; } /** * the wave line animation move speed from left to right * you can use negative number to make the animation from right to left * the default value is 290F,the smaller, the faster * * @param moveSpeed */ public void setMoveSpeed(float moveSpeed) { this.offsetSpeed = moveSpeed; } /** * User set volume, [0,100] * * @param volume */ public void setVolume(int volume) { if (Math.abs(targetVolume - volume) > perVolume) { this.targetVolume = volume; checkVolumeValue(); } } public void setBackGroundColor(int backGroundColor) { this.backGroundColor = backGroundColor; this.isTransparentMode = (backGroundColor == Color.TRANSPARENT); } public void setLineColor(int lineColor) { this.lineColor = lineColor; } /** * Sensitivity, the bigger the more sensitive [1,10] * the default value is 5 * * @param sensibility */ public void setSensibility(int sensibility) { this.sensibility = sensibility; checkSensibilityValue(); }}

import android.content.Context;import android.graphics.Canvas;import android.util.AttributeSet;import android.view.SurfaceHolder;import android.view.SurfaceView;import java.lang.ref.WeakReference;import java.util.List;public abstract class RenderView extends SurfaceView implements SurfaceHolder.Callback {    //是否正在绘制动画    private boolean isStartAnim = false;    private final static Object surfaceLock = new Object();    private RenderThread renderThread;    /**     * 绘制背景,防止开始时黑屏     * 子View可以执行此方法     *     * @param canvas     */    protected abstract void doDrawBackground(Canvas canvas);    /**     * 渲染surfaceView的回调方法。     *     * @param canvas 画布     */    protected abstract void onRender(Canvas canvas, long millisPassed);    public RenderView(Context context) {        this(context, null);    }    public RenderView(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public RenderView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        getHolder().addCallback(this);    }    /*回调/线程*/    private static class RenderThread extends Thread {        private static final long SLEEP_TIME = 16;        private WeakReference renderView;        private boolean running = false;        private boolean destoryed = false;        private boolean isPause = false;        public RenderThread(RenderView renderView) {            super("RenderThread");            this.renderView = new WeakReference<>(renderView);        }        private SurfaceHolder getSurfaceHolder() {            if (getRenderView() != null) {                return getRenderView().getHolder();            }            return null;        }        private RenderView getRenderView() {            return renderView.get();        }        @Override        public void run() {            long startAt = System.currentTimeMillis();            while (!destoryed) {                synchronized (surfaceLock) {                    //这里并没有真正的结束Thread,防止部分手机连续调用同一Thread出错                    while (isPause) {                        try {                            surfaceLock.wait();                        } catch (InterruptedException e) {                            e.printStackTrace();                        }                    }                    if (running) {                        if (getSurfaceHolder() != null && getRenderView() != null) {                            Canvas canvas = getSurfaceHolder().lockCanvas();                            if (canvas != null) {                                getRenderView().doDrawBackground(canvas);                                if (getRenderView().isStartAnim) {                                    getRenderView().render(canvas, System.currentTimeMillis() - startAt);  //这里做真正绘制的事情                                }                                getSurfaceHolder().unlockCanvasAndPost(canvas);                            }                        } else {                            running = false;                        }                    }                }//                try {//                    Thread.sleep(SLEEP_TIME);//                } catch (InterruptedException e) {//                    e.printStackTrace();//                }            }        }        public void setRun(boolean isRun) {            this.running = isRun;        }    }    @Override    public void surfaceCreated(SurfaceHolder holder) {        renderer = onCreateRenderer();        if (renderer != null && renderer.isEmpty()) {            throw new IllegalStateException();        }        renderThread = new RenderThread(this);    }    /**     * 解锁暂停,继续执行绘制任务     * 默认当Resume时不自动启动动画     */    public void onResume() {        synchronized (surfaceLock) {            if (renderThread != null) {                renderThread.isPause = false;                surfaceLock.notifyAll();            }        }    }    //假暂停,并没有结束Thread    public void onPause() {        synchronized (surfaceLock) {            if (renderThread != null) {                renderThread.isPause = true;            }        }    }    @Override    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {        //这里可以获取SurfaceView的宽高等信息    }    @Override    public void surfaceDestroyed(SurfaceHolder holder) {        synchronized (surfaceLock) {  //这里需要加锁,否则doDraw中有可能会crash            renderThread.setRun(false);            renderThread.destoryed = true;        }    }    @Override    public void onWindowFocusChanged(boolean hasFocus) {        if (hasFocus && isStartAnim) {            startAnim();        } else {            startThread();        }    }    /*绘图*/    public interface IRenderer {        void onRender(Canvas canvas, long millisPassed);    }    private List renderer;    protected List onCreateRenderer() {        return null;    }    private void render(Canvas canvas, long millisPassed) {        if (renderer != null) {            for (int i = 0, size = renderer.size(); i < size; i++) {                renderer.get(i).onRender(canvas, millisPassed);            }        } else {            onRender(canvas, millisPassed);        }    }    public void startAnim() {        isStartAnim = true;        startThread();    }    private void startThread() {        if (renderThread != null && !renderThread.running) {            renderThread.setRun(true);            try {                if (renderThread.getState() == Thread.State.NEW) {                    renderThread.start();                }            } catch (Exception e) {                e.printStackTrace();            }        }    }    public void stopAnim() {        isStartAnim = false;        if (renderThread != null && renderThread.running) {            renderThread.setRun(false);            renderThread.interrupt();        }    }    public boolean isRunning() {        if (renderThread != null) {            return renderThread.running;        }        return false;    }    //释放相关资源,防止内存泄漏    public void release() {        if (getHolder() != null && getHolder().getSurface() != null) {            getHolder().getSurface().release();            getHolder().removeCallback(this);        }    }}
                                                                

更多相关文章

  1. Android 动画 Animation
  2. android 柱状图(带动画的)
  3. android:编写属性动画程序(旋转,缩放,淡出淡入)
  4. Android 监听系统音量
  5. Android 4种补间动画基础使用。
  6. Android ViewPager动画第三方库(MagicViewPager)
  7. Android 动画框架代码分析
  8. Android Animation动画(Frame-By-Frame Animations 、Tween Anima
  9. 【Android】【Lottie】在Android中使用Lottie动画

随机推荐

  1. Android屏蔽/禁止ViewPager左右滑动/滚动
  2. Android(安卓)SMS相关操作
  3. imageView 的 android:maxHeight,maxWidt
  4. Android(安卓)AppWidget系统框架
  5. Mac Yosemite下Android(安卓)Studio环境
  6. android HTTPURLConnection解决不能访问H
  7. Android调用系统摄像头拍照并剪裁压缩
  8. Android(安卓)百分比布局
  9. linux安装安卓开发工具android studio
  10. ANDROID ADB工具使用