我学习Android都是结合源代码去学习,这样比较直观,非常清楚的看清效果,觉得很好,今天的学习源码是网上找的源码 百度搜就知道很多下载的地方 网上源码的名字叫:水果忍者点击屏幕效果.zip 我的博客写的比较乱,如果本篇文章没有看懂,
请先看上篇文章,地址:http://blog.csdn.net/u014737138/article/details/40859913
实现的原理就是一个视图,在视图上用手指画,主要的就是坐标处理,
继承Android.view.View,构造函数,重载onDraw(),手指触摸屏幕引发的坐标变换处理
主要是上面的几个步骤。接下来一步一步 解析:
1.继承类
public class GestureView extends View {
2.主要的变量定义
private Paint paint;
private float startX = Float.NaN;// 手指开始画的坐标
private float startY = Float.NaN;
private float endX = Float.NaN;// 手指结束的坐标
private float endY = Float.NaN;
// 下层view
// private View viewer;
private static final int gestureColor = Color.rgb(153, 153, 153);// 手指画的颜色
private static final int alpha = 220;// 透明度
private static final int alpha_full = 255;
// 刀锋长度
private static final int shape_length = 80;
// 刀锋截短时间
private static final int shape_cut_time = 150;
关于final关键词可以看我前面的一篇java文章, http://blog.csdn.net/u014737138/article/details/40748485
3.构造函数
public GestureView(Context context) {
super(context);
// TODO Auto-generated constructor stub
paint = new Paint();//画笔初始化
paint.setStyle(Paint.Style.FILL);//设置画笔样式
}
4.重载onDraw(Canvas canvas)
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
// NaN,是Not a Number的缩写 NaN 用于处理计算中出现的错误情况,比如 0.0 除以 0.0 或者求负数的平方根
if (!Float.isNaN(startX) && !Float.isNaN(endY)) {
float gap = getGap(startX, startY, endX, endY);
float w = gap / 10;
// 背景shape外侧点高度
float h = w > 7 ? 7 : w;
// 填充shape外侧点高度
float h2 = h * 2 / 3;
double length = Math.pow(Math.pow(w, 2) + Math.pow((h), 2), 0.5);
double length2 = Math.pow(Math.pow(w, 2) + Math.pow((h2), 2), 0.5);
double ang1_1 = Math.atan((endY - startY) / (endX - startX));
double ang1_2 = Math.atan(h / w);
double angle1_1 = ang1_1 + ang1_2;
double angle1_2 = ang1_1 - ang1_2;
double ang2_2 = Math.atan(h2 / w);
double angle2_1 = ang1_1 + ang2_2;
double angle2_2 = ang1_1 - ang2_2;
if (endX > startX) {
float xx1 = endX - (float) (length * Math.cos(angle1_1));
float yy1 = endY - (float) (length * Math.sin(angle1_1));
float xx2 = endX - (float) (length * Math.cos(angle1_2));
float yy2 = endY - (float) (length * Math.sin(angle1_2));
float xx12 = endX - (float) (length2 * Math.cos(angle2_1));
float yy12 = endY - (float) (length2 * Math.sin(angle2_1));
float xx22 = endX - (float) (length2 * Math.cos(angle2_2));
float yy22 = endY - (float) (length2 * Math.sin(angle2_2));
Path backPath = new Path();
backPath.moveTo(startX, startY);
backPath.lineTo(xx1, yy1);
backPath.lineTo(endX, endY);
backPath.lineTo(xx2, yy2);
backPath.close();
Path fillPath = new Path();
fillPath.moveTo(startX, startY);
fillPath.lineTo(xx12, yy12);
fillPath.lineTo(endX, endY);
fillPath.lineTo(xx22, yy22);
fillPath.close();
paint.setColor(Color.RED);
paint.setAlpha(alpha);
canvas.drawPath(backPath, paint);
paint.setColor(Color.RED);
paint.setAlpha(alpha_full);
canvas.drawPath(fillPath, paint);
} else {
float xx1 = endX + (float) (length * Math.cos(angle1_1));
float yy1 = endY + (float) (length * Math.sin(angle1_1));
float xx2 = endX + (float) (length * Math.cos(angle1_2));
float yy2 = endY + (float) (length * Math.sin(angle1_2));
float xx12 = endX + (float) (length2 * Math.cos(angle2_1));
float yy12 = endY + (float) (length2 * Math.sin(angle2_1));
float xx22 = endX + (float) (length2 * Math.cos(angle2_2));
float yy22 = endY + (float) (length2 * Math.sin(angle2_2));
Path backPath = new Path();
backPath.moveTo(startX, startY);
backPath.lineTo(xx1, yy1);
backPath.lineTo(endX, endY);
backPath.lineTo(xx2, yy2);
backPath.close();
Path fillPath = new Path();
fillPath.moveTo(startX, startY);
fillPath.lineTo(xx12, yy12);
fillPath.lineTo(endX, endY);
fillPath.lineTo(xx22, yy22);
fillPath.close();
paint.setColor(Color.WHITE);
paint.setAlpha(alpha);
canvas.drawPath(backPath, paint);
paint.setColor(Color.WHITE);
paint.setAlpha(alpha_full);
canvas.drawPath(fillPath, paint);
}
}
}
看下有些数学方法的意义:
1).Java.lang.Float.isNaN()方法用法实例教程 - 此方法如果此对象所表示的值是NaN,返回true,否则返回false
什么叫做NaN? ------------not a number
NaN 用于处理计算中出现的错误情况,比如 0.0 除以 0.0 或者求负数的平方根。由上面的表中可以看出,对于单精度浮点数,NaN 表示为指数为 emax + 1 = 128(指数域全为 1),且尾数域不等于零的浮点数。IEEE 标准没有要求具体的尾数域,所以 NaN 实际上不是一个,而是一族。
有个例子说明这个问题:
下面的例子说明了如何使用java.lang.Float.isNaN()方法。
package com.yiibai;
import java.lang.*;
public class FloatDemo {
public static void main(String[] args) {
Float f1 = new Float(-1.0/0.0);
Float f2 = new Float(0.0/0.0);
// returns true if this Float value is a Not-a-Number(NaN), else false
System.out.println(f1 + " = " + f1.isNaN());
System.out.println(f2 + " = " + f2.isNaN());
}
}
让我们来编译和运行上面的程序,这将产生以下结果:
-Infinity = false
NaN = true
2).java.lang.Math.pow(double a, double b) 返回的第一个参数的值提高到第二个参数的幂
下面的例子说明了如何使用lang.Math.pow()方法。
package com.yiibai;
import java.lang.*;
public class MathDemo {
public static void main(String[] args) {
// get two double numbers
double x = 2.0;
double y = 5.4;
// print x raised by y and then y raised by x
System.out.println("Math.pow(" + x + "," + y + ")=" + Math.pow(x, y));
System.out.println("Math.pow(" + y + "," + x + ")=" + Math.pow(y, x));
}
}
让我们来编译和运行上面的程序,这将产生以下结果:
Math.pow(2.0, 5.4)=42.22425314473263
Math.pow(5.4, 2.0)=29.160000000000004
在这个例子中用到这样的一个方法:
public static final float getGap(float x0, float y0, float x1, float y1) {
return (float) Math.pow(
Math.pow((x0 - x1), 2) + Math.pow((y0 - y1), 2), 0.5);
}
这个方法就是计算两个点之间的直线距离:
3)
java.lang.Math.atan(double a) 返回一个角的反正切,在到pi / 2,-π/ 2的范围内。特殊情况:
因此,必须正确舍入的结果的1 ulp范围内。结果必须是半单调性.
下面的示例演示使用的lang.Math.atan()方法.
package com.yiibai;
import java.lang.*;
public class MathDemo {
public static void main(String[] args) {
// get a variable x which is equal to PI/2
double x = Math.PI / 2;
// convert x to radians
x = Math.toRadians(x);
// get the arc tangent of x
System.out.println("Math.atan(" + x + ")" + Math.atan(x));
}
}
让我们来编译和运行上面的程序,这将产生以下结果:
Math.atan(0.027415567780803774)0.0274087022410345
本例子中有该函数的应用:
double ang1_1 = Math.atan((endY - startY) / (endX - startX));
这个函数的结果就是你手指向上滑动的方向与x轴的夹角
其他的函数就不介绍了,求cos sin 等等
这里的坐标处理就是求出 路径,以便画笔paint按照这条路径去画出你手指的移动方向
5.手指触摸事务处理
// 屏幕触摸事务处理
@Override
public boolean onTouchEvent(android.view.MotionEvent event) {
if (event.getPointerCount() == 1) {
int action = event.getAction();
if (MotionEvent.ACTION_DOWN == action) {
startX = event.getX();
startY = event.getY();
} else if (MotionEvent.ACTION_MOVE == action) {
endX = event.getX();
endY = event.getY();
// 刀锋截短时间,则截短至一半
if ((event.getEventTime() - event.getDownTime()) > shape_cut_time) {
if (Math.abs(endX - startX) > shape_length
&& Math.abs(endY - startY) > shape_length) {
startX = (float) (startX + (endX - startX) * 0.5);
startY = (float) (startY + (endY - startY) * 0.5);
}
}
invalidate();
} else if (MotionEvent.ACTION_UP == action) {
startX = Float.NaN;
startY = Float.NaN;
endX = Float.NaN;
endY = Float.NaN;
invalidate();
}
}
// 该view消费了event,所以下层的view必须dispatchTouchEvent才能获得事件
// MotionEvent newEvent = MotionEvent.obtain(event);
// viewer.dispatchTouchEvent(newEvent);
return true;
}
主要是处理拖动过程中坐标的变化:通过时间来判断
如果我手指移动的距离超过刀锋规定的长度 :80,那么就需要把刀锋的效果缩小,游戏中没有可以从左下加拖动右上角把,
也就是说你手指滑动的过程只有一定的距离可以有效的切开水果。这里设置的距离是减半
如果当你手指松开的时候,所有的坐标都失效了,让他变成一个NaN,然后更新视图
如果手指按下了,必须马上获取你手指的坐标
这里有个形状,是在你拖动的过程中如果距离跨度很大,有个箭头的形状,大部分的计算都是为了这个形状去服务。代码应该很好懂
整个技术实现很简单,就是用画笔上画布上面画,没有太多的技巧
看看运行的效果: