Android surfaceview

作者: GB_speak | 来源:发表于2017-04-28 10:31 被阅读241次

surface,这个单词的意思是浮在表面的,那么surfaceview就是浮在表面的view了。如果真的这样解释,估计有人要拍砖了。然而,话虽不能这么说,取这个名儿,多少还是有点关系的。surface是一个可见区域。

surface是surfaceview中的一个可见部分。我们知道,我们看到的屏幕上的图形,是二维的,我们看到的就是长和宽,其实,在内部实际上是三维的,另一个维度,就是层layer。我们用visio绘图,都会看到这种情况,一个图形会将另个图形遮住,是因为这个图形在上层。如果有同AutoCAD的经验,对这个更容易理解。我们看到的图形实际上是很多图形一层层的叠加在一起的,这些图形元素完全不可见,或者部分可见部分不可见,或者完全可见。

这样看来,surface就可以这样理解:它是内存中一块区域,它是surfaceview可见不那个部分,绘图操作作用于它,然后它就会被显卡之类的显示控制器绘制到屏幕上。

surface是个啥,大概已经有了些概念了。因为它对应了一个内存区,大家都知道,内存区的对象是有生命周期的,可以动态的申请创建和销毁,当然也可能会更新。于是,就有了作用于这个内存区的操作,这些操作就是surfaceCreated/Changed/Destroyed。三个操作放在一起,就是callback,
所以在很多例子里看到,会有callback。

callback,是回调,意思是自己能干一些活,不过自己不去主动做,而是到别人那里去登记一下,别人需要的时候,就会叫我去做。就这个例子而言,可以打这个比方,某建筑工人队伍A,能盖房子,能装修,也能拆房子。可是他自己不去主动的做这些事情,而是去向开发商B去登记这三种能力,当然了,他把这三种能力打了包,叫做A的能力。啥时候,B有活干了,就叫A去做,或者是盖房子,或者是装修,或者是拆建筑。在这个例子中,能力包就是callback.

说了这个例子,其实就解释了 surface相关的一些东西,callback已经说过了,下面来说说其他的。假设surface就是一栋房子,那么surface拥有surfaceHolder,谁呢?在这个例子中好比建筑队A。盖房子对应的就是surfaceCreated, 拆房子就对应了surfaceDestroyed,装修就对应了surfaceChanged.

surface有生存期,好比房子有生存期,在建造以后就存在,在拆了之后就没有了。装修必须发生在这之间。同样的,surface的change必须发生在created和destroyed之间。

surfaceview知道surface的holder是谁,在surfaceview生成的时候,会调用getHolder得到holder,然后holder会调用addCallback将三个callback函数注册。

holder拥有对于surface的控制权。在很多程序中,会在surfaceCreated的函数实现中创建另一个线程。所以在这里有两个线程,一个是UI线程,另一个负责画图的线程。画图线程由UI线程调用surfaceCreated的时候创建,在surfaceDestroyed调用的时候放回到线程池。在这中间,画图线程负责图形的绘制。

在这种模型下,UI线程和画图线程各司其职,前者主要负责和用户的交互,而后者,在负责绘制图形。这样,绘制图形的时候如果时间较长,不会阻塞用户的输入。

我们知道,线程共享内存数据,所以, surface对于两个线程是共享的。所以,为了避免在画图的时候,UI线程也对surface进行操作,在画图前,需要对surface加锁。这个工作是有holder干的,holder会先锁住surface中的一块holder.lockCanvas,我们叫canvas,然后,在上面绘画,画完之后,会解锁unlockCanvasAndPost。

在 应用中,画图可以是一次性的,也可以是由定时器触发的定时的画。实现的都是runnable类中的run方法。关于runnable类,这个Java中定义的,并不是android独有的,可以参考Java的referrence.

这个模型最大的好处就是,画图不依赖于UI线程,不会阻塞UI线程。
而单纯的view是依赖于UI线程画图的。对于完全依赖于用户的输入进行图像显示的更新的,用view是可以的,但是如果能够自动的进行绘图,而不需等待用户的输入,surfaceview无疑是更好的选择。

有图有真相,请看图

Paste_Image.png

下面是自己写的一个小例子:

<?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>
        <Button android:id="@+id/Button02" 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>```

package com.testSurfaceView;

import java.util.Timer;
import java.util.TimerTask;

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 TestSurfaceView extends Activity {
/** Called when the activity is first created. */
Button btnSimpleDraw, btnTimerDraw;
SurfaceView sfv;
SurfaceHolder sfh;

private Timer mTimer;
private MyTimerTask mTimerTask;
int Y_axis[],//保存正弦波的Y轴上的点
centerY,//中心线
oldX,oldY,//上一个XY点 
currentX;//当前绘制到的X轴上的点

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

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

    //动态绘制正弦波的定时器
    mTimer = new Timer();
    mTimerTask = new MyTimerTask();

    // 初始化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);//直接绘制正弦波
         
        } else if (v == btnTimerDraw) {
            oldY = centerY;
            mTimer.schedule(mTimerTask, 0, 5);//动态绘制正弦波
        }

    }

}

class MyTimerTask extends TimerTask {
    @Override
    public void run() {

        SimpleDraw(currentX);
        currentX++;//往前进
        if (currentX == Y_axis.length - 1) {//如果到了终点,则清屏重来
            ClearDraw();
            currentX = 0;
            oldY = centerY;
        }
    }

}
 
/**
 * 绘制指定区域
 */
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));

    Paint mPaint = new Paint();
    mPaint.setColor(Color.BLACK);// 画笔为黑色
    mPaint.setStrokeWidth(10);// 设置画笔粗细

    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);

}

}```

ezgif-2-5d7706a23e.gif

源码下载:https://github.com/gb0302/SurfaceView

相关文章

网友评论

    本文标题:Android surfaceview

    本文链接:https://www.haomeiwen.com/subject/nqsozttx.html