300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > 【Android自定义view系列】圆形百分比进度条

【Android自定义view系列】圆形百分比进度条

时间:2023-02-06 17:34:49

相关推荐

【Android自定义view系列】圆形百分比进度条

1.前言

本文由xygy8860原创,首发地址:/xygy8860/article/details/55101326,转载请注明。

在工作工程中,自己掌握的Android开发知识已经感觉到了瓶颈,Android初级知识没问题,写业务逻辑也没问题。但是作为一个Android开发工程师,并不能仅仅满足于现状,看到自身缺陷和瓶颈之后,需要对自身进行努力提高,以提升自身技能。

对于Android开发而言,自定义控件一直是Android开发进阶的一大方向,更何况,如今的APP已经不是简单的功能实现,用户对于APP的体验已经从功能实现转为用户体验,画面是否精美,转场动画是否别出心裁,UI是否美观大方?等等这些内容是必须考虑的。现如今同类APP越来越多,公司为了赢得客户,提高留存率,必然在体验和UI上下功夫。如果这时设计师设计出精美的UI,开发工程师说:I can't !,那么估计老板会说go out 了!

基于以上,开始了自定义View的学习。同时在学习过程中,记录下来,如果你也对自定义View有兴趣,我们也可以一同成长!

2.圆形百分比进度条UI分析

2.1为何会选择圆形百分比进度条?

现在下载进度条等已经高度自定义,传统的progressbar已经样式落后。在使用360手机助手等的过程中,圆形百分比进度条已经普遍而且非常精美,是常规下载组件的一部分。因此,圆形百分比进度条作为第一个学习对象。同时,在学习个过程中,完善一个自定义view的框架,可以很方便的集成各种自定义View。

在本文的学习过程中,学习参考了这篇文章/wingichoy/article/details/50334595,对于博主后面的自定义View文章,也准备系统的跟着学习下。在此感谢博主的文章,指出了自定义View学习的道路。

2.2 废话不说,上效果

录制的gif有点卡顿,不过在真机上还是很流畅的。

2.3 自定义view知识储备

我们都知道,在自定义view的时候一般都要重写三个方法:onMeasure(),onLayout()和onDraw()。

onDraw()必须有,用来绘制View图像如果要改变View大小,需要重写onMeasure()如果要改变View在父控件中的位置,需要重写onLayout()

view还有其他的方法,也一并了解下,对于自定义view有时候非常有用。

>> onFinishInflate(): 这是一个回调方法,当应用从XML布局文件加载该组件并利用它来构建界面之后,该方法将会被回调。>> onMeasure(int,int):调用该方法来检测View组件及它所包含的所有子组件的大小。>> onLayout(boolean,int,int,int,int):当该组件需要分配其自组件的位置、大小时,该方法就会被回调。>> onSizeChanged(int,int,int,int):当该组件的大小被改变时回调该方法。>> onDraw(Canvas):当该组件需要绘制它的内容时回调该方法进行绘制。>> onKeyDown(int,KeyEvent):当某个键被按下时触发该方法。>> onKeyUp(int,KeyEvent):当松开某个按键时触发该方法。>> onTrackballEvent(MotionEvent):当发生轨迹球事件时触发该方法>> onTouchEvent(MotionEvent):当发生触摸屏事件时触发该方法>> onWindowFocusChanged(boolean):当该组件得到、失去焦点时触发该方法。>> onAttachedToWindow():当把组件放入某个窗口时触发该方法>> onDetachedFrowWindow():当把组件从某个窗口上分离时触发该方法。>> onWindowVisibilityChanged(int):当包含该组件的窗口的可见性发生改变时触发该方法。

2.4 UI分析

在看到上面这个UI的时候,首先我们需要分析下UI的组成部分。任何高级UI都是有初级UI元素组成的,把一个高级UI拆解成一个个初级UI的组成部分,那么也就离我们实现它不远了。

从上面的gif图上我们可以看到,自定义圆形百分比进度条,含有三个元素:(1)一个大圆;(2)一个弧形色带;(3):百分比进度的文字。从上面的拆解我们可以发现,大圆好实现,文字也好实现,但是弧形色带怎么实现呢?

其实,经过分析我们也可以再次分解,将色带分解为:(1)一个圆弧;(2)一个中心小圆。这样实现是不是更加容易了呢?

最终,我们将UI拆解为四个部分:

(1)外层大圆;(2)中间圆弧;(3)内层小圆;(4)中心文字。

其图层显示如下(图片来源于参考博客,感谢博主):

3.圆形百分比进度条代码编写

3.1 继承View并实现构造函数

自定义view的第一步,必然是继承View或viewgroup,由于我们此文主要实现是控件,而不需要布局,同时我们是完全自绘控件,所以只需要继承View即可,同时实现构造函数。首先我们定义一个CircleProgressBar的自定义类,并实现构造函数。代码如下:

// 将前面二个构造函数都指向第三个public CircleProgressBar(Context context) {this(context, null);}// XML中使用此构造函数public CircleProgressBar(Context context, AttributeSet attrs) {this(context, attrs, 0);}public CircleProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);// 默认值,色带宽度,文字大小等mStripeWidth = PxUtils.dpToPx(30, context);mCenterTextSize = PxUtils.spToPx(20, context);mRadius = PxUtils.dpToPx(100, context);//绘制大圆画笔属性设置bigCirclePaint = new Paint();bigCirclePaint.setAntiAlias(true); // 抗锯齿bigCirclePaint.setColor(mCircleColor); // 设置大圆颜色//绘制小圆画笔属性设置smallCirclePaint = new Paint();smallCirclePaint.setAntiAlias(true);smallCirclePaint.setColor(mCircleColor);// 饼状图属性rect = new RectF();sectorPaint = new Paint();sectorPaint.setColor(mAngleColor);sectorPaint.setAntiAlias(true);// 文字画笔属性textPaint = new Paint();textPaint.setColor(Color.WHITE);}

