目录
实践过程
SurfaceView属性和方法
- surfaceCreated(@NonNull SurfaceHolder holder):surface创建时回调
- surfaceDestroyed(@NonNull SurfaceHolder holder):surface销毁时回调
- surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height):surface发生变化时回调
- SurfaceHolder.addCallback(context):添加回调,也就是上面三个方法
- lockCanvas():获取Canvas对象并锁定画布,调用Canvas进行绘图,和unlockCanvasAndPost是依次成对出现。
- unlockCanvasAndPost():结束锁定画布,并且提交改变。和lockCanvas是依次成对出现。
TextureView属性和方法
- getSurfaceTexture():此方法返回此视图使用的 SurfaceTexture。
- getBitmap(整型宽度,整型高度):此方法返回返回关联表面纹理内容的位图表示形式。
- getTransform(Matrix transform):此方法返回与此纹理视图关联的转换。
- isOpaque():此方法指示此视图是否不透明。
- lockCanvas():此方法开始编辑曲面中的像素。
- setOpaque(boolean opaque):此方法指示此纹理视图的内容是否不透明。
- setTransform(Matrix transform):此方法将转换设置为与此纹理视图关联。
- unlockCanvasAndPost(Canvas canvas):此方法完成对曲面中像素的编辑。
- onSurfaceTextureAvailable(SurfaceTexture arg0, int arg1, int arg2):创建的监听,前提开启硬件加速
- onSurfaceTextureDestroyed(SurfaceTexture arg0):销毁的监听
- onSurfaceTextureSizeChanged(SurfaceTexture arg0, int arg1,int arg2):变化的监听
- onSurfaceTextureUpdated(SurfaceTexture arg0):更新的监听
TextureView示例
public class MainActivity extends Activity implements SurfaceTextureListener{ private Camera mCamera; private TextureView [mTextureView](); protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mTextureView = new TextureView(this); mTextureView.setSurfaceTextureListener(this); setContentView(mTextureView); } public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { mCamera = Camera.open(); try { mCamera.setPreviewTexture(surface); mCamera.startPreview(); //可以修改透明度和旋转方向 mTextureView.setAlpha(1.0f); mTextureView.setRotation(90.0f); } catch (IOException ioe) { // 异常处理 } } public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { } public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { mCamera.stopPreview(); mCamera.release(); return true; } public void onSurfaceTextureUpdated(SurfaceTexture surface) { } }
SurfaceView示例
Java版本自定义
public class ECGSurfaceViewJava extends SurfaceView implements SurfaceHolder.Callback { //简单模拟一下数据 public List<Integer> ecgDatas = new ArrayList<>(); //矩阵 画布 画笔 颜色 private Rect rect; private Canvas mCanvas; private Paint mPaint; //画波形的画笔 private String line_color = "#01FC00"; //画笔默认是绿色的 private int wave_speed = 30;//定义波速:30mm/s private int sleepTime = 8;//每次锁屏的时间间距,单位ms 连线的时间长度,,如果大则会卡顿效果 private SurfaceHolder surfaceHolder; private boolean isCanRun = true; private int mStartX = 0; private int mStartY = 0; private Runnable drawRunnable = new Runnable() { @Override public void run() { while (isCanRun) { //在这获取改view的宽度 如果mStartX超过则归位 这样实现反复从头画,我这800只是简单表示下效果 if (mStartX > 800) { mStartX = 0; } //绘制区域不断向右变化 rect.set(mStartX, 0, mStartX + 16, 300); mCanvas = surfaceHolder.lockCanvas(rect); //提交绘制区域 if (mCanvas == null) return; //很重要,如果反复从头绘制,会覆盖上一次的绘制 mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); if (ecgDatas.size() > 0) { mCanvas.drawLine(mStartX, mStartY, mStartX + 16, ecgDatas.get(0), mPaint); //这个点的重点是下一个点的起点 mStartX = mStartX + 16; mStartY = ecgDatas.get(0); ecgDatas.remove(0); } else { initData(); } surfaceHolder.unlockCanvasAndPost(mCanvas); //这种方式把上次绘制的遮盖了 因此出现了断点 } } }; public ECGSurfaceViewJava(Context context, AttributeSet attrs) { super(context, attrs); this.surfaceHolder = this.getHolder();//获取holder this.surfaceHolder.addCallback(this); rect = new Rect(); //绘制矩阵内 mPaint = new Paint(); mPaint.setColor(Color.parseColor(line_color)); //画笔颜色 mPaint.setAntiAlias(true); //抗锯齿 mPaint.setStrokeWidth(2); initData(); } private void initData() { ecgDatas.clear(); for (int i = 0; i < 200; i++) { ecgDatas.add((int) (Math.random() * 200)); } mStartY = ecgDatas.get(0); } @Override public void surfaceCreated(SurfaceHolder holder) { new Thread(drawRunnable).start(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { isCanRun = false; } }
Kotlin版本自定义
class ECGSurfaceViewKotlin(context: Context?, attrs: AttributeSet?) : SurfaceView(context, attrs), SurfaceHolder.Callback { //简单模拟一下数据 var ecgDatas: MutableList<Int> = ArrayList() //矩阵 画布 画笔 颜色 private var rect: Rect? = null private var mCanvas: Canvas? = null //画波形的画笔 private var mPaint: Paint? = null private val line_color = "#01FC00" //画笔默认是绿色的 private val wave_speed = 30 //定义波速:30mm/s private val sleepTime = 8 //每次锁屏的时间间距,单位ms 连线的时间长度,,如果大则会卡顿效果 var surfaceHolder: SurfaceHolder? = null private var isCanRun = true private var mStartX = 0 private var mStartY = 0 private val drawRunnable = Runnable { while (isCanRun) { //在这获取改view的宽度 如果mStartX超过则归位 这样实现反复从头画,我这800只是简单表示下效果 if (mStartX > 800) { mStartX = 0 } //绘制区域不断向右变化 rect!![mStartX, 0, mStartX + 16] = 300 mCanvas = surfaceHolder!!.lockCanvas(rect) //提交绘制区域 if (mCanvas == null) return@Runnable //很重要,如果反复从头绘制,会覆盖上一次的绘制 mCanvas!!.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR) if (ecgDatas.size > 0) { mCanvas!!.drawLine(mStartX.toFloat(), mStartY.toFloat(), (mStartX + 16).toFloat(), ecgDatas[0].toFloat(), mPaint) //这个点的重点是下一个点的起点 mStartX = mStartX + 16 mStartY = ecgDatas[0] ecgDatas.removeAt(0) } else { initData() } surfaceHolder!!.unlockCanvasAndPost(mCanvas!!) //这种方式把上次绘制的遮盖了 因此出现了断点 } } init { surfaceHolder = this.holder //获取holder surfaceHolder!!.addCallback(this) rect = Rect() //绘制矩阵内 mPaint = Paint() mPaint!!.setColor(Color.parseColor(line_color)) //画笔颜色 mPaint!!.setAntiAlias(true) //抗锯齿 mPaint!!.setStrokeWidth(2f) initData() } private fun initData() { ecgDatas.clear() for (i in 0..199) { ecgDatas.add((Math.random() * 200).toInt()) } mStartY = ecgDatas[0] } override fun surfaceCreated(holder: SurfaceHolder?) { Thread(drawRunnable).start() } override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) { } override fun surfaceDestroyed(holder: SurfaceHolder?) { isCanRun = false } }
布局直接使用即可:
<cn.appstudy.customView.ECGSurfaceViewJava android:layout_width="match_parent" android:layout_height="200dp"/> <cn.appstudy.customView.ECGSurfaceViewKotlin android:layout_width="match_parent" android:layout_height="200dp" android:layout_marginTop="220dp" />
上面小空只是简单写了下示例,如果是画心电图其实还有更多的逻辑,比如多个心电图同步,比如超出屏幕后回到起始位置,比如实时更新心电图数据,再比如心电图速度控制等等。
而且上面心电图示例是从左到右的,还有可能从上到下,从右到左的等等,更多功能就交给大佬们去开发了。