小编最近发现,一些炫酷的view效果,通过需要自定义view和属性动画结合在一起,才能更容易的实现。
实现的效果图如下:
所用的知识有:
(1)自定义View中的path,主要用来绘制指示块。
(2)属性动画-ValueAnimator,并设置属性动画的监听器。
(3)根据属性动画是否结束的标志,决定是否绘制分数对应的描述文本内容。
实现步骤:
继承自View,在构造函数中获取自定义属性和初始化操作(初始化画笔)
privatevoidobtainAttrs(Contextcontext,AttributeSetattrs){TypedArraytypedArray=context.obtainStyledAttributes(attrs,R.styleable.ScoreView);lineLength=typedArray.getDimension(R.styleable.ScoreView_lineLength,dp2Px(10));lineColor=typedArray.getColor(R.styleable.ScoreView_lineColor,Color.WHITE);typedArray.recycle();}privatevoidinit(){arrowPaint=createPaint(Color.WHITE,0,Paint.Style.FILL,0);arcPaint=createPaint(lineColor,dp2Px(1),Paint.Style.STROKE,0);bgPaint=createPaint(lineColor,dp2Px(1),Paint.Style.FILL,0);reachProgressPaint=createPaint(Color.WHITE,dp2Px(1),Paint.Style.FILL,0);arcReachPaint=createPaint(Color.WHITE,dp2Px(1),Paint.Style.STROKE,0);scoreTextPaint=createPaint(Color.WHITE,0,Paint.Style.STROKE,dp2Px(26));descTextPaint=createPaint(Color.WHITE,0,Paint.Style.STROKE,dp2Px(16));}其中初始化画笔抽取到一个函数中:
privatePaintcreatePaint(intcolor,intstrokeWidth,Paint.Stylestyle,floattextSize){Paintpaint=newPaint(Paint.ANTI_ALIAS_FLAG);paint.setColor(color);paint.setStrokeWidth(strokeWidth);paint.setStyle(style);paint.setStrokeJoin(Paint.Join.ROUND);paint.setStrokeCap(Paint.Cap.ROUND);paint.setTextSize(textSize);returnpaint;}覆盖onSizeChanged(),得到控件的宽高,并决定要绘制区域的大小(控件默认设置了内边距)
@OverrideprotectedvoidonSizeChanged(intw,inth,intoldw,intoldh){super.onSizeChanged(w,h,oldw,oldh);viewWidth=w;viewHeight=h;halfView=Math.min(viewWidth,viewHeight)/2;//宽度或高度中最小值的一半,即决定圆心的位置。radius=(Math.min(viewWidth,viewHeight)-2*DEFAULT_PADDING)/2;//绘制园的半径是宽高除去默认内边距}核心绘制代码,覆盖onDraw()方法,根据动画是否结束的标志,决定是否绘制分数对应的文本。
@OverrideprotectedvoidonDraw(Canvascanvas){super.onDraw(canvas);drawArcBackground(canvas);drawArcProgress(canvas);drawScoreText(canvas);if(isAnimEnd){drawDescText(canvas);}}(1)绘制圆弧背景和灰色刻度背景。
privatevoiddrawArcBackground(Canvascanvas){canvas.save();canvas.translate(halfView,halfView);//绘制圆弧RectFrectF=newRectF(dp2Px(5)-radius,dp2Px(5)-radius,radius-dp2Px(5),radius-dp2Px(5));canvas.drawArc(rectF,120,300,false,arcPaint);//绘制刻度线canvas.rotate(30);for(inti=0;i<100;i++){canvas.drawLine(0,radius-dp2Px(15),0,radius-dp2Px(15)-lineLength,bgPaint);canvas.rotate(degree);}canvas.restore();}(2)绘制刻度,根据ValueAnimator进行动画的当前值curValue,来动态改变绘制指示块和已达进度圆弧,从而实现从0开始移动到设定值的动画效果。
privatevoiddrawArcProgress(Canvascanvas){canvas.save();canvas.translate(halfView,halfView);//绘制圆弧RectFrectF=newRectF(dp2Px(5)-radius,dp2Px(5)-radius,radius-dp2Px(5),radius-dp2Px(5));canvas.drawArc(rectF,120,curValue*degree,false,arcReachPaint);//绘制指示方块,方块是从0开始移动某一个位置的canvas.rotate(30+degree*curValue);Pathpath=newPath();path.moveTo(dp2Px(5),radius);path.lineTo(dp2Px(5),radius-dp2Px(10));path.lineTo(0,radius-dp2Px(15));path.lineTo(-dp2Px(5),radius-dp2Px(10));path.lineTo(-dp2Px(5),radius);path.close();canvas.drawPath(path,arrowPaint);//绘制已经达到的刻度canvas.restore();canvas.save();canvas.translate(halfView,halfView);canvas.rotate(30);for(inti=0;i privatevoiddrawScoreText(Canvascanvas){canvas.save();canvas.translate(halfView,halfView);StringscoreText=curValue+"分";floattextLength=scoreTextPaint.measureText(scoreText);canvas.drawText(scoreText,-textLength/2,0,scoreTextPaint);canvas.restore();}(4)动画结束时,绘制最终分数对应的提示信息,该信息只有在动画结束后,才会显示出来。 privatevoiddrawDescText(Canvascanvas){canvas.save();canvas.translate(halfView,halfView);Stringdesc="";if(curValue>=90){desc="车神";}else{desc="马路杀手";}floatdescLength=descTextPaint.measureText(desc);canvas.drawText(desc,-descLength/2,dp2Px(30),descTextPaint);canvas.restore();isAnimEnd=false;//isAnimEnd置为false,防止再次点击start时,就显示动画结束时才能显示的内容}(5)提供对外设置最大值的接口,决定最后的分数。 /***对外提供的接口,用于设置进度的最大值**@paramvalue*/publicvoidsetMaxValue(intvalue){if(valueAnimator==null){valueAnimator=ValueAnimator.ofInt(0,value);}valueAnimator.setInterpolator(newLinearInterpolator());valueAnimator.setDuration(30*value);valueAnimator.addUpdateListener(newValueAnimator.AnimatorUpdateListener(){@OverridepublicvoidonAnimationUpdate(ValueAnimatoranimation){curValue=(int)animation.getAnimatedValue();Log.i("debug","curValue="+curValue);invalidate();}});valueAnimator.addListener(newAnimatorListenerAdapter(){@OverridepublicvoidonAnimationEnd(Animatoranimation){super.onAnimationEnd(animation);isAnimEnd=true;//标记动画结束Log.i("debug","onAnimationEnd");Log.i("debug","onAnimationEndcurValue="+curValue);invalidate();}});valueAnimator.start();}