前两天由于有面试和笔试,一直没有更新这个九宫格解锁,刚更新完面试4.0马上就来了。在九宫格解锁之一里面给了注意事项,忘了一个比较重要的东西了,就是导类包的问题,由于Android studio的自动导入会帮我们实现包的导入,但是有些同名类就会造成导包错误的情况,就会造成找不到方法的异常。下面列出的是实现九宫格解锁需要用的包,如果真的有找不到方法的异常而且没法解决的话,来这里对一下包,看看有没有哪个不一样。(应该是有一个自动导入会导错的,但博主忘记了,就只好都列出来了。)如果重新导入的话,先把导入错的包删掉,然后选中那个类,Alt+Enter(回车),就可以啦。里面会让你选择那个类的包。
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
//这里给大家分享一下小技巧:ctrl+o可以在出来的对话框中选择你要重写的父类方法。(本例的父类View方法有点多)
那么现在来说一下九宫格线的绘制:
分两部分: 1.第一部分是如果碰到了九宫格的点的话(最少有两个点),那么就会出现一条点到点的线。这线是固定的。 如图所示:
2.第二部分就是没有碰到九宫格的点时的实时线。是会根据手点的位置随便动的。 如图所示:
来看看实现所需要的技术了: 1.ArrayList<Intrger>,一个list集合,用来保存那些圆心的所在点,是用来画固定的线用的。 2.Math.sqrt()是Math的一个方法,用来开平方根的。(Math为一个数学类,里面有很多数学运算的方法,感兴趣的小伙伴可以了 解一下) 3.View(就是父类)的对于手势感应的方法,说白了就是需要进行重写的方法。 public boolean onTouchEvent(MotionEvent event)方法,通过判断里面的event.getAction的值来写各种状态下发生的事件, (用switch来书写)比如当event.Action等于ACTION_DOWN的时候写的代码为当手指刚碰到屏幕时所执行的代码。 4.list的contains(index)方法,判断wordlist中是否有index,有返回true,没有返回false。 5. canvas.drawLine(x1,y1,x2,y2,paint);画一个线的方法,x1,x2为第一个点坐标,y1,y2为第二个点坐标,paint为 画笔类型。
来看看具体如何实现的:
1.首先我们必须先分析如何画出线,说白了就是当你的手指碰到那个圆的时候记录碰到的是哪个圆,然后添加到集合中,然后画线。这里我们定义两个方法,第一个方法是用来判断手指碰到的位置是不是圆里面,第二个方法为如果碰到了的话返回其碰到的圆心是哪个。关于对圆心Intger的定义后边会介绍的。(应为判断手指碰到位置是不是圆里面的方法是一个小算法,所以将它单独放在一个方法中了,其实两个方法合二为一是没问题的) 先看一下第一个方法把:
private boolean isCollision(float x1,float y1,float x2,float y2,float R)
{
//这里下x1与y1是要判断的实时点,x2与y2是圆心点,R为半径。
double number=(x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);//勾股定理(应该没人不知道,但还是说一下)
if (Math.sqrt(number )<=R) //Math.sqrt()方法是开根号
{
// 如果点和圆心距离小于或等于半径则认为发生碰撞
return true;
}
return false;
}
再来看一下第二个方法:
private int isNumber(float x,float y)
{
for(int i=0;i<list.size();i++)
{
Point point=list .get(i);
if(isCollision(x,y,point.x,point.y,bigR))
{
return i;
}
}
return -1;
}
不知大家记不记得list了,list就是那个保存了所有圆心点的集合,这里遍历list集合,然后看一下当遍历到第几个点的时候方法一返回的是true,然后返回那个值,由于集合中第一个数保存在0之中,所以说圆心的返回值是这样的: //0 3 6 //1 4 7 //2 5 8 当然如果感觉丑的话,或者不适用的话,可以返回 i+1就可以了。这里博主的代码是按返回 i 来的。那个返回-1 其实就是一个随便的数就可以,当然不能是0~8,这样就搞笑了,因为这个方法返回的是具体哪个圆心。返回-1表示的就是不在任何一个圆心。
2.搞定了获取圆心的方法之后,就重写View的另一个方法。(上面有介绍的)目的是在手触碰到圆的时候,将触碰的圆心保存到wordlist集合中。
public boolean onTouchEvent(MotionEvent event)
{
//获取实时手指点的坐标位置
float x=event.getX();//实时的x坐标
float y=event.getY();//实时的y坐标
switch (event.getAction())
{
//在手刚接触屏幕时候:什么都没发生
case MotionEvent.ACTION_DOWN:
break;
//手离开屏幕的时候
case MotionEvent.ACTION_UP:
//手在屏幕上移动的时候
break;
case MotionEvent.ACTION_MOVE:
//用上边所写的第二个方法判断属于哪个圆心的方法来判断属于哪个圆心
int index=isNumber(x,y);
//movePoint为一个Point类型的变量,用于记录实时的手指点的地方的坐标
if(movePoint==null)
{
//在一次绘制结束之后会将movePoint变为null,所以没开始绘制时,需要再定义一下。
movePoint=new Point((int) x,(int)y);
}
else
{
movePoint.set((int)x,(int)y);
}
if(index!=-1)//如果实时的点属于圆心
{
//因为九宫格解锁是不能重复的,所以只有当wordlist中没有实时点属于的圆心,才可以加入。
if (!wordList.contains(index))
//判断wordlist中是否有index,有返回true,没有返回false。
wordList.add(index);
}
invalidate();//这个方法为刷新方法,这个很重要。
break;
}
return true;
}
//如果没有使用invalidate()这个刷新方法的话,不会程序报错,但是运行app的话会崩溃,需要注意一下。
3.现在有了需要画线的点了,就开始画线把,画线与画本体一样,都得在onDraw中绘制。
public void onDraw(Canvas canvas)
{
super.onDraw(canvas);
bigR=100;//外圆半径
smallR=20;//定义内圆半径
//求出每个圆心的坐标保存在集合中。
for(int i=1;i<=3;i++)
{
for(int j=1;j<=3;j++)
{
Point point=new Point((3*(i-1)+2)*(int)bigR,(3*(j-1)+2)*(int)bigR);
list.add(point);
}
}
//遍历集合,用圆心坐标来确定位置来画圆
for(int i=0;i<list.size();i++)
{
Point point=list.get(i);
//判断圆心是否为连接线所经过,如是经过需要变为实心的。
if(wordList.contains(i))
{
canvas.drawCircle(point.x,point.y,bigR,bigPaint);
canvas.drawCircle(point.x,point.y,smallR,connectPaint);
}
else
{
canvas.drawCircle(point.x,point.y,bigR,bigPaint);
canvas.drawCircle(point.x,point.y,smallR,smallPaint);
}
}
draw_line(canvas);//画连接线的方法
}
这就是上次画本体时候所用的代码,但是会发现有些不一样。看一下变化了的代码把。
if(wordList.contains(i)) { canvas.drawCircle(point.x,point.y,bigR,bigPaint); //新定义了一个Paint,用Paint connectPaint就行,因为默认Paint就是实心的。 canvas.drawCircle(point.x,point.y,smallR,connectPaint); } else { canvas.drawCircle(point.x,point.y,bigR,bigPaint); canvas.drawCircle(point.x,point.y,smallR,smallPaint); }
这里用了一个判断,为的是让如果线经过圆的话,圆心变成实心的 。(这里的connectPaiont也要定义在最上边)这里为了方便区分,我就把对线的绘制封装到一个方法里了,当然也可以写在画本体的方法下边。 下边实现draw_line()方法:
void draw_line(Canvas canvas)
{
//画出已经固定的连接线
//这里定义两个坐标,是指连线的两个坐标
Point f1=null;
Point f2=null;
//遍历存着经过点的集合wordList。
for(int i:wordList)
{
if(f1==null)//当第一坐标是空值的时候
{
f1 = list.get(i);//这里用list来求出经过的点的对应的坐标。
}
else
{
//f1为起始的线,f2为终点的线,画完之后将f2的坐标赋给f1,f2的坐标变为起点。
f2= list.get(i);
canvas.drawLine(f1.x,f1.y,f2.x,f2.y,linePaint);//画线的方法
f1=f2;
}
}
//画出实时的连接线
if(movePoint!=null&&f1!=null)//只有先有经过一个圆才能有实时线。
{
canvas.drawLine(f1.x,f1.y,movePoint.x,movePoint.y,linePaint);
}
}
由于canvas需要用onDraw方法传递进来的canvas,所以这里我们也接收一个canvas,来把onDraw方法中的canvas传进来。当然画线所用的linePaint的Paint也需要定义的。(定义在构造函数中)
linePaint=new Paint();
linePaint.setColor(Color.BLACK);
linePaint.setStrokeWidth(10);
到这里关于连接线的绘制也结束了,这篇可能写的有点多,甚至有点啰嗦,其实是因为博主昨天看一个翻页动画的例子时,有个视频教学,博主点进去了,视频只有50分钟,但是里面的知识点真的超多,博主连理解带百度知识点,花了一天多,现在还没做成功呢,感觉那个教学真的说不上优秀,所以博主更加决定以后的程序分享一定要做的很详细,让人看我的博客也能看懂,而不是在我这看完,又查资料又百度的。其实这几天还是有挺多宣讲会和笔试的,但是已经好几天了,一个编程实例拖太久,小伙伴们就得重看一下之前的(一)才能看懂这个(二)了,所以博主熬夜更新了一下。(真的勤快<开心>)
//老规矩,如果按照博主说的编写代码无法成功运行,可以私信或者留言。博主会帮助解决的,如果是代码错误会马上更改的。