HardBirch

Android提高第二篇之SurfaceView的基本使用

时间:10-11-03 栏目:安卓入门与提高 作者:张飞不张,文采横飞 评论:82 点击: 31,631 次

     上次介绍MediaPlayer  的时候稍微介绍了SurfaceView,SurfaceView由于可以直接从内存或者DMA等硬件接口取得图像数据,因此是个非常重要的绘图容器,这次我就用两篇文章来介绍SurfaceView的用法。网上介绍SurfaceView的用法有很多,写法也层出不同,例如继承SurfaceView类,或者继承SurfaceHolder.Callback类等,这个可以根据功能实际需要自己选择,我这里就直接在普通的用户界面调用SurfaceHolder的lockCanvas和unlockCanvasAndPost。

        先来看看程序运行的截图:

截图1主要演示了直接把正弦波绘画在SurfaceView上

对比上面的左右两图,右图用.lockCanvas(null),而左图用.lockCanvas(new Rect(oldX, 0, oldX + length,
getWindowManager().getDefaultDisplay().getHeight())),对比一下两个效果,由于左图是按指定Rect绘画,所以效率会比右图的全控件绘画高些,并且在清屏之后(canvas.drawColor(Color.BLACK))不会留有上次绘画的残留。

接下来贴出main.xml的源码:




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

接下来贴出程序源码:

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.GREEN);// 画笔为绿色
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);

}
}

