android案例---绘制锁屏

作者: return_toLife | 来源:发表于2017-08-14 17:24 被阅读72次

    通过这个文章你可以学习到:

    1. 绘制圆形图案
    2. 绘制直线
    3. 锁屏不就是上面两个+触摸吗?(<( ̄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,这里我就不啰嗦啦)

    aaa.PNG

    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>
    

    到此,我们的锁屏案例就结束啦

    总结思路:

    1. 绘制圆形图案
    2. 触摸监听,绘制出圆形图案之间的直线,和正在连接的直线
      3.实现回调接口来判断两次设置的图案锁是否一样之后要做的事情

    总体来说还是很简单的,就是绘制直线那里需要判断的情况比较多,慢慢看下就很好理解了

    有发现问题的可以留言,谢谢大家观赏,你的点赞是我继续分享的动力!!!

    项目github地址:https://github.com/DongDian455/Android.git

    相关文章

      网友评论

        本文标题:android案例---绘制锁屏

        本文链接:https://www.haomeiwen.com/subject/fglhrxtx.html