Android之SurfaceView

本来这个SurfaceView没什么好写的。仅仅是发现网络上很多SurfaceView的教程代码不全,入门者可能会感到困惑,因为不知道谁应该放在哪里。所以这里力求无论新手熟手,都能从源码中得到全部信息,权当是一个补充。

第一步

首先自己先建一个类,我这里叫MySurfaceView,完整的源码如下(参考了【1】,但修复了其中退出时会引发黑屏的一个小bug,另外多说一句,里面采用新建线程的方法不要学,线程能不建就尽量不要建;对于while循环,一般在有外来信息时加入判断条件,及时中止即可,不过这与本文主旨无关)。

package com.spacesoftwares.ssapplication;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;


/**
 * Created by Administrator on 2018/7/19 0019.
 */

public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
    // SurfaceHolder
    private SurfaceHolder mSurfaceHolder;
    // 画布
    private Canvas mCanvas;
    // 子线程标志位
    private boolean isDrawing;
    // 画笔
    Paint mPaint;
    // 路径
    Path mPath;
    private float mLastX, mLastY;//上次的坐标

    public MySurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    /**
     * 初始化
     */
    private void init() {
        //初始化 SurfaceHolder mSurfaceHolder
        mSurfaceHolder = getHolder();
        mSurfaceHolder.addCallback(this);

        setFocusable(true);
        setFocusableInTouchMode(true);
        this.setKeepScreenOn(true);
        //画笔
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
        mPaint.setStrokeWidth(10f);
        mPaint.setColor(Color.parseColor("#FF4081"));
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        //路径
        mPath = new Path();
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {//创建
        isDrawing = true;
        Log.e("surfaceCreated","--"+isDrawing);
        //绘制线程
        new Thread(this).start();
    }


    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {//改变

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {//销毁
        isDrawing = false;
        Log.e("surfaceDestroyed","--"+isDrawing);
    }

    @Override
    public void run() {
        while (isDrawing){
            drawing();
        }
    }

    /**
     * 绘制
     */
    private void drawing() {
        try {
            mCanvas = mSurfaceHolder.lockCanvas();
            if(null == mCanvas){
                return;
            }
            mCanvas.drawColor(Color.YELLOW);
            mCanvas.drawPath(mPath,mPaint);
        } finally {
            if (mCanvas != null) {
                mSurfaceHolder.unlockCanvasAndPost(mCanvas);
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:

                mLastX = x;
                mLastY = y;
                mPath.moveTo(mLastX, mLastY);
                break;
            case MotionEvent.ACTION_MOVE:
                float dx = Math.abs(x - mLastX);
                float dy = Math.abs(y - mLastY);
                if (dx >= 3 || dy >= 3) {
                    mPath.quadTo(mLastX, mLastY, (mLastX + x) / 2, (mLastY + y) / 2);
                }
                mLastX = x;
                mLastY = y;
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        return true;
    }

    /**
     * 测量
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int wSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int wSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int hSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int hSpecSize = MeasureSpec.getSize(heightMeasureSpec);

        if (wSpecMode == MeasureSpec.AT_MOST && hSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(300, 300);
        } else if (wSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(300, hSpecSize);
        } else if (hSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(wSpecSize, 300);
        }
    }
}

其中drawing函数中 if(null == mCanvas){  return;  }这句是必须的,否则退出时程序可能会崩溃,因此此时从surfaceHolder中得到的canvas一定是空的。报错内容大概和这个差不多:

java.lang.NullPointerException: Attempt to invoke virtual method 'void android.graphics.Canvas.drawColor(int)' on a null object reference....bla bla bla...。

第二步 SurfaceViewActivity

SurfaceViewAcitivity源码如下

package com.spacesoftwares.ssapplication;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.SurfaceView;

public class SurfaceViewActivity extends AppCompatActivity {

    private MySurfaceView surface;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_surface_view);
        surface = findViewById(R.id.sv_main);
    }
}

对应的layout/activity_surface_view.xml如下

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.spacesoftwares.ssapplication.SurfaceViewActivity">

    <com.spacesoftwares.ssapplication.MySurfaceView
        android:id="@+id/sv_main"
        android:layout_width="match_parent"
        android:layout_height="400dp"
        />

</android.support.constraint.ConstraintLayout>

这里你可以看到,布局文件com.spacesoftwares.ssapplication.MySurfaceView就是我们自己定义的那个MySurfaceView。

当然,不要忘记检查AndroidManifest.xml中有这么一句(一般Android Studio会自动添加)

com.spacesoftwares.ssapplication.SurfaceViewActivity

 

【1】 https://www.jianshu.com/p/15060fc9ef18

posted @ 2018-07-19 09:43  SpaceVision  阅读(36)  评论(0编辑  收藏  举报