注意一下 for (int i = oldX + 1; i < length; i++) {// 绘画正弦波 这句,在.lockCanvas()指定Rect内减少循环画线的次数,可以提高绘图效率。

声明: 本文由( 张飞不张,文采横飞 )原创编译,转载请保留链接: Android提高第二篇之SurfaceView的基本使用

Android提高第二篇之SurfaceView的基本使用:目前有82 条留言

  1. 0楼
    kf156:

    [e01]顶起

    2010-11-03 16:08 [回复]
  2. 0楼
    badmanyu:

    [e01][e03]

    2010-11-03 16:10 [回复]
  3. 0楼
    hmc1985:

    [e01]学习一下!

    2010-11-03 16:12 [回复]
  4. 0楼
    oooo_ada:

    高手…请问在程序里如何设置textview的坐标 ?不是修改xml[e01]

    2010-11-03 16:15 [回复]
  5. 0楼
    scyclone:

    顶丫的

    2010-11-03 16:28 [回复]
  6. 顶你,GV.

    2010-11-03 16:41 [回复]
  7. 0楼
    javacn_org:

    转了 谢谢

    2010-11-03 22:57 [回复]
  8. 0楼
    ublearning:

    [e01]学习了

    2010-11-04 10:09 [回复]
  9. 0楼
    kx29126390:

    高手,但是讲的太泛了,初学者很难看的明白,可以再讲的细致一些吗

    2010-11-04 12:09 [回复]
  10. 0楼
    hyx_lsxh:

    [e01]

    2010-11-05 14:03 [回复]
  11. 0楼
    mlianghua:

    [e01],楼主强人呀,要好好向楼主学习

    2010-11-10 09:43 [回复]
  12. 0楼
    lurkerming:

    学习了。呵呵。谢谢。
    实践过程中发现个问题。
    if (v == btnSimpleDraw) {
    SimpleDraw(Y_axis.length-1);//直接绘制正弦波

    代码片段改为
    if (v == btnSimpleDraw) {
    oldY = centerY;
    SimpleDraw(Y_axis.length-1);//直接绘制正弦波

    绘制出来的曲线貌似才正确啊。呵呵

    2010-11-11 23:31 [回复]
  13. 0楼
    renfujiang:

    12-06 09:47:49.269: ERROR/AndroidRuntime(451): at com.mars.MainActivity$ClickEvent.onClick(MainActivity.java:60)
    为什么提示着个错误啊?

    2010-12-06 17:47 [回复]
  14. 0楼
    renfujiang:

    12-06 09:47:49.269: ERROR/AndroidRuntime(451): java.lang.IllegalStateException: TimerTask is scheduled already

    12-06 09:47:49.269: ERROR/AndroidRuntime(451): at com.mars.MainActivity$ClickEvent.onClick(MainActivity.java:60)
    为什么啊?

    2010-12-06 17:48 [回复]
  15. 0楼
    hellogv:

    回复 renfujiang:
    这个问题,我这里也没遇到过。。。

    2010-12-06 17:53 [回复]
  16. 0楼
    brook19:

    很好的例子,不知有没有研究过两个SurfaceVeiw同时画画的例子。

    2011-01-10 11:55 [回复]
  17. [e01]

    2011-02-23 09:34 [回复]
  18. 0楼
    wudiwo:

    [e01]

    2011-02-25 10:35 [回复]
  19. 0楼
    chrisghr:

    [e01][e03]

    2011-03-18 19:16 [回复]
  20. 强悍啊[e01]

    2011-03-20 13:04 [回复]
  21. 0楼
    hustWQQ:

    请问有研究过Datepickdialog的重写么。我想实现自定义界面的Datepickdialog

    2011-03-21 20:58 [回复]
  22. 高手,学习了。

    2011-03-29 19:12 [回复]
  23. 0楼
    aidesudi:

    楼主,不知道你对OpenGL是否有过了解,是否可以将画好的3D图,也就是GLSurfaceView对象放入到SurfaceView?

    2011-03-30 10:21 [回复]
  24. 0楼
    hellogv:

    回复 aidesudi:
    我做app,暂时还没涉足游戏,所以对于opengl还没有了解

    2011-03-30 10:30 [回复]
  25. 这个我怎么不能实现啊 你上面那个if(length == 0){}的判断中 length不等于0啊 不能执行里面的代码

    2011-04-15 22:07 [回复]
  26. 全部都是到处操的一样的代码 全都是这个错误[e02] 明明要if(length != 0){}的时候才能执行里面的代码 结果网上疯传的这段surfaceView代码 都是一样的 在砸一次[e02]

    2011-04-15 22:15 [回复]
  27. 楼上真言,这才看懂

    2011-05-07 15:33 [回复]
  28. 很不错啊 ,顶

    2011-05-09 11:00 [回复]
  29. [e01]

    2011-05-13 16:01 [回复]
  30. 0楼
    lvtao77:

    这个canvas对象是null[e08]

    2011-05-20 13:04 [回复]
  31. 0楼
    jr209152:

    首先很感谢你这个例子, 但是,我发现一个问题, 就是当按二次定时器绘画时就会出现异常错误, 本人用线程解决了这个问题!

    2011-05-24 00:04 [回复]
  32. 0楼
    hoozh:

    请教一下,我现在用surfafeview显示摄像头图像,同时又想在摄像头图像上绘制一些线条,矩形之类的要怎么做呢?

    2011-06-10 10:30 [回复]
  33. 0楼
    hellogv:

    回复 hoozh:
    用FrameLayout

    2011-06-11 09:34 [回复]
  34. 0楼
    zkyong314:

    受教了。

    2011-07-14 10:07 [回复]
  35. 请问一下:我把你的代码完全拷到android eclipse中,为什么在代码R.id.Button01 ,凡是带id的就出错?我找不到原因。请教你一下怎么回事?id.Button01什么意思呀?

    2011-07-26 14:16 [回复]
  36. 0楼
    hoozh:

    [reply]hellogv[/reply]
    嗯,实验成功了

    2011-08-04 16:28 [回复]
  37. 0楼
    hellogv:

    [reply]xinmengdie01[/reply]
    clean一下工程试试

    2011-08-05 07:56 [回复]
  38. [reply]jr209152[/reply]
    的确有这个问题
    程序不太能折腾
    但是不知道要怎么解
    请教一下…

    2011-08-21 17:15 [回复]
  39. 请教下 怎么的SurfaceView 显示不出来东西啊

    2011-08-27 22:31 [回复]
  40. 0楼
    hellogv:

    [reply]zhang_cheng919[/reply]
    这个问题的难度有"为什么1+1=2"那么难。。。

    2011-08-28 09:53 [回复]
  41. 是我没有表达清楚了,我打印错误是MediaPlayer播放错误,我的视频格式是3gp. 加载路径也出来了,

    2011-08-28 22:49 [回复]
  42. 0楼
    hellogv:

    [reply]zhang_cheng919[/reply]
    MediaPlayer要先prepare在start

    2011-08-30 09:14 [回复]
  43. 0楼
    xyz1375684:

    给力啊!

    2011-09-01 09:47 [回复]
  44. 0楼
    xyz1375684:

    加油啊!

    2011-09-01 11:10 [回复]
  45. 0楼
    courysky:

    做的真细腻

    2011-09-16 16:14 [回复]
  46. 学习一下!

    2011-09-20 10:25 [回复]
  47. 代码运行不起来,跟踪一下,发现canvas为null,我看网上的其他例子都是建立surfaceview子类的方式,implement callback,监听surfaceCreated,surfaceDestroyed; surfaceholder.addCallBack(); 这样canvas = surfaceholder.lockCanvas()才能获得画布,问下楼主你写的代码是运行过吗?? 我怎么运行不起来。

    2011-09-20 11:23 [回复]
  48. [reply]jr209152[/reply]
    你好,第二次按绘图出现异常,请问用线程是怎么解决的!!请指教一下!

    2011-09-20 18:37 [回复]
  49. 0楼
    zhfally520:

    博主 用SurfaceView实现图片平滑移动 咋实现啊

    2011-09-23 09:49 [回复]
  50. 0楼
    hellogv:

    [reply]zhfally520[/reply]
    Bitmap平移

    2011-09-25 17:11 [回复]
  51. 0楼
    liuviking:

    你好!看了你的文章受益匪浅!
    我刚学安卓,有个地方不是很清楚:
    我有个工程,里面有两个activity: test1和MySurfaceView
    其中MySurfaceView就是继承自SurfaceView类的。而test1这个类中有一些EditText控件,现在我想把test1中的EditText值传递到MySurfaceView中使用,请问该怎么实现?用Intent好像不行吧?
    代码如下:
    r=new MySurfaceView(test1.this);//这里MySurfaceView怎么 获取test1中的值?
    setContentView(r);//显示MySurfaceView

    2011-10-09 23:16 [回复]
  52. 0楼
    hellogv:

    [reply]liuviking[/reply]
    刚学android,还是先把基本的控件弄熟,迟点再弄SurfaceView

    2011-10-10 14:14 [回复]
  53. 厉害

    2011-10-20 10:59 [回复]
  54. 0楼
    j68205150:

    [reply]liuviking[/reply]
    class test1{
    …..
    getEditTextValue(){
    return XXXEditText.getText().toString();
    }
    ….
    }

    class MySurfaceView{
    MySurfaceView(test1){
    test1.getEditTextValue();
    }

    }
    就可以取到值了…

    2011-10-21 01:23 [回复]
  55. 0楼
    awen_PC:

    挺不错的,值得借鉴

    2011-11-12 09:27 [回复]
  56. 0楼
    Jack_Yang4:

    我运行程序的时候出现button为null的情况,这是为什么呢?已经出现很久这种情况了,请问有什么解决方法吗?

    2011-11-19 19:10 [回复]
  57. 0楼
    hellogv:

    [reply]Jack_Yang4[/reply]
    setContentView(R.layout.main); 你的xml是叫做main.xml?

    2011-11-20 23:37 [回复]
  58. 0楼
    Jack_Yang4:

    [reply]hellogv[/reply]
    是版本问题,我更新后没错误了。但又发现你程序出现一种现象。当执行简单绘画之后再执行定时绘画程序会停止执行。

    2011-11-21 18:59 [回复]
  59. 0楼
    Jack_Yang4:

    [reply]jr209152[/reply]
    我也发现这个错误,能给个正确的例子吗?邮箱:yang_star4@163.com

    2011-11-21 19:01 [回复]
  60. 最近连续看了博主几篇文章,博主用心了。 写代码最主要的是能把自己的思想写进去,而不是模仿,拷贝。 支持楼主,顺便问下: 楼主的效果图怎么制作的?

    2011-11-23 10:43 [回复]
  61. 0楼
    a603235871:

    把一些错误的垃圾弄在上面误导人呀

    2011-11-27 19:13 [回复]
  62. 0楼
    hellogv:

    [reply]a603235871[/reply]
    谢谢你的批评,麻烦指出文中错误,好让我文章精益求精。

    2011-11-27 23:29 [回复]
  63. 评论真的很受益!

    2011-11-30 22:03 [回复]
  64. 0楼
    hellogv:

    [reply]liuhhaiffeng[/reply]
    谢谢支持

    2011-12-02 13:28 [回复]
  65. 0楼
    zengjz88:

    写的真的很不错。
    有个问题是java代码的风格好像不是你这样写的。
    1.类命名:单词的首字母大写testSurfaceView =>TestSurfaceView 。
    2.属性和方法第一个单词的首字母小写,之后每个单词的首字母大写void ClearDraw() =>void clearDraw() 等等。

    2011-12-14 23:39 [回复]
  66. 0楼
    hellogv:

    [reply]zengjz88[/reply]
    谢谢指点

    2011-12-15 22:27 [回复]
  67. 0楼
    copy_zn:

    2012-02-05 03:33 [回复]
  68. 0楼
    chairzzz:

    我不太懂java的机制,想请问下,我觉得你这个类的
    oldX,oldY,//上一个XY点
    currentX
    这3个变量是不是没有初始化就拿来使用了?需要在定义的时候,或者在onCreate函数里面初始化为0么?

    当然,从程序运行结果来看,程序应该是默默的帮你初始化为0了。

    2012-02-12 00:08 [回复]
  69. 0楼
    maadiah:

    打印出一个实时变量可以吗,怎么写?

    2012-02-21 16:21 [回复]
  70. 0楼
    lijiuche45:

    您好,通过代码,我发现一个问题,在文中您提到不能完全清空上次绘画残余。具体原因是下面我所说的,但是我没法解决掉这个问题。获取连续的画布,特请教你。
    通过循环lockcanvas 和unlock ,来获取画布更新。除第一次外每次获取获取的画布都是上上次的,即第二次循环获得是初始画布,第三次循环获得是第一次画布,也就是第三次绘图是在第一次绘图的基础上的而不是第二次。也许也就是看起来刷新频率不够的原因……除了循环控制外,我还没找到其他办法解决……

    2012-03-19 10:51 [回复]
  71. 非常非常好。谢谢

    2012-05-14 10:22 [回复]
  72. 我个人觉得你,写的还不错哦!

    2012-05-14 20:33 [回复]
  73. 0楼
    Sun_bb:

    挺不错的呢,学习了,谢谢,可以留个方式方便交流吗?谢谢啦

    2012-07-27 21:08 [回复]
  74. 0楼
    isverlis:

    這位大大 看了你這篇文章讓我受益匪淺
    想請問有沒有辦法加快繪圖的速度
    您timer設5毫秒 照道理應該一秒可以畫出200點才對
    可是我測了一下大概一秒只能畫出12點 13點左右
    我timer改1毫秒好像也沒快到哪? 請問這是哪邊出了問題

    2012-08-07 22:08 [回复]
  75. 0楼
    zuo_chuang:

    [reply]oooo_ada[/reply]
    可以用view.setX() 和view.setY()吧

    2012-08-11 10:27 [回复]
  76. 0楼
    iken168:

    [reply]isverlis[/reply]
    我也发现同样问题,经过测试再我的android设备运行,大概15ms描一个点,那个5ms的地方改1ms是没用的,15ms以上才有效果。
    我也想知道有什么办法可以提高描点效率,缩短时间。

    2012-08-11 10:57 [回复]
  77. 0楼
    isverlis:

    [reply]iken168[/reply]
    看你你跟我遇到同樣的問題 我測也差不多在15ms 20ms就是極限了 每秒差不多接近60個點左右

    2012-08-14 16:40 [回复]
  78. 0楼
    chenzujie:

    我想请问下为什么我把mTimer.schedule(mTimerTask, 0, 5);
    改为mTimer.schedule(mTimerTask, 0);后按按钮却没有出现绘图效果?有人遇到这样的问题吗?

    2012-09-16 17:51 [回复]
  79. 0楼
    chenzujie:

    [reply]renfujiang[/reply]
    你是不是连续按了两次定时器绘画?

    2012-09-16 17:52 [回复]
  80. 0楼
    chenzujie:

    [reply]wxa263994137[/reply]
    我也发现这个错误了,如果按照博客里的代码,简单绘画就只能画一次。

    2012-09-16 20:51 [回复]
  81. lz,我很崇拜你哦,我一有难题首先想到的就是到你这里来取经,但是现在出现个问题了,你知道怎么使mediaplayer像videoview一样不用点击事件而直接oncreate里面start

    2012-09-26 17:50 [回复]
  82. 我就是想用mediaview当运行这段代码就直接播放视频

    2012-09-26 17:52 [回复]

发表评论


QQ群互动

Linux系统与内核学习群:194051772

WP建站技术学习交流群:194062106

魔豆之路QR

魔豆的Linux内核之路

魔豆的Linux内核之路

优秀工程师当看优秀书籍

优秀程序员,要看优秀书!

赞助商广告

友荐云推荐