SurfaceView绘制录音波形图
本文简单记录由View绘制转为SurfaceView绘制的波形图问题.
上代码:
1 public class VoiceLineView extends View { 2 private final int LINE = 0; 3 private final int RECT = 1; 4 5 private int middleLineColor = Color.BLACK; 6 private int voiceLineColor = Color.BLACK; 7 private float middleLineHeight = 4; 8 private Paint paint; 9 private Paint paintVoicLine; 11 /** 12 * 灵敏度 13 */ 14 private int sensibility = 4; 15 16 private float maxVolume = 100; 17 18 19 private float translateX = 0; 20 private boolean isSet = false; 21 22 /** 23 * 振幅 24 */ 25 private float amplitude = 1; 26 /** 27 * 音量 28 */ 29 private float volume = 10; 30 private int fineness = 1; 31 private float targetVolume = 1; 32 33 34 private long speedY = 50; 35 private float rectWidth = 25; 36 private float rectSpace = 5; 37 private float rectInitHeight = 4; 38 private List<Rect> rectList; 39 40 private long lastTime = 0; 41 private int lineSpeed = 90; 42 43 List<Path> paths = null; 44 45 public VoiceLineView(Context context) { 46 super(context); 47 } 48 49 public VoiceLineView(Context context, AttributeSet attrs) { 50 super(context, attrs); 51 initAtts(context, attrs); 52 } 53 54 public VoiceLineView(Context context, AttributeSet attrs, int defStyleAttr) { 55 super(context, attrs, defStyleAttr); 56 initAtts(context, attrs); 57 } 58 59 private void initAtts(Context context, AttributeSet attrs) { 60 TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.voiceView); 61 mode = typedArray.getInt(R.styleable.voiceView_viewMode, 0); 62 voiceLineColor = typedArray.getColor(R.styleable.voiceView_voiceLine, Color.BLACK); 63 maxVolume = typedArray.getFloat(R.styleable.voiceView_maxVolume, 100); 64 sensibility = typedArray.getInt(R.styleable.voiceView_sensibility, 4); 65 if (mode == RECT) { 66 rectWidth = typedArray.getDimension(R.styleable.voiceView_rectWidth, 25); 67 rectSpace = typedArray.getDimension(R.styleable.voiceView_rectSpace, 5); 68 rectInitHeight = typedArray.getDimension(R.styleable.voiceView_rectInitHeight, 4); 69 } else { 70 middleLineColor = typedArray.getColor(R.styleable.voiceView_middleLine, Color.BLACK); 71 middleLineHeight = typedArray.getDimension(R.styleable.voiceView_middleLineHeight, 4); 72 lineSpeed = typedArray.getInt(R.styleable.voiceView_lineSpeed, 90); 73 fineness = typedArray.getInt(R.styleable.voiceView_fineness, 1); 74 paths = new ArrayList<>(20); 75 for (int i = 0; i < 20; i++) { 76 paths.add(new Path()); 77 } 78 } 79 typedArray.recycle(); 80 } 81 82 @Override 83 protected void onDraw(Canvas canvas) { 87 drawMiddleLine(canvas); 88 drawVoiceLine(canvas); 90 run(); 91 } 92 93 private void drawMiddleLine(Canvas canvas) { 94 if (paint == null) { 95 paint = new Paint(); 96 paint.setColor(middleLineColor); 97 paint.setAntiAlias(true); 98 } 99 canvas.save(); 100 canvas.drawRect(0, getHeight() / 2 - middleLineHeight / 2, getWidth(), getHeight() / 2 + middleLineHeight / 2, paint); 101 canvas.restore(); 102 } 103 104 private void drawVoiceLine(Canvas canvas) { 105 lineChange(); 106 if (paintVoicLine == null) { 107 paintVoicLine = new Paint(); 108 paintVoicLine.setColor(voiceLineColor); 109 paintVoicLine.setAntiAlias(true); 110 paintVoicLine.setStyle(Paint.Style.STROKE); 111 paintVoicLine.setStrokeWidth(2); 112 } 113 canvas.save(); 114 int moveY = getHeight() / 2; 115 for (int i = 0; i < paths.size(); i++) { 116 paths.get(i).reset(); 117 paths.get(i).moveTo(getWidth(), getHeight() / 2); 118 } 119 for (float i = getWidth() - 1; i >= 0; i -= fineness) { 120 amplitude = 4 * volume * i / getWidth() - 4 * volume * i * i / getWidth() / getWidth(); 121 for (int n = 1; n <= paths.size(); n++) { 122 float sin = amplitude * (float) Math.sin((i - Math.pow(1.22, n)) * Math.PI / 180 - translateX); 123 paths.get(n - 1).lineTo(i, (2 * n * sin / paths.size() - 15 * sin / paths.size() + moveY)); 124 } 125 } 126 for (int n = 0; n < paths.size(); n++) { 127 if (n == paths.size() - 1) { 128 paintVoicLine.setAlpha(255); 129 } else { 130 paintVoicLine.setAlpha(n * 130 / paths.size()); 131 } 132 if (paintVoicLine.getAlpha() > 0) { 133 canvas.drawPath(paths.get(n), paintVoicLine); 134 } 135 } 136 canvas.restore(); 137 } 138 139 167 168 public void setVolume(int volume) { 169 if (volume > maxVolume * sensibility / 25) { 170 isSet = true; 171 this.targetVolume = getHeight() * volume / 2 / maxVolume; 172 } 173 } 174 175 private void lineChange() { 176 if (lastTime == 0) { 177 lastTime = System.currentTimeMillis(); 178 translateX += 1.5; 179 } else { 180 if (System.currentTimeMillis() - lastTime > lineSpeed) { 181 lastTime = System.currentTimeMillis(); 182 translateX += 1.5; 183 } else { 184 return; 185 } 186 } 187 if (volume < targetVolume && isSet) { 188 volume += getHeight() / 30; 189 } else { 190 isSet = false; 191 if (volume <= 10) { 192 volume = 10; 193 } else { 194 if (volume < getHeight() / 30) { 195 volume -= getHeight() / 60; 196 } else { 197 volume -= getHeight() / 30; 198 } 199 } 200 } 201 } 202 221 public void run() {225 invalidate();227 } 228 229 }
上面是View的canvas实现的波形图绘制,cpu占用率比较高,在20%左右,一边录音一边绘制,手机发烫是最明显的感觉了.
下面我们换成SurfaceView绘制的
public class VoiceLineSurfaceView extends SurfaceView implements Runnable,SurfaceView.Callback { private final int LINE = 0; private final int RECT = 1; private int middleLineColor = Color.BLACK; private int voiceLineColor = Color.BLACK; private float middleLineHeight = 4; private Paint paint; private Paint paintVoicLine;/** * 灵敏度 */ private int sensibility = 4; private float maxVolume = 100; private float translateX = 0; private boolean isSet = false; /** * 振幅 */ private float amplitude = 1; /** * 音量 */ private float volume = 10; private int fineness = 1; private float targetVolume = 1; private long speedY = 50; private float rectWidth = 25; private float rectSpace = 5; private float rectInitHeight = 4; private List<Rect> rectList; private long lastTime = 0; private int lineSpeed = 90; private SurfaceHolder surfaceHolder; List<Path> paths = null; private boolean isWaveDrawing = false; private boolean isMiddleLineDrawing = true; private Thread thread; private Canvas canvas; public VoiceLineSurfaceView(Context context) { super(context); } public VoiceLineSurfaceView(Context context, AttributeSet attrs) { super(context, attrs); initAtts(context, attrs); } public VoiceLineSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initAtts(context, attrs); } private void initAtts(Context context, AttributeSet attrs) { TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.voiceView); mode = typedArray.getInt(R.styleable.voiceView_viewMode, 0); voiceLineColor = typedArray.getColor(R.styleable.voiceView_voiceLine, Color.BLACK); maxVolume = typedArray.getFloat(R.styleable.voiceView_maxVolume, 100); sensibility = typedArray.getInt(R.styleable.voiceView_sensibility, 4); middleLineColor = typedArray.getColor(R.styleable.voiceView_middleLine, Color.BLACK); middleLineHeight = typedArray.getDimension(R.styleable.voiceView_middleLineHeight, 4); lineSpeed = typedArray.getInt(R.styleable.voiceView_lineSpeed, 90); fineness = typedArray.getInt(R.styleable.voiceView_fineness, 1); paths = new ArrayList<>(20); for (int i = 0; i < 20; i++) { paths.add(new Path()); } typedArray.recycle(); setZOrderOnTop(true); getHolder().setFormat(PixelFormat.TRANSLUENT); surfaceHolder = getHolder(); surfaceHolder.addCallback(this); } private void drawMiddleLine(Canvas canvas) { if (paint == null) { paint = new Paint(); paint.setColor(middleLineColor); paint.setAntiAlias(true); } canvas.save(); canvas.drawRect(0, getHeight() / 2 - middleLineHeight / 2, getWidth(), getHeight() / 2 + middleLineHeight / 2, paint); canvas.restore(); } private void drawVoiceLine(Canvas canvas) { lineChange(); if (paintVoicLine == null) { paintVoicLine = new Paint(); paintVoicLine.setColor(voiceLineColor); paintVoicLine.setAntiAlias(true); paintVoicLine.setStyle(Paint.Style.STROKE); paintVoicLine.setStrokeWidth(2); } canvas.save(); int moveY = getHeight() / 2; for (int i = 0; i < paths.size(); i++) { paths.get(i).reset(); paths.get(i).moveTo(getWidth(), getHeight() / 2); } for (float i = getWidth() - 1; i >= 0; i -= fineness) { amplitude = 4 * volume * i / getWidth() - 4 * volume * i * i / getWidth() / getWidth(); for (int n = 1; n <= paths.size(); n++) { float sin = amplitude * (float) Math.sin((i - Math.pow(1.22, n)) * Math.PI / 180 - translateX); paths.get(n - 1).lineTo(i, (2 * n * sin / paths.size() - 15 * sin / paths.size() + moveY)); } } for (int n = 0; n < paths.size(); n++) { if (n == paths.size() - 1) { paintVoicLine.setAlpha(255); } else { paintVoicLine.setAlpha(n * 130 / paths.size()); } if (paintVoicLine.getAlpha() > 0) { canvas.drawPath(paths.get(n), paintVoicLine); } } canvas.restore(); } public void setVolume(int volume) { if (volume > maxVolume * sensibility / 25) { isSet = true; this.targetVolume = getHeight() * volume / 2 / maxVolume; } } private void lineChange() { if (lastTime == 0) { lastTime = System.currentTimeMillis(); translateX += 1.5; } else { if (System.currentTimeMillis() - lastTime > lineSpeed) { lastTime = System.currentTimeMillis(); translateX += 1.5; } else { return; } } if (volume < targetVolume && isSet) { volume += getHeight() / 30; } else { isSet = false; if (volume <= 10) { volume = 10; } else { if (volume < getHeight() / 30) { volume -= getHeight() / 60; } else { volume -= getHeight() / 30; } } } } @Override public void surfaceCreated(){ } @Override public void surfaceChanged(){ isMiddleLineDrawing = true; thread = new Thread(this); thread.start(); } @Override public void surfaceDestroyed(){ isMiddleLineDrawing = false; } @Override public void run() { while(isMiddleLineDrawing){ canvas = surfaceHolder.lockCanvas(); if(canvas==null){ return; } canvas.drawColor(Color.TRANSLUENT,PorterDuff.Mode.CLEAR); if(isWaveDrawing){ drawVoiceLine(canvas); } drawMiddleLine(canvas); surfaceHolder.unlockCanvasAndPost(canvas); } } public void setDrawing(boolean drawing){ isMiddleLineDrawing = true; isWaveDrawing = drawing; } }