使用Matrix控制变换
Matrix是Android的一个矩形工具类,它与其它API结合来控制图形、组件的变换。使用步骤如下:
- 获取Matrix对象,新建或获取已封装的Matrix
- 调用Matrix方法进行平移、旋转、缩放、倾斜等
- 将程序对Matrix所做的变换应用到指定图像或组件
Matrix变换方法有:
setTranslate(float dx, float dy)
|
控制Matrix进行平移
|
setSkew(float kx, float ky, float px, float py)
|
控制Matrix以px、py为轴心进行倾斜,kx、ky为X、Y方向上的倾斜距离
|
setSkew(float kx, float ky)
|
|
setRotate(float degrees)
|
|
setRotate(float degrees,float px, float py)
|
旋转角度degree,旋转轴心(px, py)
|
setScale(float sx, float sy)
|
缩放,sx、sy控制X、Y方向上的缩放比例
|
setScale(float sx,float sy, float px, float py)
|
|
Canvas提供了drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint),在绘制Bitmap时应用Matrix上的变换
示例代码:
(自定义了一个View,该View可检测到用户键盘事件,当用户单击手机方向键时,该自定义View会用Matrix对绘制的图形进行旋转、倾斜)
借助Bitmap的createBitmap方法可以“挖取”源位图的其中一块,这样可以再程序中通过定时器控制不断地“挖取”源位图不同位置的块,从而给用户看到背景移动“假象”
例如,“雷电”中,为了给用户造成飞机不断向下飞行的错觉,可以通过这种方式控制背景图片不断下移,来使用户产生错觉
示例代码:
main.xml:
public class MoveBack extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new MyView(this));
}
class MyView extends View
{
final int BACK_HEIGHT = 1700;
private Bitmap back;
private Bitmap plane;
final int WIDTH=320;
final int HEIGHT=480;
private int startY = BACK_HEIGHT - HEIGHT;
public MyView(Context context)
{
super(context);
back = BitmapFactory.decodeResource(context.getResources()
, R.drawable.back_img);
plane = BitmapFactory.decodeResource(context.getResources()
, R.drawable.plane);
final Handler handler = new Handler()
{
//定义错误,导致无法移动
// public void HandleMessage(Message msg)
public void handleMessage(Message msg)
{
if (msg.what == 0x123)
{
if(startY <= 0)
{
startY = BACK_HEIGHT - HEIGHT;
}
else
{
startY -= 3;
}
}
invalidate();
}
};
new Timer().schedule(new TimerTask()
{
@Override
public void run()
{
handler.sendEmptyMessage(0x123);
}
},0,100);
}
@Override
public void onDraw(Canvas canvas)
{
Bitmap bitmap2 = Bitmap
.createBitmap(back, 0 , startY, WIDTH, HEIGHT);
canvas.drawBitmap(bitmap2, 0, 0,null);
canvas.drawBitmap(plane, 160, 360,null);
}
}
}
使用drawBitmapMesh扭曲图像
Canvas提供了一个drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset, Paint paint)方法对bitmap进行扭曲。(水波荡漾、风吹旗帜)
bitmap
|
源位图
|
meshWidth
|
在横向上把该源位图划分成多少格
|
meshHeight
|
|
verts
|
该参数是一个长度为(meshWidth+1)*(meshHeight+1)*2的数组,它记录了扭曲后的位图各“顶点”位置。虽然它是一位数组,实际上记录的是(x0,y0),(x1,y1)...
|
vertOffset
|
控制verts中从第几个数组才开始对bitmap进行扭曲
|
示例代码如下:
public
class
MeshTest
extends Activity{
private Bitmap bitmap;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(new MyView(this, R.drawable.jinta));
}
private class MyView extends View
{
private final int WIDTH = 20;
private final int HEIGHT = 20;
private final int COUNT = (WIDTH + 1) * (HEIGHT + 1);
private final float [] verts = new float[COUNT * 2];
private final float[] orig = new float[COUNT * 2];
public MyView(Context context, int drawableId)
{
super(context);
setFocusable(true);
bitmap = BitmapFactory.decodeResource(getResources()
,drawableId);
float bitmapWidth = bitmap.getWidth();
float bitmapHeight = bitmap.getHeight();
int index = 0;
for(int y = 0; y <= HEIGHT; y++)
{
float fy = bitmapHeight * y / HEIGHT;
for(int x = 0; x <= WIDTH; x++)
{
float fx = bitmapWidth * x/ WIDTH;
orig[index*2 + 0] = verts[index *2 + 0] = fx;
orig[index*2 + 1] = verts[index *2 + 1] = fy;
index += 1;
}
}
setBackgroundColor(Color.WHITE);
}
@Override
protected void onDraw(Canvas canvas)
{
canvas.drawBitmapMesh(bitmap, WIDTH, HEIGHT
, verts, 0, null, 0, null);
}
private void warp(float cx, float cy)
{
for(int i = 0; i< COUNT * 2;i += 2)
{
float dx = cx - orig[i +0];
float dy = cy - orig[i + 1];
float dd = dx * dx + dy * dy;
float d = (float) Math.sqrt(dd);
float pull = 80000/ ((float)(dd * d));
if (pull >= 1)
{
verts[i+0] = cx;
verts[i+1] = cy;
}
else
{
verts[i+0] = orig[i + 0] + dx* pull;
verts[i+1] = orig[i + 1] + dy* pull;
}
}
invalidate();
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
warp(event.getX(), event.getY());
return true;
}
}
private Bitmap bitmap;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(new MyView(this, R.drawable.jinta));
}
private class MyView extends View
{
private final int WIDTH = 20;
private final int HEIGHT = 20;
private final int COUNT = (WIDTH + 1) * (HEIGHT + 1);
private final float [] verts = new float[COUNT * 2];
private final float[] orig = new float[COUNT * 2];
public MyView(Context context, int drawableId)
{
super(context);
setFocusable(true);
bitmap = BitmapFactory.decodeResource(getResources()
,drawableId);
float bitmapWidth = bitmap.getWidth();
float bitmapHeight = bitmap.getHeight();
int index = 0;
for(int y = 0; y <= HEIGHT; y++)
{
float fy = bitmapHeight * y / HEIGHT;
for(int x = 0; x <= WIDTH; x++)
{
float fx = bitmapWidth * x/ WIDTH;
orig[index*2 + 0] = verts[index *2 + 0] = fx;
orig[index*2 + 1] = verts[index *2 + 1] = fy;
index += 1;
}
}
setBackgroundColor(Color.WHITE);
}
@Override
protected void onDraw(Canvas canvas)
{
canvas.drawBitmapMesh(bitmap, WIDTH, HEIGHT
, verts, 0, null, 0, null);
}
private void warp(float cx, float cy)
{
for(int i = 0; i< COUNT * 2;i += 2)
{
float dx = cx - orig[i +0];
float dy = cy - orig[i + 1];
float dd = dx * dx + dy * dy;
float d = (float) Math.sqrt(dd);
float pull = 80000/ ((float)(dd * d));
if (pull >= 1)
{
verts[i+0] = cx;
verts[i+1] = cy;
}
else
{
verts[i+0] = orig[i + 0] + dx* pull;
verts[i+1] = orig[i + 1] + dy* pull;
}
}
invalidate();
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
warp(event.getX(), event.getY());
return true;
}
}
}
使用Shader填充图形
Shader本身是个抽象类,它提供如下实现类
BitmapShader
LinearGradient
RadialGradient
SweepGradient
ComposeShader