通过这个文章你可以学习到:
- 绘制圆形图案
- 绘制直线
- 锁屏不就是上面两个+触摸吗?(<( ̄3 ̄)> )
最终效果图:
GIF.gif那我们开始正题
1. 新建一个VIEW类,定义圆形内部类,保存外圆和内圆的半径等信息
public class MyView extends View {
public MyView(Context context) {
super(context);
}
public MyView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private class Pattern{
float patternX; //圆心X坐标
float patternY; //圆心Y坐标
float bigR; //外圆的半径
float smallR; //内圆的半径
boolean isClick; //是否选中
}
}
2. 有了圆,当然还要一些绘制工具啦,定义两个集合,一个保存所有圆的信息,一个保存选中哪几个圆的信息
(ps:记得在构造方法中调用)
List<Pattern> patterns; //所有圆的信息
List<Integer> numbers; //选中那几个圆
Paint paint; //画笔
Path path; //绘制路径
...
...
...
/**
* @methodName: init
* @Description: 初始化参数
*/
private void init(){
patterns=new ArrayList<Pattern>();
numbers=new ArrayList<Integer>();
paint=new Paint();
path=new Path();
3. 工具和集合都有了,那么我们要开始初始化每个圆的信息了,所以首先我们要计算出第一个圆心的位置
我是将每一行分成3个正方形,找到第一个正方形的中心就是我们所要得到的圆心
float firstCenter; //第一个圆的圆心
....
....
....
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
int width=getMeasuredWidth();
int hight=getMeasuredHeight();
int size=Math.min(width,hight); //确保整体是个正方形
firstCenter=size/3f/2f;
initPattern(); //给每个圆的信息初始化的方法,下面就用到了
}
4. 万事俱备,我们可以给圆初始化信息了
/**
* @methodName: initPattern
* @Description:以第一个圆心为基础,每三个换行
*/
private void initPattern() {
float x=firstCenter;
float y=firstCenter;
for(int i=1;i<=9;i++){
Pattern pattern=new Pattern();
pattern.bigR=firstCenter-firstCenter/2f;
pattern.smallR=firstCenter-firstCenter/1.15f;
pattern.patternX=x;
pattern.patternY=y;
patterns.add(pattern);
if(i%3==0){
x=firstCenter;
y+=2*firstCenter;
}else{
x+=2*firstCenter;
}
}
}
5. 圆的信息有了,那么我们可以开始画圆了
** 绘制圆用的主要方法是Path.addCircle();
private void drawBigPattern(Canvas canvas) {
for(int i=0;i<patterns.size();i++){
if(patterns.get(i).isClick){ //根据是否选中设置画笔颜色
paint.setColor(Color.YELLOW);
}else{
paint.setColor(Color.GRAY);
}
path.addCircle(
patterns.get(i).patternX, //圆心x坐标
patterns.get(i).patternY, //圆心y坐标
patterns.get(i).bigR, //半径
Path.Direction.CW); //绘制圆的方向
canvas.drawPath(path,paint);
path.reset();
}
}
然后我们在onDraw方法中调用该方法就可以绘制出如下图
(ps:当然啦,你要在布局文件中使用你自己的VIEW,这里我就不啰嗦啦)
6. 聪明的小伙伴应该能想到,小圆的绘制方法是一样的,不过调用顺序要在大圆后面
private void drawSmallPattern(Canvas canvas) {
paint.setColor(Color.WHITE);
for(int i=0;i<patterns.size();i++){
path.addCircle(patterns.get(i).patternX,patterns.get(i).patternY,patterns.get(i).smallR, Path.Direction.CW);
canvas.drawPath(path,paint);
path.reset();
}
}
bbb.PNG
7. 接下来就要绘制直线了,因为直线是根据触摸的位置为终点来时时绘制的,那么我们要定义两个变量来标志终点位置
float endLineX; //线条结束x坐标
float endLineY; //线条结束y坐标
int index=-1; //选中第几个圆,-1为没选中
....
....
....
private void drawLine(Canvas canvas) {
if(index==-1){
return;
}
paint.setColor(Color.RED);
//画笔的宽度
paint.setStrokeWidth(20);
//绘制已经选中的圆形图案之间的直线
for(int i=0;i<numbers.size()-1;i++){
canvas.drawLine(
patterns.get(numbers.get(i)).patternX,
patterns.get(numbers.get(i)).patternY,
patterns.get(numbers.get(i+1)).patternX,
patterns.get(numbers.get(i+1)).patternY,paint);
}
//绘制触摸时候的直线
canvas.drawLine(
patterns.get(index).patternX,
patterns.get(index).patternY,
endLineX, endLineY,paint);
}
这里为了保证效果,让小圆在上面,所以我们的调用顺序为
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawBigPattern(canvas);
drawLine(canvas);
drawSmallPattern(canvas);
}
8. 当然啦,虽然我们有了绘制直线的方法,但并没有给直线终点赋值,所以并没有什么效果,接下来就是触摸事件了
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN: //按下的时候,我们要清空之前的信息然后重新绘制
cancel();
updatePattern(event.getX(),event.getY()); //用于判断是否选中圆并更新信息
break;
case MotionEvent.ACTION_MOVE:
updatePattern(event.getX(),event.getY());
break;
case MotionEvent.ACTION_UP:
//判断是否选中了圆
if(index!=-1){
endLineX=patterns.get(index).patternX;
endLineY=patterns.get(index).patternY;
}
break;
}
invalidate();
return true;
}
/**
* @methodName: cancel
* @Description: 清空图案选中信息
*/
public void cancel(){
index=-1;
numbers.clear();
for(int i=0;i<patterns.size();i++){
patterns.get(i).isClick=false;
}
invalidate();
}
/**
* @methodName: updatePattern
* @Description:判断是否触摸到圆形图案,并更新圆的信息
*/
private void updatePattern(float x, float y) {
endLineX=x;
endLineY=y;
for(int i=0;i<patterns.size();i++){
if(x>=(patterns.get(i).patternX-patterns.get(i).bigR) &&
x<=(patterns.get(i).patternX+patterns.get(i).bigR) &&
y<=(patterns.get(i).patternY+patterns.get(i).bigR) &&
y>=(patterns.get(i).patternY-patterns.get(i).bigR) &&
!patterns.get(i).isClick){
patterns.get(i).isClick=true;
index=i; //当前选中第几个圆
numbers.add(index); //将选中的圆形图案加入集合,方便后续判断
return;
}
}
}
到这里,我们就已经实现了基本的锁屏绘制了。
小伙伴肯定都实现了~
9. 锁屏的设置一般要输入两次,然后判断两次输入是否正确,我们来实现下,我这里用了回调接口
所以我们先定义一个接口
IOnDrawFinish iOnDrawFinish;
...
...
...
public void setInterFace(IOnDrawFinish iOnDrawFinish){
this.iOnDrawFinish=iOnDrawFinish;
}
public interface IOnDrawFinish{
public void oneDraw(); //第一次绘制完后要做的事情
public void towDraw(boolean isOK); //第二次绘制后传入两次是否输入相同
}
然后我们再定义一个字符串和一个标志位
String password; //当前绘制的图案密码
boolean isTowDraw=false; //是否是第二次绘制
//然后再触摸抬起的时候加入下面方法
case MotionEvent.ACTION_UP:
//是否选中了圆
if(index!=-1){
endLineX=patterns.get(index).patternX;
endLineY=patterns.get(index).patternY;
if(iOnDrawFinish!=null){
//是否第二次绘制
if(isTowDraw){
isOK(password.equals(numbers.toString()));
}else{
password=numbers.toString();
iOnDrawFinish.oneDraw();
}
}
}
break;
isok方法:
private void isOK(boolean equals) {
iOnDrawFinish.towDraw(equals);
}
最后在MainActivity中加入两个按钮,实现IOnDrawFinish接口,重写两个方法
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
MyView myView;
Button btn_again;
Button btn_current;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findView();
btn_again.setOnClickListener(this);
btn_current.setOnClickListener(this);
myView.setInterFace(new MyView.IOnDrawFinish() {
@Override
public void oneDraw() {
btn_again.setEnabled(true);
btn_current.setEnabled(true);
}
@Override
public void towDraw(boolean isOK) {
if(isOK){
Toast.makeText(MainActivity.this, "设置成功,重头开始", Toast.LENGTH_SHORT).show();
myView.cancel();
myView.setTowDraw(false);
btn_current.setText("再次绘制");
}else{
Toast.makeText(MainActivity.this, "设置失败,重头开始", Toast.LENGTH_SHORT).show();
myView.cancel();
myView.setTowDraw(false);
btn_current.setText("再次绘制");
}
}
});
}
private void findView() {
myView=(MyView)findViewById(R.id.myview);
btn_again=(Button)findViewById(R.id.again);
btn_current=(Button)findViewById(R.id.current);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.again:
myView.cancel();
btn_again.setEnabled(false);
btn_current.setEnabled(false);
break;
case R.id.current:
myView.cancel();
myView.setTowDraw(true);
btn_again.setEnabled(false);
btn_current.setEnabled(false);
btn_current.setText("确定");
}
}
}
actvity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.example.a455.mydraw.MyView
android:id="@+id/myview"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="6"
tools:layout_editor_absoluteY="8dp"
tools:layout_editor_absoluteX="8dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<Button
android:id="@+id/again"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="重绘"
android:enabled="false" />
<Button
android:layout_marginLeft="150dp"
android:id="@+id/current"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="再次绘制"
android:enabled="false"/>
</LinearLayout>
</LinearLayout>
到此,我们的锁屏案例就结束啦
总结思路:
- 绘制圆形图案
- 触摸监听,绘制出圆形图案之间的直线,和正在连接的直线
3.实现回调接口来判断两次设置的图案锁是否一样之后要做的事情
总体来说还是很简单的,就是绘制直线那里需要判断的情况比较多,慢慢看下就很好理解了
有发现问题的可以留言,谢谢大家观赏,你的点赞是我继续分享的动力!!!
项目github地址:https://github.com/DongDian455/Android.git
网友评论