attributes.recycle();
}
嗯,看起来代码挺长,其实都是在获取自定义属性,没什么技术含量。
3、onMeasure
刚才不是出onDraw里面写写就行了么,为什么要改onMeasure呢,主要是因为我们所有的属性比如进度条宽度让用户自定义了,所以我们的测量也得稍微变下。
@Override
protected synchronized void onMeasure(int widthMeasureSpec,
int heightMeasureSpec)
{
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (heightMode != MeasureSpec.EXACTLY)
{
float textHeight = (mPaint.descent() + mPaint.ascent());
int exceptHeight = (int) (getPaddingTop() + getPaddingBottom() + Math
.max(Math.max(mReachedProgressBarHeight,
mUnReachedProgressBarHeight), Math.abs(textHeight)));
heightMeasureSpec = MeasureSpec.makeMeasureSpec(exceptHeight,
MeasureSpec.EXACTLY);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
宽度我们不变,所以的自定义属性不涉及宽度,高度呢,只考虑不是EXACTLY的情况(用户明确指定了,我们就不管了),根据padding和进度条宽度算出自己想要的,如果非EXACTLY下,我们进行exceptHeight封装,传入给控件进行测量高度。
测量完,就到我们的onDraw了~~~
4、onDraw
@Override
protected synchronized void onDraw(Canvas canvas)
{
canvas.save();
//画笔平移到指定paddingLeft, getHeight() / 2位置,注意以后坐标都为以此为0,0
canvas.translate(getPaddingLeft(), getHeight() / 2);
boolean noNeedBg = false;
//当前进度和总值的比例
float radio = getProgress() * 1.0f / getMax();
//已到达的宽度
float progressPosX = (int) (mRealWidth * radio);
//绘制的文本
String text = getProgress() + “%”;
//拿到字体的宽度和高度
float textWidth = mPaint.measureText(text);
float textHeight = (mPaint.descent() + mPaint.ascent()) / 2;
//如果到达最后,则未到达的进度条不需要绘制
if (progressPosX + textWidth > mRealWidth)
{
progressPosX = mRealWidth - textWidth;
noNeedBg = true;
}
// 绘制已到达的进度
float endX = progressPosX - mTextOffset / 2;
if (endX > 0)
{
mPaint.setColor(mReachedBarColor);
mPaint.setStrokeWidth(mReachedProgressBarHeight);
canvas.drawLine(0, 0, endX, 0, mPaint);
}
// 绘制文本
if (mIfDrawText)
{
mPaint.setColor(mTextColor);
canvas.drawText(text, progressPosX, -textHeight, mPaint);
}
// 绘制未到达的进度条
if (!noNeedBg)
{
float start = progressPosX + mTextOffset / 2 + textWidth;
mPaint.setColor(mUnReachedBarColor);
mPaint.setStrokeWidth(mUnReachedProgressBarHeight);
canvas.drawLine(start, 0, mRealWidth, 0, mPaint);
}
canvas.restore();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
mRealWidth = w - getPaddingRight() - getPaddingLeft();
}
其实核心方法就是onDraw了,但是呢,onDraw也很简单,绘制线、绘制文本、绘制线,结束。
还有两个简单的辅助方法:
/**
-
dp 2 px
-
@param dpVal
*/
protected int dp2px(int dpVal)
{
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
dpVal, getResources().getDisplayMetrics());
}
/**
-
sp 2 px
-
@param spVal
-
@return
*/
protected int sp2px(int spVal)
{
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
spVal, getResources().getDisplayMetrics());
}
好了,到此我们的横向进度就结束了,是不是很简单~~如果你是自定义View,你还得考虑progress的更新,考虑状态的销毁与恢复等等复杂的东西。
接下来看我们的RoundProgressBarWidthNumber圆形的进度条。
2、RoundProgressBarWidthNumber
圆形的进度条和横向的进度条基本变量都是一致的,于是我就让RoundProgressBarWidthNumber extends HorizontalProgressBarWithNumber 了。
然后需要改变的就是测量和onDraw了:
完整代码:
package com.zhy.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint.Cap;
import android.graphics.Paint.Style;
import android.graphics.RectF;
import android.util.AttributeSet;
import com.zhy.library.view.R;
public class RoundProgressBarWidthNumber extends
HorizontalProgressBarWithNumber {
/**
- mRadius of view
*/
private int mRadius = dp2px(30);
public RoundProgressBarWidthNumber(Context context) {
this(context, null);
}
public RoundProgressBarWidthNumber(Context context, AttributeSet attrs) {
super(context, attrs);
mReachedProgressBarHeight = (int) (mUnReachedProgressBarHeight * 2.5f);
TypedArray ta = context.obtainStyledAttributes(attrs,
R.styleable.RoundProgressBarWidthNumber);
mRadius = (int) ta.getDimension(
R.styleable.RoundProgressBarWidthNumber_radius, mRadius);
ta.recycle();
mTextSize = sp2px(14);
mPaint.setStyle(Style.STROKE);
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setStrokeCap(Cap.ROUND);
}
@Override
protected synchronized void onMeasure(int widthMeasureSpec,
int heightMeasureSpec) {
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int paintWidth = Math.max(mReachedProgressBarHeight,
mUnReachedProgressBarHeight);
if (heightMode != MeasureSpec.EXACTLY) {
int exceptHeight = (int) (getPaddingTop() + getPaddingBottom()
- mRadius * 2 + paintWidth);
heightMeasureSpec = MeasureSpec.makeMeasureSpec(exceptHeight,
MeasureSpec.EXACTLY);
}
if (widthMode != MeasureSpec.EXACTLY) {
int exceptWidth = (int) (getPaddingLeft() + getPaddingRight()
- mRadius * 2 + paintWidth);
widthMeasureSpec = MeasureSpec.makeMeasureSpec(exceptWidth,
MeasureSpec.EXACTLY);
}
super.onMeasure(heightMeasureSpec, heightMeasureSpec);
}
@Override
protected synchronized void onDraw(Canvas canvas) {
String text = getProgress() + “%”;
// mPaint.getTextBounds(text, 0, text.length(), mTextBound);
float textWidth = mPaint.measureText(text);
float textHeight = (mPaint.descent() + mPaint.ascent()) / 2;
canvas.save();
canvas.translate(getPaddingLeft(), getPaddingTop());
mPaint.setStyle(Style.STROKE);
// draw unreaded bar
mPaint.setColor(mUnReachedBarColor);
mPaint.setStrokeWidth(mUnReachedProgressBarHeight);
canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);
// draw reached bar
mPaint.setColor(mReachedBarColor);
mPaint.setStrokeWidth(mReachedProgressBarHeight);
float sweepAngle = getProgress() * 1.0f / getMax() * 360;
canvas.drawArc(new RectF(0, 0, mRadius * 2, mRadius * 2), 0,
sweepAngle, false, mPaint);
// draw text
mPaint.setStyle(Style.FILL);
canvas.drawText(text, mRadius - textWidth / 2, mRadius - textHeight,
mPaint);
canvas.restore();
}
}
首先获取它的专有属性mRadius,然后根据此属性去测量,测量完成绘制;
绘制的过程呢?
先绘制一个细一点的圆,然后绘制一个粗一点的弧度,二者叠在一起就行。文本呢,绘制在中间~~~总体,没什么代码量。
好了,两个进度条就到这了,是不是发现简单很多。总体设计上,存在些问题,如果抽取一个BaseProgressBar用于获取公共的属性;然后不同样子的进度条继承分别实现自己的测量和样子,这样结构可能会清晰些~~~
4、使用
====
布局文件
<ScrollView xmlns:android=“http://schemas.android.com/apk/res/android”
xmlns:tools=“http://schemas.android.com/tools”
xmlns:zhy=“http://schemas.android.com/apk/res-auto”
android:layout_width=“match_parent”
android:layout_height=“match_parent” >
<LinearLayout
android:layout_width=“match_parent”
android:layout_height=“match_parent”
android:orientation=“vertical”
android:padding=“25dp” >
<com.zhy.view.HorizontalProgressBarWithNumber
android:id=“@+id/id_progressbar01”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:layout_marginTop=“50dip”
android:padding=“5dp” />
<com.zhy.view.HorizontalProgressBarWithNumber
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:layout_marginTop=“50dip”
android:padding=“5dp”
android:progress=“50”
zhy:progress_text_color=“#ffF53B03”
zhy:progress_unreached_color=“#ffF7C6B7” />
<com.zhy.view.RoundProgressBarWidthNumber
android:id=“@+id/id_progress02”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:layout_marginTop=“50dip”
android:padding=“5dp”
android:progress=“30” />
<com.zhy.view.RoundProgressBarWidthNumber
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:layout_marginTop=“50dip”
android:padding=“5dp”
android:progress=“50”
zhy:progress_reached_bar_height=“20dp”
zhy:progress_text_color=“#ffF53B03”
zhy:radius=“60dp” />
MainActivity
package com.zhy.sample.progressbar;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import com.zhy.annotation.Log;
import com.zhy.view.HorizontalProgressBarWithNumber;
public class MainActivity extends Activity {
private HorizontalProgressBarWithNumber mProgressBar;
private static final int MSG_PROGRESS_UPDATE = 0x110;
private Handler mHandler = new Handler() {
总结:
各行各样都会淘汰一些能力差的,不仅仅是IT这个行业,所以,不要被程序猿是吃青春饭等等这类话题所吓倒,也不要觉得,找到一份工作,就享受安逸的生活,你在安逸的同时,别人正在奋力的向前跑,这样与别人的差距也就会越来越遥远,加油,希望,我们每一个人,成为更好的自己。
-
BAT大厂面试题、独家面试工具包,
-
资料包括 数据结构、Kotlin、计算机网络、Framework源码、数据结构与算法、小程序、NDK、Flutter,
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
talProgressBarWithNumber mProgressBar;
private static final int MSG_PROGRESS_UPDATE = 0x110;
private Handler mHandler = new Handler() {
总结:
各行各样都会淘汰一些能力差的,不仅仅是IT这个行业,所以,不要被程序猿是吃青春饭等等这类话题所吓倒,也不要觉得,找到一份工作,就享受安逸的生活,你在安逸的同时,别人正在奋力的向前跑,这样与别人的差距也就会越来越遥远,加油,希望,我们每一个人,成为更好的自己。
-
BAT大厂面试题、独家面试工具包,
-
资料包括 数据结构、Kotlin、计算机网络、Framework源码、数据结构与算法、小程序、NDK、Flutter,
[外链图片转存中…(img-CZG7I1We-1714321231833)]
[外链图片转存中…(img-LYVIlZMu-1714321231833)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!