Android SurfaceView 实例简介

1.简介

当我们对ui 显示要求较高的时候,或者对程序执行要求较高的时候,view 类并不能满足需求,这个时候就要求用使用 SurfaceView.
比如 在一个运行速度要求很高的游戏中,可以使用双缓存来显示。游戏中的图片动画等绘制在一个画布(Canvas) 上,而SurfaceView 可以直接访问画布,SurfaceView 是提供给需要直接画像素而不是使用窗体部件的应用使用的。
每一个 Surface 创建一个 Canvas 对象,用来管理View 和 Surface 上的操作。

一般 view 都是在相同的 GUI线程中绘制的,这个主应用程序线程同时也用来处理所有的用户交互(例如,按钮单击或者文本输入)。 当需要快速的更新View的 Ui,或者渲染代码阻塞GUI线程时间过长的时候,SurfaceView就是解决上述问题的最佳选择。 SurfaceView封装了一个Surface对象,而不是Canvas。这一点很重要,因为Surface可以使用后台线程绘制。对于那些资源敏感的操作,或者那些要求快速更新或者高速帧率的地方,例如,使用3D图形,创建游戏,或者实时预览摄像头,这一点特别有用。

SurfaceView使用的方式与任何View所派生的类都是完全相同的。可以像其他View那样应用动画,并把它们放到布局中。

SurfaceView封装的Surface支持使用所有标准Canvas方法进行绘图,同时也支持完全的OpenGL ES库。
使用OpenGL,可以在Surface上绘制任何支持的2D或者3D对象,与在2D画布上模拟相同的效果相比,这种方法可以依靠硬件加速(可用的时候)来极大地提高性能。
对于显示动态的3D图像来说,例如,那些使用Google Earth功能的应用程序,或者那些提供沉浸体验的交互式游戏,SurfaceView特别有用。它还是实时显示摄像头预览的最佳选择。

2、示例 1

我们依照实现一个 小圆刷新更改颜色,然后可以点击改变位置 通过 自定义SurfaceView 来时实现,详解看代码注释。

在这里插入图片描述

1) 代码结构

在这里插入图片描述

2)MySurfaceView.java 自定义的SurfaceView
package myapplication.lum.com.myanimation;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {

    private boolean mbLoop = false;  //控制线程ui刷新
    private  SurfaceHolder mSurfaceHolder = null;   //定义 SurfaceHolder 对象

    private  int count = 0;
    public   float x = 50, y = 50; //圆球的显示位置
    private  int screenWidth = getWidth(), screenHeight = getHeight(); //屏幕大小

    public MySurfaceView(Context context) {
        super(context);

        mbLoop = true;

        mSurfaceHolder = this.getHolder();  //初始化 mSurfaceHolder 对象
        /*
        * 添加回调 函数
        * 注意这里这句 mSurfaceHolder.addCallback(this)这句执行完了之后
        * 马上就会回调 surfaceCreated方法了 然后开启线程 执行绘图方法
         * */
        mSurfaceHolder.addCallback(this);
        this.setFocusable(true);
    }

    //surface创建时激发 此方法在主线程总执行
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        new Thread(this).start();	// 开启绘图线程
    }

    //在surface的大小发生改变时调用
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        screenWidth = width;		// reset width when screen orientation is changed
        screenHeight = height;		// reset height when screen orientation is changed
    }

    //在surface销毁时 调用
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        mbLoop = false;
    }

    @Override
    public void run() {
        while (mbLoop) {
            synchronized (mSurfaceHolder) { //同步对象,保证画布一时间只有一个操作
                onDrawCanvas();
            }

            try {
                Thread.sleep(200);
            } catch (Exception e) {
            }
        }
    }

    ///绘图方法 注意这里是另起一个线程来执行绘图方法了不是在UI 线程了
    public void onDrawCanvas() {
        //锁定画布,得到canvas 用SurfaceHolder对象的lockCanvas方法
        Canvas canvas = mSurfaceHolder.lockCanvas();
        if (mSurfaceHolder == null || canvas == null) {
            return;
        }

        if (count < 100) {
            count++;
        } else {
            count = 0;
        }

        //绘图
        Paint mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.CYAN);
        //绘制矩形---清屏作用
        canvas.drawRect(0, 0, screenWidth, screenHeight, mPaint);	// repaint background color
        switch (count % 4) {
            case 0:
                mPaint.setColor(Color.BLUE);
                break;
            case 1:
                mPaint.setColor(Color.GREEN);
                break;
            case 2:
                mPaint.setColor(Color.RED);
                break;
            case 3:
                mPaint.setColor(Color.YELLOW);
                break;
            default:
                mPaint.setColor(Color.WHITE);
                break;
        }
        //绘制小圆
        canvas.drawCircle(x, y, 50, mPaint);
        //绘制后解锁,绘制后必须解锁才能显示
        mSurfaceHolder.unlockCanvasAndPost(canvas);
    }
}

