Graphics学习篇

从接触JAVA到J2EE,再到J2ME,然后是现在的Android,这条路上我竟然没有真正拥有过其中一样技能,更可怕的是仿佛一切尽是空白。我太不擅长学以致用了。所以,我想还是通过博客/日志的形式,一点一点地记录下自己曾经用心做过,即便没有成功的事情。当然,这也不是我幡然醒悟。因为明白问题所在,与解决问题之间,往往需要很长一段时间。而这段时间对我来说已然太长,足以让我绝望;同时也使我明白效率是一个人能力的很大体现。但愿自己天天向上。

 

以后会陆续在这里写下自己的学习历程(坚持……多看、多想、多做,最重要的是要多作总结,这样既可以方便回顾,又可以加强理解)。

 

在Android Dev Guide里,把Graphics分为两大类:2D Graphics和3D with OpenGL。其中,2D Graphics的大部分APIs都放在drawable包里。3D 则可以在OpenGL ES,还有一些OpenGL工具包里找到。

 

 

1、2D Graphics绘制

 

首先要提到Drawable(android.Graphics.drawable.Drawable),这个类可以通过Resources的方法getDrawable(int id)获得。也就是说,Drawable是可以直接跟资源文件关联的,但是它并不是可视组件,属于底层数据。所以还需要调用draw(Canvas canvas)方法,将数据装载进可描绘的容器中。需要注意的是:切记不要忘了setBound()。(在之前很多次学习中,遇到了令人尴尬的问题:没有对Drawable进行绘制区域界定,即setBounds()。结果是无论怎么改程序,就是不显示。莫非默认区域是“零”?我想,是的。//在后面的DEV GUIDE中看到了关于shapeDrawable的说明:If you do not set the bounds, then the shape will not be drawn, whereas if you don't set the color, it will default to black.我认为这对Drawable来说是一致的,颜色除外。个人觉得不进行绘制与绘制区域为“零”是等价的,不知道在底层是如何判断的。有待考证。。。)

 

既然说到了Canvas(android.Graphics.Canvas),那么我们就来分析下这个类(这个类非比寻常,我认为是相当重头戏的一个类)。Canvas在API里的描述是拥有调用“绘制图像能力”的一个类,这个能力就是draw...(..., Paint),也就是说可对各种图像原型进行描绘,包括基本形状(矩形、圆、路径、文字、点、线等)和Bitmap(这是一个持有像素控制能力的类)。 参数中带有Paint类,这是因为在这个机制中我们还需要提供对“颜色、图像样式等”属性进行解析的服务,而Paint就是这个功能组件。

 

其次要说的是Bitmap(android.Graphics.Bitmap),这个类是通过一系列静态方法获得实例,如:Bitmap myBitmap = Bitmap.createBitmap(int width, int height, Config)。那么它作何用呢?前面提到了Bitmap可以控制像素,那么可以说,它其实是一个像素容器,它与Canvas直接关联,很多时候我们都会用Canvas(Bitmap)这个构造函数去实例化Canvas。对于参数Config有必要进行说明一下,Config是一个常量,我们通常使用Bitmap.Config.ARGB_8888(A指Alpha,即透明通道;RGB分别指Red,Green,Blue,即三原色;后面的8888指代各个通道的颜色位深,也就是常说的32位色彩深度。)。我想这个解释应该会有助于对“像素容器”这个概念的理解吧。

 

最后对Paint(android.Graphics.Paint)稍加说明一下,API里描述如下:Paint持有图像样式和颜色信息(关于如何描绘几何体、文本和位图的信息)。

 

除了以上4个类之外,在2D绘图中,有些时候我们也会使用BitmapFactory这个类。在API中,这个类包含了多个静态方法,实现从files(如Resources)、streams(如InputStream)、Byte-array生成Bitmap对象。在总结中的第二种方法,就是用的BitmapFactory,免去了使用Drawable关联资源文件的麻烦。

 

(1)通用View作为容器的绘图机制

自定义View类(即继承View)的绘图:需要重写onDraw(Canvas),在实例化View子类(包括XML布局文件的调用和构造函数的调用)的过程中,系统会自动调用onDraw(Canvas)进行绘图。在这之后,系统几乎不会再去调用(更准确地说是只在必要的情况下去调用),那么怎么去告诉系统我们需要重绘呢?直接通过对象调用onDraw()是不允许的,于是我们需要invalidate()和postInvalidate()这两个方法,前者用在UI线程中,后者用在非UI线程中。说白了,就是使之前的面板无效,所以系统就自动重绘了。

误解:我们是不是可以把绘图功能写在onDraw(Canvas)之外的方法里,那么我们就可以通过对象进行调用了,重绘效果是一样的?

答案是否定的,这个绘图机制是不允许在onDraw(Canvas)之外重绘的,尝试的结果是空指针异常,但是可以通过onDraw(Canvas)调用外部方法(说白了还是必须在onDraw(Canvas)里实现)。

 

总结绘图机制如下:

方法一:利用Drawable关联资源文件和Canvas。Drawable负责解析资源文件,把数据提交到Canvas,执行绘图操作。

@Override
public void onDraw(Canvas canvas)
{
	super.onDraw(canvas);
	Resources res = getResources();
	Drawable mDrawable = res.getDrawable(R.drawable.background);
	mDrawable.setBounds(0, 0, this.getWidth(), this.getHeight());//这里铺满屏幕。
	mDrawable.draw(canvas);
}

 

  方法二:利用BitmapFactory解析资源文件。

 

问题:关于这个方法,在Bitmap对象生成的过程中,没有上述的Config属性,那么它的像素信息又在哪儿定义呢?难道有默认值?有待考证。不过毕竟人那是BitmapFactory,工厂嘛,批量生产。嘿嘿。

@Override
public void onDraw(Canvas canvas)
{
	super.onDraw(canvas);
	Resources res = getResources();
	Bitmap mBitmap = BitmapFactory.decodeResource(res, R.drawable.earthrise);
	mBitmap = Bitmap.createScaledBitmap(mBitmap, getWidth(), getHeight(), true);//对图片资源进行缩放,这里是全屏覆盖。
	canvas.drawBitmap(mBitmap, 0, 0, new Paint());//这个笔刷可以为null。为啥?有待深入。。。
}

 

(2)使用SurfaceView类作为容器的绘图机制

尽管SurfaceView是View的子类,但是在绘图上却与View大不相同。在这个绘图机制里,系统是不会调用onDraw(Canvas)这个函数的,它采用的是implements SurfaceHolder.Callback的方法。也就是实现SurfaceHolder.Callback这个接口,然后使用SurfaceHolder这个类进行控制。

这个绘图机制的一个优点是很大程度上提高画面重绘的帧率,适合游戏画面描绘。原因是它有独立的缓存(貌似是Surface类),同时还可以通过外部加速(如硬件加速)。

 

总结绘图机制:所用的绘图流程与通用View下的绘图一致,但是并不重写onDraw(Canvas),而是重写SurfaceHolder.Callback接口下的方法(surfaceCreated(...), surfaceChange(...), surfaceDestory(...)//参数参见API),在实例化自定义SurfaceView子类时,首先调用surfaceCreated(...),随即调用surfaceChange(...),在销毁时调用surfaceDestory。

切记:这个绘图机制与之前提及的“通用View绘图“尽管在借助Drawable、Bitmap、BitmapFactory、Canvas和Paint的使用上是一致的,但是底层实现是不同的。并且这个机制下,View.onDraw(Canvas)这个方法是无法被调用的。与View同理,画面绘制只能介于Created与Destory之间(称为有效/合法生命周期),不论直接在这三个重写方法(其实只有前两个,销毁就免了)里实现,还是重写方法调用外部方法都是可行的,但请不要尝试用对象mView.doDraw()类似的途径,会出现空指针异常。原理为何?有待考证。。。

下面给出其中一个方法,另一个可参照“通用View作为容器的绘图机制“:

//这是采用BitmapFactory的方法。

 

@Override
public void surfaceCreated(SurfaceHolder holder) 
{//注意:其实这是实现接口的抽象方法,虽然也是重写方法,但是super(将父类的方法沿用)是没有意义的,因为super本身是抽象的。
	Resources res = getResources();
	Bitmap mBitmap = BitmapFactory.decodeResource(res, R.drawable.earthrise);
	mBitmap = Bitmap.createScaledBitmap(mBitmap, getWidth(), getHeight(), true);
	Canvas canvas = holder.lockCanvas();//锁住,并取回画布
	canvas.drawBitmap(mBitmap, 0, 0, new Paint());
	holder.unlockCanvasAndPost(canvas);//释放,并提交画布
}
 

 

 

待续……

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值