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

    最近打算好好看一下自定义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()也可以,它们这间有没有明显的优劣呢。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,084评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,623评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,450评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,322评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,370评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,274评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,126评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,980评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,414评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,599评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,773评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,470评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,080评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,713评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,852评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,865评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,689评论 2 354

推荐阅读更多精彩内容