3) MainActivity.java 界面的加载
package myapplication.lum.com.myanimation;

import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.MotionEvent;



public class MainActivity extends Activity {
    MySurfaceView mySurfaceView;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mySurfaceView = new MySurfaceView(this);
        setContentView(mySurfaceView);
    }


      //鼠标点击 改变小球的现实的坐标位置
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(event.getAction() == MotionEvent.ACTION_DOWN){
            mySurfaceView.x = event.getX();
            mySurfaceView.y = event.getY();
        }

        return true;
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if(keyCode == KeyEvent.KEYCODE_BACK){
            this.finish();
        }

        return true;
    }
}

最后了解一下SurfaceView的基本使流程:
1.获取到 SurfaceView 对应的 SurfaceHolder,并且要给 SurfaceHolder 添加一个 SurfaceHolder.callback 对象。
2.创建一个线程用于渲染视图;
3.在SurfaceHolder.callback的surfaceCreated方法中开始渲染视图,绘制结束后,SurfaceHolder.callback的surfaceDestroyed方法中使用 unlockCanvasAndPost方法解锁 Canvas。

3、示例 2

在这里插入图片描述

1)文件结构

在这里插入图片描述

2) activity_main.xml 布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent" android:layout_height="fill_parent"
    android:orientation="vertical">

    <LinearLayout android:id="@+id/LinearLayout01"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <Button android:id="@+id/Button01"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="画正弦">
        </Button>

    </LinearLayout>

    <SurfaceView android:id="@+id/SurfaceView01"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">
    </SurfaceView>
</LinearLayout>

3)功能实现
package myapplication.lum.com.myanimation;

import android.app.Activity;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;


public class MainActivity extends Activity {
    /** Called when the activity is first created. */
    Button btnSimpleDraw, btnTimerDraw;
    SurfaceView sfv;
    SurfaceHolder sfh;

    int Y_axis[],//保存正弦波的Y轴上的点
            centerY,//中心线
            oldX,oldY;//上一个XY点

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btnSimpleDraw = (Button) this.findViewById(R.id.Button01);
        btnSimpleDraw.setOnClickListener(new ClickEvent());
        sfv = (SurfaceView) this.findViewById(R.id.SurfaceView01);
        sfh = sfv.getHolder();


       // 初始化y轴数据
        centerY = (getWindowManager().getDefaultDisplay().getHeight() - sfv
                .getTop()) / 2;
        Y_axis = new int[getWindowManager().getDefaultDisplay().getWidth()];
        for (int i = 1; i < Y_axis.length; i++) {// 计算正弦波
            Y_axis[i - 1] = centerY
                    - (int) (100 * Math.sin(i * 2 * Math.PI / 180));
        }
    }

    class ClickEvent implements View.OnClickListener {

        @Override
        public void onClick(View v) {
            if (v == btnSimpleDraw) {
                SimpleDraw(Y_axis.length-1);//直接绘制正弦波

            }

        }

    }

    /**
     * 绘制指定区域
     */
    void SimpleDraw(int length) {
        if (length == 0)
            oldX = 0;
        Canvas canvas = sfh.lockCanvas(new Rect(oldX, 0, oldX + length,
                getWindowManager().getDefaultDisplay().getHeight()));// 关键:获取画布
        Log.i("Canvas:",
                String.valueOf(oldX) + "," + String.valueOf(oldX + length));
        canvas.drawColor(Color.BLACK);
        Paint mPaint = new Paint();
        mPaint.setColor(Color.BLACK);
        mPaint.setColor(Color.RED);// 画笔为红色
        mPaint.setStrokeWidth(2);// 设置画笔粗细

        int y;
        for (int i = oldX + 1; i < length; i++) {// 绘画正弦波
            y = Y_axis[i - 1];
            canvas.drawLine(oldX, oldY, i, y, mPaint);
            oldX = i;
            oldY = y;
        }
        sfh.unlockCanvasAndPost(canvas);// 解锁画布,提交画好的图像
    }

    void ClearDraw() {
        Canvas canvas = sfh.lockCanvas(null);
        canvas.drawColor(Color.BLACK);// 清除画布
        sfh.unlockCanvasAndPost(canvas);

    }
}

文件参考:

[Android开发常见问题-24] Android 的 SurfaceView 双缓冲应用
https://blog.csdn.net/pi9nc/article/details/19418695

发布了354 篇原创文章 · 获赞 114 · 访问量 44万+

猜你喜欢

转载自blog.csdn.net/qq_27061049/article/details/99942155