R.drawable.file_name是一个int常量,若想获取实际的Drawable对象,可调用Resource的getDrawable(int id)获取
Bitmap与BitmapFactory
BitmapDrawable里封装的图片就是一个Bitmap对象,封装方法如下:
BitmapDrawable drawable = new BitmapDrawable(bitmap);
如果要获取BitmapDrawable所包装的Bitmap对象,则可调用BitmapDrawable的getBitmap()方法,示例如下:
Bitmap bitmap = drawable.getBitmap();
Bitmap还提供了一些静态方法来创建新的Bitmap对象,如下:
createBitmap(Bitmap source, int x, int y, int width, int height)
|
从源位图source的指定坐标开始,从中挖取width*height的一块来创建新Bitmap
|
createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter)
|
对源位图进行缩放,缩放成(dstWidth*dstHeight)的新位图
|
createBitmap(int width, int height, Bitmap.Config config)
|
创建一个宽width、高height的新位图
|
createBitmap(Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter)
|
从源位图挖取一块指定宽高创建Bitmap对象,并按Matrix指定的规则变换
|
BitmapFactory包含大量方法
decodeByteArray(byte[] data, int offset, int length)
|
从指定字节数组的offset开始,将长度为length的字节数据解析成Bitmap对象
|
decodeFile(String pathName)
|
从pathName指定的文件中解析创建Bitmap对象
|
decodeFileDescriptor(FileDescriptor fd)
|
用于从FileDescriptor对应的文件
|
decodeResource(Resources res, int id)
|
根据给定资源ID从指定资源解析、创建
|
decodeStream(InputStream is)
|
从指定流中解析、创建
|
通常做法是把图片放在/res/drawabe-hdpi目录下,程序通过ID来获取对象。如果系统不停的去解析、创建Bitmap对象,可能由于前面创建Bitmap所占内存还没有回收,而导致程序发生OutOfMemory错误,为此设计了如下方法:
boolean isRecycled():返回是否已被回收
void recycle():强制一个Bitmap对象以及回收自己
绘图
Canvas、Paint
Android的绘图继承View组件,并重写它的onDraw(Canvas canvas)方法
API Canvas代表依附于指定View画布,它有如下方法:
除了上述方法外,Canvas还提供如下方法进行坐标变换:
rotate(float degrees,float px, float py)
|
对Canvas执行旋转变换
|
scale(float sx, float sy, float sy,float px, float py)
|
对Canvas执行缩放变换
|
skew(float sx, float sy)
|
对Canvas执行倾斜变换
|
translate(float dx, float dy)
|
移动Canvas。向右移动dx距离(dx为负则向左),向下移动dy距离(dy为负则向上)
|
Canvas提供上面的方法还涉及一个API:Paint,Paint代表了Canvas上的画笔,用于设置绘制风格
Paint常用方法:
setARGB(int a, int r, int g, int b)setColor(int color)
|
设置颜色
|
setAlpha(int a)
|
设置透明度
|
setAntiAlias(boolean aa)
|
设置是否抗锯齿
|
setColor(int color)
|
|
setPathEffect(PathEffect effect)
|
设置绘制路径时的路径效果
|
setShader(Shader shader)
|
设置画笔的填充效果
|
setShadowLayer(float radius, float dx, float dy, int color)
|
设置阴影
|
setStrokeWidth(float width)
|
设置画笔的笔触宽度
|
setStrikeJoin(Paint.Join join)
|
设置画笔转弯处的连接风格
|
setStyle(Paint.Style style)
|
设置Paint的填充风格
|
setTextAlign(Paint.Align align)
|
设置绘制文本时文字的对齐方式
|
setTextSize(float textSize)
|
大小
|
Canvas提供的绘制方法中还有一个API:Path,Path代表任意多条直线连接而成的任意图形,当Canvas根据Path绘制图形,它可以绘制出任意形状。
还可以自定义一个View组件,重写它的onDraw(Canvas)方法。
Canvas不仅可以绘制简单图形,还可以直接将一个Bitmap绘制到画布上,这是极大的分工方便
下面详解Path类
Android还为路径绘制提供了PathEffect来定义绘制效果,PathEffect包含如下子类:
- ComposePathEffect
- CornerPathEffect
- DashPathEffect
- DiscretePathEffect
- PathDashPathEffect
- SumPathEffect
示例代码:
main.xml
public
class
PathTest
extends
ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView( new MyView( this ));
}
class MyView extends View
{
float phase ;
PathEffect[] effects = new PathEffect[7];
int [] colors ;
private Paint paint ;
Path path ;
public MyView(Context context)
{
super (context);
paint = new Paint();
paint .setStyle(Paint.Style. STROKE );
paint .setStrokeWidth(4);
path = new Path();
path .moveTo(0,0);
for ( int i =1; i<= 15; i++)
{
path .lineTo(i*20, ( float )Math.random()*60);
}
colors = new int []{Color. BLACK ,Color. BLUE , Color. CYAN
, Color. GREEN , Color. MAGENTA , Color. RED , Color. YELLOW };
}
@Override
protected void onDraw(Canvas canvas)
{
canvas.drawColor(Color. WHITE );
effects [0]= null ;
effects [1] = new CornerPathEffect(10);
effects [2] = new DiscretePathEffect(3.0f, 5.0f);
effects [3] = new DashPathEffect( new float []{20,10,5,10}, phase );
Path p = new Path();
p.addRect(0,0,8,8,Path.Direction. CCW );
effects [4] = new PathDashPathEffect(p,12, phase ,
PathDashPathEffect.Style. ROTATE );
effects [5] = new ComposePathEffect( effects [2], effects [4]);
effects [6] = new SumPathEffect( effects [4], effects [3]);
canvas.translate(8, 8);
for ( int i = 0; i< effects . length ; i++)
{
paint .setPathEffect( effects [i]);
paint .setColor( colors [i]);
canvas.drawPath( path , paint );
canvas.translate(0, 60);
}
phase += 1;
invalidate();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView( new MyView( this ));
}
class MyView extends View
{
float phase ;
PathEffect[] effects = new PathEffect[7];
int [] colors ;
private Paint paint ;
Path path ;
public MyView(Context context)
{
super (context);
paint = new Paint();
paint .setStyle(Paint.Style. STROKE );
paint .setStrokeWidth(4);
path = new Path();
path .moveTo(0,0);
for ( int i =1; i<= 15; i++)
{
path .lineTo(i*20, ( float )Math.random()*60);
}
colors = new int []{Color. BLACK ,Color. BLUE , Color. CYAN
, Color. GREEN , Color. MAGENTA , Color. RED , Color. YELLOW };
}
@Override
protected void onDraw(Canvas canvas)
{
canvas.drawColor(Color. WHITE );
effects [0]= null ;
effects [1] = new CornerPathEffect(10);
effects [2] = new DiscretePathEffect(3.0f, 5.0f);
effects [3] = new DashPathEffect( new float []{20,10,5,10}, phase );
Path p = new Path();
p.addRect(0,0,8,8,Path.Direction. CCW );
effects [4] = new PathDashPathEffect(p,12, phase ,
PathDashPathEffect.Style. ROTATE );
effects [5] = new ComposePathEffect( effects [2], effects [4]);
effects [6] = new SumPathEffect( effects [4], effects [3]);
canvas.translate(8, 8);
for ( int i = 0; i< effects . length ; i++)
{
paint .setPathEffect( effects [i]);
paint .setColor( colors [i]);
canvas.drawPath( path , paint );
canvas.translate(0, 60);
}
phase += 1;
invalidate();
}
}
}
此外,Android的Canvas还提供了一个drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint)方法,该方法可以沿着Path绘制文本。
示例代码如下:
main.xml
public
class
PathTest
extends
ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView( new TextView( this ));
// setContentView(new MyView(this));
}
class TextView extends View
{
final String DRAW_STR = "android java" ;
Path[] paths = new Path[3];
Paint paint ;
public TextView(Context context)
{
super (context);
paths [0] = new Path();
paths [0].moveTo(0, 0);
for ( int i = 1; i<= 7;i++)
{
paths [0].lineTo(i * 30, ( float ) Math.random() * 30);
}
paths [1] = new Path();
RectF rectF = new RectF(0,0,200,120);
paths [1].addOval(rectF,Path.Direction. CCW );
paths [2] = new Path();
paths [2].addArc(rectF, 60, 180);
paint = new Paint();
paint .setAntiAlias( true );
paint .setColor(Color. CYAN );
paint .setStrokeWidth(1);
}
@Override
protected void onDraw(Canvas canvas)
{
canvas.drawColor(Color. WHITE );
canvas.translate(40, 40);
paint .setTextAlign(Paint.Align. RIGHT );
paint .setTextSize(20);
paint .setStyle(Paint.Style. STROKE );
canvas.drawPath( paths [0], paint );
paint .setStyle(Paint.Style. FILL );
canvas.drawTextOnPath( DRAW_STR , paths [0],-8,20, paint );
canvas.translate(0, 60);
paint .setStyle(Paint.Style. STROKE );
canvas.drawPath( paths [1], paint );
paint .setStyle(Paint.Style. FILL );
canvas.drawTextOnPath( DRAW_STR , paths [1], -20, 20, paint );
canvas.translate(0, 120);
paint .setStyle(Paint.Style. STROKE );
canvas.drawPath( paths [2], paint );
paint .setStyle(Paint.Style. FILL );
canvas.drawTextOnPath( DRAW_STR , paths [2], -10, 20, paint );
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView( new TextView( this ));
// setContentView(new MyView(this));
}
class TextView extends View
{
final String DRAW_STR = "android java" ;
Path[] paths = new Path[3];
Paint paint ;
public TextView(Context context)
{
super (context);
paths [0] = new Path();
paths [0].moveTo(0, 0);
for ( int i = 1; i<= 7;i++)
{
paths [0].lineTo(i * 30, ( float ) Math.random() * 30);
}
paths [1] = new Path();
RectF rectF = new RectF(0,0,200,120);
paths [1].addOval(rectF,Path.Direction. CCW );
paths [2] = new Path();
paths [2].addArc(rectF, 60, 180);
paint = new Paint();
paint .setAntiAlias( true );
paint .setColor(Color. CYAN );
paint .setStrokeWidth(1);
}
@Override
protected void onDraw(Canvas canvas)
{
canvas.drawColor(Color. WHITE );
canvas.translate(40, 40);
paint .setTextAlign(Paint.Align. RIGHT );
paint .setTextSize(20);
paint .setStyle(Paint.Style. STROKE );
canvas.drawPath( paths [0], paint );
paint .setStyle(Paint.Style. FILL );
canvas.drawTextOnPath( DRAW_STR , paths [0],-8,20, paint );
canvas.translate(0, 60);
paint .setStyle(Paint.Style. STROKE );
canvas.drawPath( paths [1], paint );
paint .setStyle(Paint.Style. FILL );
canvas.drawTextOnPath( DRAW_STR , paths [1], -20, 20, paint );
canvas.translate(0, 120);
paint .setStyle(Paint.Style. STROKE );
canvas.drawPath( paths [2], paint );
paint .setStyle(Paint.Style. FILL );
canvas.drawTextOnPath( DRAW_STR , paths [2], -10, 20, paint );
}
}
}
绘制游戏动画
动画其实就是不断地重复调用View组件的onDraw(Canvas canvas)方法
如果要View上绘制的图形发生部分改变,就需要程序采用变量来“记住”这些状态数据;
如果需要游戏动画随着用户操作而改变,就需要为用户动作编写事件监听器;
如果需要游戏动画自动改变,就需要使用Timer控制状态数据修改;
绘图中,为了保留用户之间绘制的内容,程序需要借助“双缓冲”技术。
(双缓冲:当程序需要在指定View上进行绘制时,程序并不直接绘制到该View组件上,而是先绘制到一个内存中的Bitmap上,等到内存中的Bitmap绘制好之后,再一次性将Bitmap绘制到View组件上)