前面仿了两个特效(咕噜咕噜和球)感觉还不错,今天继续仿一个我比较喜欢的特效:贪吃蛇3D。
大概以下几个步骤:
1. 切割bitmap成m*n份
2. 绘制贪吃蛇
2.1 根据手势滑动方向和手势移动距离绘制当前的bitmap
2.2 根据手势滑动方向和手势移动距离绘制即将显示的bitmap
3. 绘制玻璃
1. 切割bitmap成m*n份
这里我就不介绍如何切割bitmap了,在之前的咕噜咕噜中有相关代码介绍。
2. 绘制贪吃蛇
我们事先定一个贪吃蛇移动的准则:
当手势是从右往左滑动时:
贪吃蛇是向下移动,并且当前页面最后一行bitmap是从右往左滑动,显示的效果则是当前页面从左下角滑动消失。
而下一页bitmap从上面滑下来,至于从左上角还是右上角滑下,就得看我们是把bitmap切割成几行了。(这个放到2.2来讲)
当手势是从左往右滑动时:
贪吃蛇是向上移动,并且当前页面第一行的bitmap是从左往右滑动,显示的效果则是当前页面从右上角滑动消失。
而下一页bitmap从下面滑上来,至于从左下角还是右下角滑上来,也得看切割成几行了。(还是放到2.2来讲)
2.1 根据手势滑动方向和手势移动距离绘制当前的bitmap
首先在滑动一定距离时得将每个bitmap按照一定比例缩小。
当手势从右往左滑动,那当前页的bitmap最后一行肯定得往左滑动,这样才能使当前页向下消失,每一行的滑动方向的公式:
(行数 - 当前行索引) % 2 等于1的话代表从右往左滑动,反之从左往右滑动。
当手势从左往右滑动,那当前页的bitmap最前一行肯定得往右滑动,这样才能使当前页向上消失,每一行的滑动方向的公式:
当前行索引 % 2 等于1的话代表从右往左滑动,反之从左往右滑动。
知道了每一行的初始滑动方向以及手势滑动距离,我们就可以定位每一个bitmap的位置了。代码在2.2讲完后贴出。
2.2 根据手势滑动方向和手势移动距离绘制即将显示的bitmap
当手势从右往左时,下一页从上面往下出现,当手势从左往右时,下一页从下面往上出现。所以从这里可以看出我们下一页的所有bitmap的x坐标的求法跟2.1的一样,其实y坐标的求法也一样,只不过y坐标要相应地减去或增加画布的高度。
但是这样是有问题的,请看下图:
当然,这样是不对的。但是当行数为偶数时,如下图:
为偶数时是对的,从下往上出现时也有这个问题,所以得有个公式来解决,如下图:
当为基数行时,得让下一页多移动一行的距离并且最后一行的移动方向和当前页的最后一行的移动方向一致。
当手势从右往左时:
方向:(行数 - 当前行索引) % 2 等于1的话代表从右往左滑动,反之从左往右滑动。
画布偏移距离(y坐标):-getMeasuredHeight() - getMeasuredHeight() / mRows
当手势从左往右时:
当前行索引 % 2 等于1的话代表从右往左滑动,反之从左往右滑动。
画布偏移距离(y坐标):getMeasuredHeight() + getMeasuredHeight() / mRows
好了,综合2.1和2.2的代码如下:
/**
* 绘制移动的部分
*
* @param canvas 画布
* @param imagePiece bitmap(被绘制的对象)
* @param curDistance 当前离初始位置的距离
* @param oldCoordX 初始位置(x坐标)
* @param oldCoordY 初始位置(y坐标)
* @param offsetHeight 画布偏移高度
* @param uod 1:代表手势从左往右,下一页从下面往上出现 -1:代表手势从右往左,下一页从上面往下出现
*/
private void drawSnake(Canvas canvas, ImagePiece imagePiece, float curDistance, float oldCoordX, float oldCoordY, float offsetHeight, int uod) {
//当前x坐标
float coordX = 0;
//当前y坐标
float coordY = 0;
/**
* direction为0代表bitmap的移动方向为从右向左
* direction为1代表bitmap的移动方向为从左向右
*/
if (imagePiece.direction == 0) {
//滑离初始行后的距离
float curDistance1 = curDistance - oldCoordX - imagePiece.bitmap.getHeight();
//每一行的滑动距离(不包括初始行)
float curWidth = imagePiece.bitmap.getWidth() * (mColumns - 1) + imagePiece.bitmap.getHeight();
//offset小于零:还处在初始行 offset大于零:已经滑离初始行
float offset = curDistance1 / curWidth;
//当前bitmap划过了完整的一行的行数
//完整的一行的距离为curWidth
int intOffset = (int) Math.floor(offset);
if (offset <= 0) {
if (oldCoordX - curDistance >= 0) { //还在初始行
coordX = oldCoordX - curDistance;
coordY = oldCoordY;
} else { //在行的拐角处
coordX = 0;
coordY = oldCoordY + uod * (curDistance - oldCoordX);
}
} else {
if (intOffset % 2 == 0) { //当前bitmap正在从左往右滑动
if ((curWidth * offset - intOffset * curWidth) > imagePiece.bitmap.getWidth() * (mColumns - 1)) { //正处在右边拐角处
coordX = imagePiece.bitmap.getWidth() * (mColumns - 1);
coordY = oldCoordY + uod * (intOffset + 1) * imagePiece.bitmap.getHeight() + uod * ((curWidth * offset - intOffset * curWidth) - imagePiece.bitmap.getWidth() * (mColumns - 1));
} else { //正处在当前行(非拐角处)
coordX = curWidth * offset - intOffset * curWidth;
coordY = oldCoordY + uod * (intOffset + 1) * imagePiece.bitmap.getHeight();
}
} else { //当前bitmap正在从右往左滑动
if ((curWidth * offset - intOffset * curWidth) > imagePiece.bitmap.getWidth() * (mColumns - 1)) { // 正处在左边拐角处
coordX = 0;
coordY = oldCoordY + uod * (intOffset + 1) * imagePiece.bitmap.getHeight() + uod * ((curWidth * offset - intOffset * curWidth) - imagePiece.bitmap.getWidth() * (mColumns - 1));
} else { //正处在当前行(非拐角处)
coordX = imagePiece.bitmap.getWidth() * (mColumns - 1) - (curWidth * offset - intOffset * curWidth);
coordY = oldCoordY + uod * (intOffset + 1) * imagePiece.bitmap.getHeight();
}
}
}
} else {
//滑离初始行后的距离
float curDistance1 = curDistance - (imagePiece.bitmap.getWidth() * (mColumns - 1) - oldCoordX) - imagePiece.bitmap.getHeight();
//每一行的滑动距离(不包括初始行)
float curWidth = imagePiece.bitmap.getWidth() * (mColumns - 1) + imagePiece.bitmap.getHeight();
//offset小于零:还处在初始行 offset大于零:已经滑离初始行
float offset = curDistance1 / curWidth;
//当前bitmap划过了完整的一行的行数
//完整的一行的距离为curWidth
int intOffset = (int) Math.floor(offset);
if (offset <= 0) {
if (oldCoordX + curDistance <= imagePiece.bitmap.getWidth() * (mColumns - 1)) { //还在初始行
coordX = oldCoordX + curDistance;
coordY = oldCoordY;
} else { //在行的拐角处
coordX = imagePiece.bitmap.getWidth() * (mColumns - 1);
coordY = oldCoordY + uod * (curDistance - (imagePiece.bitmap.getWidth() * (mColumns - 1) - oldCoordX));
}
} else {
if (intOffset % 2 == 0) { //当前bitmap正在从右往左滑动
if ((curWidth * offset - intOffset * curWidth) > imagePiece.bitmap.getWidth() * (mColumns - 1)) { //正处在左边拐角处
coordX = 0;
coordY = oldCoordY + uod * (intOffset + 1) * imagePiece.bitmap.getHeight() + uod * ((curWidth * offset - intOffset * curWidth) - imagePiece.bitmap.getWidth() * (mColumns - 1));
} else { //正处在当前行(非拐角处)
coordX = imagePiece.bitmap.getWidth() * (mColumns - 1) - (curWidth * offset - intOffset * curWidth);
coordY = oldCoordY + uod * (intOffset + 1) * imagePiece.bitmap.getHeight();
}
} else { //当前bitmap正在从左往右滑动
if ((curWidth * offset - intOffset * curWidth) > imagePiece.bitmap.getWidth() * (mColumns - 1)) { //正处在右边拐角处
coordX = imagePiece.bitmap.getWidth() * (mColumns - 1);
coordY = oldCoordY + uod * (intOffset + 1) * imagePiece.bitmap.getHeight() + uod * ((curWidth * offset - intOffset * curWidth) - imagePiece.bitmap.getWidth() * (mColumns - 1));
} else { //正处在当前行(非拐角处)
coordX = curWidth * offset - intOffset * curWidth;
coordY = oldCoordY + uod * (intOffset + 1) * imagePiece.bitmap.getHeight();
}
}
}
}
mMatrix.reset();
canvas.save();
mMatrix.preScale(scaleX, scaleY);
mMatrix.preTranslate(-(imagePiece.bitmap.getWidth() / 2), -(imagePiece.bitmap.getHeight() / 2));
mMatrix.postTranslate(+(imagePiece.bitmap.getWidth() / 2), +(imagePiece.bitmap.getHeight() / 2));
mMatrix.postTranslate(coordX, coordY + offsetHeight);
canvas.drawBitmap(imagePiece.bitmap, mMatrix, null);
canvas.restore();
}
3. 绘制玻璃
看到go桌面的玻璃时,我猜应该不是直接用canvas绘制的,应该是用的图片。所以我把go桌面反编译了,看到资源文件中果然有玻璃的图片:
其实有三张图,另外两张我之所以不贴,是因为那两张图是反光的png,贴上来也看不清,所以只贴了一张9图。
绘制玻璃的代码如下:
/**
* 根据滑动距离绘制玻璃
*
* @param canvas
*/
private void drawGlass(Canvas canvas) {
float ratio = Math.abs(deltaDistance) / (mWidth / 16);
if (deltaDistance == 0) {
return;
}
if (ratio <= 1.0f) {
mPaint.setAlpha((int) (ratio * 255));
} else {
ratio = (Math.abs(deltaDistance) - mWidth / 2) / (mWidth / 4);
if (ratio >= 0 && ratio <= 1.0f) {
mPaint.setAlpha((int) ((1 - ratio) * 255));
} else if (ratio < 0) {
mPaint.setAlpha(255);
} else {
return;
}
}
Rect mDestRect1 = new Rect(0, 0, mWidth, mHeight);
NinePatch patch = new NinePatch(bitmap1, bitmap1.getNinePatchChunk(), null);
patch.draw(canvas, mDestRect1, mPaint);
Rect mSrcRect2 = new Rect(0, 0, bitmap2.getWidth(), bitmap2.getHeight());
Rect mDestRect2 = new Rect(0, 0, mWidth, mHeight - bitmap3.getHeight());
canvas.drawBitmap(bitmap2, mSrcRect2, mDestRect2, mPaint);
Rect mSrcRect3 = new Rect(0, 0, bitmap3.getWidth(), bitmap3.getHeight());
Rect mDestRect3 = new Rect(0, mHeight - bitmap3.getHeight(), mWidth / 2, mHeight);
canvas.drawBitmap(bitmap3, mSrcRect3, mDestRect3, mPaint);
}
好了,稍微核心的东西大概说完了,看一下效果图吧:
代码: