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();
}
}
}
此外,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
);
}
}
}
绘制游戏动画
动画其实就是不断地重复调用View组件的onDraw(Canvas canvas)方法
如果要View上绘制的图形发生部分改变,就需要程序采用变量来“记住”这些状态数据;
如果需要游戏动画随着用户操作而改变,就需要为用户动作编写事件监听器;
如果需要游戏动画自动改变,就需要使用Timer控制状态数据修改;
绘图中,为了保留用户之间绘制的内容,程序需要借助“双缓冲”技术。
(双缓冲:当程序需要在指定View上进行绘制时,程序并不直接绘制到该View组件上,而是先绘制到一个内存中的Bitmap上,等到内存中的Bitmap绘制好之后,再一次性将Bitmap绘制到View组件上)