Canvas提供了一个drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset, Paint paint)方法,该方法可以对bitmap进行扭曲.这个方法非常灵活,如果用好这个方法,可以在Android应用上开发出"水波荡漾","风吹旗帜"等各种扭曲效果.
drawBitmapMesh方法关键参数的说明:
> bitmap: 指定需要扭曲的源位图.
> meshWidth: 该参数控制在横向上把该源位图划分成多少格.
> meshHeight: 该参数控制在纵向上把该源位图划分为多少格.
> verts: 该参数是一个长度为(meshWidth + 1)*(meshHeight+1)* 2 的数组,它记录了扭曲后的位图各"顶点"位置.虽然它是个一维数组,实际上它记录的数据是形如(x0, y0),(x1, y1),(x2, y2)......(xN, yN)格式的数据,这些数组元素控制对bitmap位图的扭曲效果.
> vertOffset: 控制verts数组中从第几个数组元素开始才对bitmap进行扭曲(忽略vertOffset之前数据的扭曲效果).
drawBitmapMesh方法对源位图扭曲时最关键的参数是meshWidth, meshHeight, verts,这三个参数对扭曲的控制如下图:
从上图可以看出,当程序希望调用drawBitmapMesh方法对位图进行扭曲是,关键是计算verts数组的值----该数组的值记录了扭曲后的位图上各"顶点"的坐标.
下面的实例是通过drawBitmapMesh方法来控制图片的扭曲.
1.自定义MeshView组件
package com.example.matrixdemo.view;
import com.example.matrixdemo.R;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/***
* 通过Canvas的drawBitmapMesh方法控制图片的扭曲效果
* @author Administrator
*
*/
public class MyMeshView extends View {
/** 源图片 */
private Bitmap bitmap;
/** 定义常量,常量指定该图片横向上被分为20格 */
private final int MESH_WIDTH = 20;
/** 定义常量,常量指定该图片纵向上被分为20格 */
private final int MESH_HEIGHT = 20;
/** 记录该图片上包含的441个顶点 */
private final int COUNT = (MESH_WIDTH + 1) * (MESH_HEIGHT + 1);
/** 定义一个数组,保存Bitmap上的21*21个点的坐标 */
private float[] verts = new float[COUNT * 2];
/** 定义一个数组,保存Bitmap上的21*21个点经过扭曲后的坐标 */
// 对图片进行扭曲的关键就是修改该数组里元素的值
private float[] orig = new float[COUNT * 2];
public MyMeshView(Context context) {
this(context, null);
}
public MyMeshView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyMeshView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setFocusable(true);
// 加载Bitmap资源
bitmap = ((BitmapDrawable) getResources().getDrawable(
R.drawable.bg02)).getBitmap();
// 获取图片的宽度,高度
float bitmapWidth = bitmap.getWidth();
float bitmapHeight = bitmap.getHeight();
int index = 0;
for (int i = 0; i <= MESH_HEIGHT; i++) {
float fy = bitmapHeight * i / MESH_HEIGHT;
for (int j = 0; j < MESH_WIDTH; j++) {
float fx = bitmapWidth * i / MESH_WIDTH;
// 初始化orig,verts数组,初始化后,orig,verts两个数组均匀的保存了21*21个点的坐标
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) {
super.onDraw(canvas);
canvas.drawBitmapMesh(bitmap, MESH_WIDTH, MESH_HEIGHT, verts, 0,
null, 0, null);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//调用warp方法根据触摸屏事件的坐标来扭曲verts数组
warp(event.getX(), event.getY());
return true;
}
/***
* 根据触摸事件的位置计算verts数组里个元素的值
*
* @param x
* X坐标
* @param y
* Y坐标
*/
private void warp(float cx, float cy) {
for (int i = 0; i < MESH_WIDTH * 2; i += 2) {
float dx = cx - orig[i + 0];
float dy = cy - orig[i + 1];
float dd = dx * dx + dy * dy;
//计算每个坐标点与当前坐标点(cx,cy)之间的距离
float d = (float) Math.sqrt(dd);
//计算扭曲度,距离当前点(cx,cy)越远,扭曲度越小
float pull = 80000 / (dd * d);
//对verts数组(保存bitmap上21*21进过扭曲后的坐标)重新赋值
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;
}
}
//通知view组件重绘
invalidate();
}
}
2.activity调用以及布局
package com.example.matrixdemo;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class MeshActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mesh);
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<com.example.matrixdemo.view.MyMeshView
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
上面的程序中,warp方法会根据触摸点的位置动态修改verts数组里所有数组元素的值,这样就控制了drawBitmapMesh方法的扭曲效果.