美文网首页
Android自定义View——搞一个战网验证

Android自定义View——搞一个战网验证

作者: 奥利奥真好吃 | 来源:发表于2018-01-06 18:20 被阅读0次

        最近打算好好看一下自定义View这块的东西,刚好无意(毕竟AFK好久了)看到手机里的战网安全令牌,心想这东西既不难也不简单,刚好适合来练习一下。于是开始尝试写一个类似的东西。好吧,先看一下官方的:

    不要妄图登录我的战网

    先来分析一波:

        首先一个大饼,(其实整个屏幕显示都可以做成一块儿,主要是背影图片与大饼的颜色关联,我这里单独提取出大饼来),然后是外圈的一个圆环,没有走过的进度显示为黑色,而走过的进度大致可以分成三种颜色(不知道是不是我色盲,感觉它这三种颜色应该是做了过度的)。我这里直接分为绿色、橙色、红色好了。要注意的是最后五秒的时候会有一个小圆里有5秒的倒计时。

        先画饼:

    ```

    //大圆半径

    final int centerX=getWidth()/2,centerY=getWidth()/2;

    final int radius=Math.min(centerX,centerY)-getPaddingLeft();

    //画最大的实心圆

    mPaint.setAntiAlias(true);

    mPaint.setStyle(Paint.Style.FILL);

    mPaint.setColor(Color.parseColor("#434343"));

    canvas.drawCircle(centerX, centerY, radius, mPaint);

    //画外圈圆环

    mPaint.setColor(Color.BLACK);

    mPaint.setStrokeWidth(20);

    mPaint.setStyle(Paint.Style.STROKE);

    canvas.drawCircle(centerX, centerY, radius -30, mPaint);

    //画进度圆环

    mProgressPaint.setAntiAlias(true);

    mProgressPaint.setStrokeWidth(25);

    mProgressPaint.setStyle(Paint.Style.STROKE);

    mProgressPaint.setColor(mProgressColor);

    RectF oval =new RectF(centerX - radius+30, centerY - radius+30, centerX + radius-30, centerY+ radius-30);

    canvas.drawArc(oval, -90, mProgress, false, mProgressPaint);

    ```

            这里的动画刚开始的时候我是用子线程通过sleep一定时间来更新mProgress,然后invalidate()方法进行刷新,后来觉得既然是动画那不是应该用Android自带的各种Animator嘛,使用Thread.sleep()会不会比较奇怪?然后果断换成了ValueAnimator,感觉效果挺好。

            最后5秒显示小圆里的倒计时,这个一看就需要计算最后一段弧上的坐标参数了,简单的正弦余弦定理,不多说。因为我们的刷新周期是30秒,所以最后5秒的时候还有60度的圆环没走完。注意令数字居中显示即可。

    ```

    //画倒计时5秒

    if(mProgress>=300){

    mProgressPaint.setStyle(Paint.Style.FILL);

        int minRadius=getPaddingLeft();

        double angle=Math.toRadians(360f-mProgress);

        float minCenterX=(float) (centerX-(Math.sin(angle)*(radius-30)));

        float minCenterY=(float) (centerY-(Math.cos(angle)*(radius-30)));

        canvas.drawCircle(minCenterX,minCenterY,minRadius,mProgressPaint);

        mPaint.setTextSize(DisplayUtil.sp2px(mContext, mTextSize)-30);

        mPaint.setStrokeWidth(5);

        int lastSecond=5;

        Rect tempRect=new Rect();

        mPaint.getTextBounds(lastSecond+"",0,1,tempRect);

        lastSecond=(int)(360-mProgress)/12+1;

        canvas.drawText(lastSecond+"",minCenterX,minCenterY+tempRect.height()/2,mPaint);

    }

    ```

            最后动态密码下面还有一个可以点击的字符串提示“复制密码”,它与上面的动态密码中间有一定的间隔,要注意的是这个间隔在点击事件上算在了“复制密码”这个字符串上面。当开始的时候我的Touch事件是严格限定在“复制密码”这个Rect上面的,但后来测试发现手指常常点不到?这不科学呀,我这个字体不比官方的小啊,为啥官方的从来没有这种情况。后来我仔细测试了一下官方的这个功能,发现我还是天真了,官方的并没有将touch事件严格限定在“复制密码”上面,原来是连上面的空隙一起来算的,我晕,这样的话就简单了。

    ```

    mPaint.setTextSize(DisplayUtil.sp2px(mContext,16));

    mPaint.getTextBounds(mCopyText,0,mCopyText.length(),mCopyRect);

    mPaint.setStrokeWidth(2);

    mPaint.setColor(Color.BLUE);

    canvas.drawText(mCopyText,centerX,centerY+mTextRect.height(),mPaint);

    @Override

    public boolean onTouch(View view, MotionEvent motionEvent) {

    float x=motionEvent.getX(),y=motionEvent.getY();

        Point p1=new Point(getWidth()/2-mCopyRect.width()/2,getHeight()/2);

        Point p2=new Point(getWidth()/2+mCopyRect.width()/2,getHeight()/2+2*mCopyRect.height());

        if(motionEvent.getAction()== MotionEvent.ACTION_UP) {

    if (mState== State.START&&x>p1.x&&xp1.y&&y

    ClipboardManagercm= (ClipboardManager)mContext.getSystemService(Context.CLIPBOARD_SERVICE);

                cm.setPrimaryClip(ClipData.newPlainText(null,mText));

                Toast.makeText(mContext,mContext.getString(R.string.BallentVerifierView_copyover_test),Toast.LENGTH_SHORT).show();

            }

    }

    return true;

    }

    ```

            来看一下效果:

    请忽略这么丑的背景色 开始界面应该是这个样子的

            下面那张图是开始的时候显示的界面,当有验证请求的话会自动切换到显示密码的状态。我们这里只是简单的做一下界面工作。

    ```

    //画显示器

    RectF rect=new RectF(centerX/3*2+centerX/12,centerY/3+centerY/12,centerX/3*4-centerX/12,centerY/3*2+centerY/12);

    mPaint.setStrokeWidth(10);

    mPaint.setColor(Color.BLUE);

    canvas.drawRoundRect(rect,20,20,mPaint);

    mPaint.setStrokeWidth(80);

    canvas.drawLine(centerX,centerY/3*2+centerY/12,centerX,centerY/3*2+centerY/12*2,mPaint);

    mPaint.setStrokeWidth(10);

    canvas.drawLine(centerX-70,centerY/3*2+centerY/12*2,centerX+70,centerY/3*2+centerY/12*2,mPaint);

    //画ZZZ

    mPaint.setTextAlign(Paint.Align.CENTER);

    mPaint.setTextSize(50);

    mPaint.getTextBounds("Z Z Z", 0, "Z Z Z".length(), mTextRect);

    if(mProgress<1){

    canvas.drawText("Z    ",centerX,centerY/3*2-mTextRect.height()/2,mPaint);

    }else if(mProgress<2){

    canvas.drawText("Z Z  ",centerX,centerY/3*2-mTextRect.height()/2,mPaint);

    }else{

    canvas.drawText("Z Z Z",centerX,centerY/3*2-mTextRect.height()/2,mPaint);

    }

    //开始游戏吧

    mPaint.setTextSize(60);

    mPaint.setStrokeWidth(3);

    mText=mContext.getString(R.string.BallentVerifierView_nologin_test);

    mPaint.getTextBounds(mText,0,mText.length(),mTextRect);

    canvas.drawText(mText,centerX,centerY+mTextRect.height(),mPaint);

    int tempHeight=mTextRect.height();

    mText=mContext.getString(R.string.BallentVerifierview_startgame_test);

    mPaint.setTextSize(45);

    mPaint.getTextBounds(mText,0,mText.length(),mTextRect);

    canvas.drawText(mText,centerX,centerY+tempHeight+mTextRect.height()*2,mPaint);

    ```

            其实在真实的应用场景中,这个view需要考虑的东西还有很多,比如30秒的周期,不可能每次进入密码界面都是从0度开始计时。它一定是需要与产生密码的服务器进行时间上的同步的。当然,这里可以优化的东西其实也有很多,比如界面刷新问题,毕竟它从来只是进行局部刷新,没有必要每次都让所有视图元素进行重绘,尽管这可能也浪费不了多少系统资源。

            这个简单的自定义view做到这里其实也有一些其它的疑问,比如一般我们会不会尽量使用一个画笔,还是根据需要new出多个Paint,同样,在什么情况下我们会需要一块新的画布,还是说一般而言只要ondraw()里的canvas就足够了。在自定义view中,动画的制作一般采用Android自带的Animation还是根据需要简单的new一个Thread然后sleep接着invalidate()也可以,它们这间有没有明显的优劣呢。

    相关文章

      网友评论

          本文标题:Android自定义View——搞一个战网验证

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