3.2 onMeasure

由于我们不涉及到改变控件在viewparent的位置问题,所以不需要重写onLayout()。所以我们重写的第一步是onMeasure()。

在重写onMeasure()的时候,需要了解一些其他知识。我们都知道,在xml中我们需要设置layout_height和layout_width属性,那么,控件在onMeasure()的时候,就需要用到这些属性。根据这篇博客/lmj623565791/article/details/24252901的解读:

当我们设置明确的宽度和高度时,系统帮我们测量的结果就是我们设置的结果,当我们设置为WRAP_CONTENT,或者MATCH_PARENT系统帮我们测量的结果就是MATCH_PARENT的长度。所以,当设置了WRAP_CONTENT时,我们需要自己进行测量,即重写onMesure方法”:重写之前先了解MeasureSpec的specMode,一共三种类型:EXACTLY:一般是设置了明确的值或者是MATCH_PARENTAT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENTUNSPECIFIED:表示子布局想要多大就多大,很少使用

MeasureSpec对象包含了测量的模式和大小。他是一个32位的int值,其中高两位为测量的模式,低30位是测量的大小。采用位运算和运行效率有关。所以可以从一个MeasureSpec对象分别获取模式和值 如://获取模式 值为 EXACTLY AT_MOST UNSPECIFIEDint specMode = MeasureSpec.getMode(measureSpec);//获取测量值int specSize = MeasureSpec.getSize(measureSpec);

了解了测量模式之后,我们就可以根据specMode 的值,来判断当前layout_height和layout_width的属性,从而计算出当前控件的实际宽高,此文中也即获得了大圆的半径。

@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//获取测量模式int widthMode = MeasureSpec.getMode(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);//获取测量大小int widthSize = MeasureSpec.getSize(widthMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);// match_parentif (widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY) {mRadius = widthSize / 2;x = widthSize / 2;y = heightSize / 2;mWidth = widthSize;mHeight = heightSize;}// warp_content// 如果是自适应,则取默认值if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {mWidth = (int) (mRadius * 2);mHeight = (int) (mRadius * 2);x = mRadius;y = mRadius;}setMeasuredDimension(mWidth, mHeight);}

3.3 onDraw

在控件测量完成之后,系统会调用onDraw()方法绘制控件,在此重写,达到绘制圆形百分比进度条的目的。onDraw()主要分四个方面,即上文提到的大圆、圆弧、小圆和文字,逐层进行绘制,顺序不可调换,不然会导致覆盖。

@Overrideprotected void onDraw(Canvas canvas) {mEndAngle = (int) (mCurPercent * 3.6);// 大圆canvas.drawCircle(x, y, mRadius, bigCirclePaint); // 通过canvas画圆//饼状图rect.right = mWidth;rect.bottom = mHeight;//参数说明见知识补充canvas.drawArc(rect, 270, mEndAngle, true, sectorPaint);// 小圆canvas.drawCircle(x, y, mRadius - mStripeWidth, smallCirclePaint);//绘制文本String text = mCurPercent + "%";textPaint.setTextSize(mCenterTextSize);float textLength = textPaint.measureText(text);canvas.drawText(text, x - textLength / 2, y, textPaint);}

3.4 外部调用

布局方面写完之后,我们还需要向外界暴露一个借口,以控制进度条的显示和文字的改变。

// 外部设置百分比数public void setPercent(int percent) {if (percent > 100 || percent < 0) {throw new IllegalArgumentException("percent must more than 0 and less than 100!");}mCurPercent = percent;invalidate();}

3.5 成果

至此大功告成,我们只需要在布局中引用,即可验证我们的成果。布局中代码也很简单,和普通控件一样即可。

<com.chenghui.customview.widget.CircleProgressBarandroid:id="@+id/circleView"android:layout_width="wrap_content"android:layout_height="wrap_content" />

然后在代码中findViewById即可得到实例,根据下载进度,持续调用setPercent()方法就能达到动画效果。

3.6 扩展

在代码写完,功能实现之后,为了更好的扩展性,必须对控件的属性进行接口暴露,使用者能根据自己的意愿对控件进行适当的修改,以和应用的整体风格契合。因此,暴露以下方法:

// 设置圆的颜色public void setCircleColor(int mCircleColor) {this.mCircleColor = mCircleColor;smallCirclePaint.setColor(mCircleColor);bigCirclePaint.setColor(mCircleColor);// 此方法是为了使设置生效invalidate();}// 设置圆弧的颜色public void setAngleColor(int mAngleColor) {this.mAngleColor = mAngleColor;sectorPaint.setColor(mAngleColor);invalidate();}// 设置色带宽度public void setStripeWidth(float mStripeWidth) {this.mStripeWidth = mStripeWidth;invalidate();}// 设置字体颜色public void setCenterTextColor(int color) {textPaint.setColor(color);invalidate();}// 设置字体大小public void setmCenterTextSize(float mCenterTextSize) {this.mCenterTextSize = mCenterTextSize;invalidate();}

4.源码

本文的源码已经上传github,地址:/xygy8860/CustomView(求star),请各位自取,下载源码后可以按自己的风格自行修改

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。