基本思路,继承一个view,重写其ondraw方法,在其中9个圆(也可以自己自定义属性,让使用者控制圆的个数,但是过多或者过少都会影响美观而且太复杂的话用户不容易记住自己设置的手势,所以基本可以不用考虑自定义这个属性),每个圆都有一个相当于下标的属性,用于唯一确定一个圆,最后手势的比较也是通过这个坐标点,也就是说你画的手势它只记录了下标值(比如你的手势是一个z,它记录下来的是下标的位置顺序(0124678),你同样画一个z,当时是逆向画(8764210)就没有用,因为最后比较的是下标的顺序),千万不要以为它比较的是图形,画好圆之后就可以重写view的ontouchEvent事件,根据点击的坐标记录被点击的圆,在ondraw中根据是否被点击变换图形颜色,同时画出path,以上就是基本的实现原理,接下来看看代码:
@Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
super.onLayout(changed, left, top, right, bottom);
int perSize = 0;
if (cycles == null && (perSize = getWidth() / 6) > 0) {
cycles = new MyCircle[9];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
MyCircle cycle = new MyCircle();
cycle.setNum(i * 3 + j);
cycle.setOx(perSize * (j * 2 + 1));
cycle.setOy(perSize * (i * 2 + 1));
cycle.setR(perSize * 0.5f);
cycles[i * 3 + j] = cycle;
}
}
}
}
在onlayout中能获得屏幕的高和宽,所以在这里设置圆的大小等属性,接下来重写ontouchevent事件
@Override
public boolean onTouchEvent(MotionEvent event) {
if (canContinue) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
onGestureFinishListener.OnGestureStart();
case MotionEvent.ACTION_MOVE:
canContinue = true;
/**
* event.getX()是相对于view的坐标点,getrawx是相对于屏幕的
*/
eventX = (int) event.getX();
eventY = (int) event.getY();
for (int i = 0; i < cycles.length; i++) {
if (cycles[i].isPointIn(eventX, eventY)) {
cycles[i].setOnTouch(true);
if (!linedCycles.contains(cycles[i].getNum())) {
linedCycles.add(cycles[i].getNum());
}
}
}
break;
case MotionEvent.ACTION_UP:
canContinue = false;
StringBuffer sb = new StringBuffer();
for (int i = 0; i < linedCycles.size(); i++) {
sb.append(linedCycles.get(i));
}
if (onGestureFinishListener != null) {
result = onGestureFinishListener.OnGestureFinish(sb
.toString());
}
timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
// 还原
eventX = eventY = 0;
for (int i = 0; i < cycles.length; i++) {
cycles[i].setOnTouch(false);
}
linedCycles.clear();
linePath.reset();
canContinue = true;
postInvalidate();
timer.cancel();
}
}, 1000);
break;
default:
break;
}
invalidate();
}
return true;
}
根据我们触摸点的位置判断是不是点击了圆的位置,是的话就把这个圆的下标添加到list中,在ondraw中用于画线(就是我们的手势轨迹),抬起之后就结束本次手势,并且在绘制了手势1秒后恢复我们的view,在此期间不在响应我们的点击,否则会乱
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < cycles.length; i++) {
if(!result){
/**
* 手指已经抬起,并且手势错误
*/
paintOutCycle.setColor(ERROR_COLOR);
paintInnerCycle.setColor(INNER_CYCLE_ERROR_COLOR);
paintLines.setColor(ERROR_COLOR);
}
if (cycles[i].isOnTouch()) {
/**
* 圆圈被选中
*/
paintOutCycle.setColor(OUT_CYCLE_ONTOUCH);
paintInnerCycle.setColor(INNER_CYCLE_TOUCHED);
paintLines.setColor(LINE_COLOR);
} else {
paintOutCycle.setColor(OUT_CYCLE_NORMAL);
paintInnerCycle.setColor(INNER_CYCLE_NOTOUCH);
paintLines.setColor(LINE_COLOR);
}
canvas.drawCircle(cycles[i].getOx(), cycles[i].getOy(),
cycles[i].getR(), paintOutCycle);
canvas.drawCircle(cycles[i].getOx(), cycles[i].getOy(),
cycles[i].getR() / 1.5f, paintInnerCycle);
}
drawLine(canvas);
}
private void drawLine(Canvas canvas) {
linePath.reset();
if (linedCycles.size() > 0) {
for (int i = 0; i < linedCycles.size(); i++) {
int index = linedCycles.get(i);
float x = cycles[index].getOx();
float y = cycles[index].getOy();
if (i == 0) {
linePath.moveTo(x,y);
} else {
linePath.lineTo(x,y);
}
}
if(canContinue){
linePath.lineTo(eventX,eventY);
}else{
linePath.lineTo(cycles[linedCycles.get(linedCycles.size()-1)].getOx(), cycles[linedCycles.get(linedCycles.size()-1)].getOy());
}
canvas.drawPath(linePath, paintLines);
}
}
最后是ondraw方法,在其中根据在ontouchevent中得到的list绘制手势轨迹,改变点击的圆的颜色等
参考:http://blog.csdn.net/centralperk/article/details/23374683
我整理之后的代码,主要简化了一些代码:http://download.csdn.net/detail/u012806692/9456989