第六章 一张白纸好作画—Canvas画布
前面的相关章节,我们详细说明过Android UI组件的使用。通过前面章节的学习,开发者已经可以开发出令人满意的UI效果了。但是有的时候,我们需要实现更加漂亮的UI效果,此时可能就无法直接使用UI组件,而是需要自己画出各种UI效果了。
在Android中,Canvas就是一个画布,开发者可以在画布上绘制想要的任何东西。在本章中,我们将介绍Canvas及相关的技术。
6.1 Canvas画布介绍
6.1.1View Canvas—使用普通View的Canvas画图
从J2ME MIDLET时我们就知道Java提供了Canvas类,而目前在Android平台中,它主要任务为管理绘制过程, 就像一个画布,任何的绘画都将在这个画布上完成。
Canvas类有三个构造方法,分别为构造一个空的Canvas,从Bitmap中构造(2D)和从GL对象中创建(3D),具体如下:
Canvas();//一般使用在View中的Canvas Canvas(Bitmap bitmap);//从2D对象中创建 Canvas(GL gl); //从3D对象中创建 |
由于Java先天的一些问题,基于设备硬件的处理速度考虑,3D的应用更多的需要使用C++的底层库来实现,这样才能有更好的用户体验。这里我们将重点介绍的是Canvas在2D中的功能。
先来看如何使用普通View的Canvas画图。
一般的,操作步骤如下:
1)定义一个自己的View :class your_view extends View{}。
2)重载View的onDraw方法:protectedvoid onDraw(Canvas canvas){}。
3)在onDraw方法中定义你自己的画图操作。
4)在代码或布局文件中使用。
例如:我们可以实现一个带边框文字的textview。代码片段如下所示。
// import略 public class ShadeTextView extends TextView { public ShadeTextView(Context context, AttributeSet attrs) { super(context, attrs); } @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); drawText(canvas, 0xffff0000); super.onDraw(canvas); } // 注:这里只做了单行的字处理 private void drawText( Canvas canvas, int bg) { Paint paint = getPaint(); // 获取textview的文本 String text = String.valueOf(getText()); // 获取第一行文字的左边距 float startX = getLayout().getLineLeft(0); // 获取第一行文字的底部距离 float startY = getBaseline() ; paint.setColor(bg); canvas.drawText(text, startX + 1 , startY, paint); canvas.drawText(text, startX, startY - 1 , paint); canvas.drawText(text, startX, startY + 1 , paint); canvas.drawText(text, startX - 1 , startY, paint); } } |
在布局文件中使用:
<com.yourpackage.ShadeTextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text=" 测试带阴影的文字" android:textSize="20sp"/> |
图6-1 带边框TextView的实现结果
在这里,我们新建了一个类ShadeTextView,继承自TextView类,重写了onDraw(Canvas canvas)方法,在这个方法里画出了边框。
6.1.2 Bitmap Canvas—使用普通Bitmap的Canvas画图
我们也可以定义自己的Bitmap,然后生成一个属于这个Bitmap的Canvas,并在其上进行你需要的绘画。最终可以自由的使用这个图片。
这种方式,主要用于自定义的绘制图形,和刷新比较快的、需要进行双缓冲防止闪屏的场合。
代码示例如下。
Bitmap b = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); // 必须将这个Bitmap放入View的Canvas中,画的图才会被显示出来 Canvas c = new Canvas(b); |
6.1.3SurfaceView Canvas—使用SurfaceView的Canvas画图
前面看到如何使用普通View的Canvas画图和普通的图片的Canvas,Android为方便我们使用View的Canvas进行画图还封装一个类SurfaceView。SurfaceView方式和View方式的主要区别在于:SurfaceView中定义了一个专门的线程来完成画图工作,应用程序不需要等待View的刷图,提高了性能。View的方式适合处理量比较小,帧率比较小的动画,比如说象棋游戏之类的;而SurfaceView方式主要用在游戏,高品质动画方面的画图。
使用SurfaceView,一般步骤如下:
1)定义一个自己的SurfaceView:class your_SurfaceView extends extendsSurfaceView implements SurfaceHolder.Callback() {}
2)实现SurfaceHolder.Callback的3个方法:surfaceCreated()、surfaceChanged()、surfaceDestroyed()
3)定义自己的专注于画图的线程:class your_thread extends Thread
4)重载线程的run()函数(在SurfaceView 的surfaceCreated()中启动这个线程)
下面的示例代码详细说明了线程的处理过程。
// import略 public class YourViewThread extends Thread{ // 睡眠的毫秒数 private int sleepSpan = 100; // 循环标记位 private boolean flag = false; // 游戏界面的引用 private YourSurfaceView mYourSurfaceView; private SurfaceHolder mSurfaceHolder = null; public YourViewThread (YourSurfaceView mYourSurfaceView , SurfaceHolder mSurfaceHolder){ this. mYourSurfaceView = mYourSurfaceView; this.mSurfaceHolder = mSurfaceHolder; } public void run(){ // 画布 Canvas c; while(flag) { c = null; try { // 锁定整个画布,在内存要求比较高的情况下,建议参数不要为null c = mSurfaceHolder.lockCanvas(null); synchronized (this.mSurfaceHolder) { try{ mYourSurfaceView.onDraw(c); } catch(Exception e) { } } } finally { if (c != null) { // 更新屏幕显示内容 mSurfaceHolder.unlockCanvasAndPost(c); } } try { // 睡眠sleepSpan毫秒 Thread.sleep(sleepSpan); } catch(Exception e) { } } } public void setFlag(boolean flag) { // 设置循环标记 this.flag = flag; } } |
--------------------------------------------
程序员赚钱不易 一定要学会理财
平安陆金所 隶属于平安集团的p2p平台
年投资回报率7%-9% 是替代银行理财的首选
个人经验 推荐投资安鑫或者有担保的彩虹项目
不要投资安e 那个几乎无法转让 想提前提现非常困难
网站链接 http://affiliate.lufax.com/action/36XBU
首次投资1000元即可额外赚几百元 不赚白不赚
--------------------------------------------
下面的示例代码详细说明了画图的过程。
// import略 public class YourSurfaceView extends SurfaceView implements SurfaceHolder.Callback{ private YourViewThread mYourViewThread; public YourSurfaceView(Activity activity) { super(activity); // 将SurfaceView画布的句柄传给刷新线程 mYourViewThread = new YourViewThread(this,getHolder()); getHolder().addCallback(this); } protected void onDraw(Canvas canvas) { if(canvas == null) { return; } } @Override public boolean onTouchEvent(MotionEvent event){ return true; } @Override public boolean onKeyDown(int keyCode,KeyEvent event){ return false; } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) { // 当画布发生变化的时候会自动调用 } @Override public void surfaceCreated(SurfaceHolder holder) { // 当画布被创建的时候会自动调用 try{ // 启动刷新线程 mYourViewThread.setFlag(true); mYourViewThread.start(); } catch(Exception ex){ mYourViewThread = new YourViewThread(this,getHolder()); mYourViewThread.setFlag(true); mYourViewThread.start(); } } @Override public void surfaceDestroyed(SurfaceHolder holder) { // 当画布被销毁的时候会自动调用 boolean retry = true; mYourViewThread.setFlag(false); while (retry) { try { mYourViewThread.join(); retry = false; } catch (InterruptedException e) { } } } } |