Android圆形进度条控件-CircleSeekBar

1.引言

博主Android开发起步没多久,一脚踏入社会工作。对Android可以说是非常的喜欢,这里花了一天多的时间写了一个圆形进度条的控件,没有参考其他类似控件的实现方式。如果有什么好的建议,或者需要我改善的,希望大家能够指出,和你们一起进步哦。 另外项目我已经放到Git上,大家可以随意使用。CircleSeekBar项目地址:https://github.com/Hellobird/CircleSeekBar-For-Android。

2.预览图

感觉效果还可以吧,下面讲下主要的方法。

3.主要方法

首先该类是继承自View类的,重写了onDraw、onMeasure方法。onDraw主要控制界面的绘制,onMeasure主要计算了一些重要参数,列如进度条边框的Rect,View的高度宽度等。

3.1 onDraw()方法

该方法因为是绘制调用的方法,所以不能涉及大量的运算,或者声明变量。这里主要是包括进度的绘制,文字的绘制,以及淡入淡出效果、缩放效果效果的设置。进度绘制主要使用了drawArc()这个方法,可以根据自己的设定画弧。动画的刷新主要靠判断当当前进度还未到达目标进度时,会调用invalidate()继续刷新。我也不知道常用滑动效果是否是这样不断刷新出来的,如果有更好的方法务必请告诉我一下。
@Overrideprotected void onDraw(Canvas canvas) {// 判断当前角度偏移方向if (mCurrentAngle > mTargetAngle) {mCurrentAngle = mCurrentAngle - mVelocity;if (mCurrentAngle < mTargetAngle) {mCurrentAngle = mTargetAngle;}} else if (mCurrentAngle < mTargetAngle) {mCurrentAngle = mCurrentAngle + mVelocity;if (mCurrentAngle > mTargetAngle) {mCurrentAngle = mTargetAngle;}}float ratio = mCurrentAngle / 360f;// 设置透明度if (mFadeEnable) {int alpha = (int) ((mEndAlpha - mStartAlpha) * ratio);mProgressPaint.setAlpha(alpha);}// 设置二级进度缩放效果if (mZoomEnable) {zoomSProgressRect(ratio);}// 绘制二级进度条canvas.drawArc(mSProgressRect, 0, 360f, false, mSProgressPaint);// 绘制进度条canvas.drawArc(mProgressRect, mStartAngle, mCurrentAngle, mUseCenter,mProgressPaint);// 绘制字体if (mShowText) {String text = formatProgress(mCurrentAngle / 360f * mMaxProgress);mTextPaint.getTextBounds(text, 0, text.length(), mTextBounds);canvas.drawText(text, (getWidth() - mTextBounds.width()) >> 1,(getHeight() >> 1) + (mTextBounds.height() >> 1),mTextPaint);}// 如果当前进度不等于目标进度,继续绘制if (mCurrentAngle != mTargetAngle) {invalidate();}}

3.2 onMeasure()方法

该方法主要是计算控件所需空间,以及确定进度环绘制的位置。这里的计算凡是除以2的,我都用的位运算替代,因为这个比除法快多了,不过不知道是否有隐患,知道的高手希望能解答一下。

@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {/* 计算控件宽度与高度 */int widthMode = MeasureSpec.getMode(widthMeasureSpec);int widthSize = MeasureSpec.getSize(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);int width;int height;if (widthMode == MeasureSpec.EXACTLY) {width = widthSize;} else {int desired = (int) (getPaddingLeft()+ DimenUtils.dip2px(mContext, MIN_WIDTH) + getPaddingRight());width = desired;}if (heightMode == MeasureSpec.EXACTLY) {height = heightSize;} else {int desired = (int) (getPaddingTop()+ DimenUtils.dip2px(mContext, MIN_HEIGHT) + getPaddingBottom());height = desired;}setMeasuredDimension(width, height);/* 计算进度显示的矩形框 */float radius = width > height ? height >> 1 : width >> 1;float maxStrokeWidth = mProgressStrokeWidth > mSProgressStrokeWidth ? mProgressStrokeWidth: mSProgressStrokeWidth;radius = radius - getMaxPadding() - maxStrokeWidth;int centerX = width >> 1;int centerY = height >> 1;mProgressRect.set(centerX - radius, centerY - radius, centerX + radius,centerY + radius);mSProgressRect = new RectF(mProgressRect);

3.3 控件属性定义

                                                                                                                                                                                    

3.4总结

Android原生控件虽然少,但魅力就在可以随心所欲制定自己想要的控件和样式。这个方法里其实最重要的也就是onDraw和onMeasure方法,加起来一百来行代码,实现的效果感觉还不错。另外对控件有什么疑问或者是觉得可以改进和增加的效果,可以评论我哦。如果想要自己动手,也可以去git上挥洒你的代码。希望大家相互学习支持,让Android学习更加有趣。最后附上完整代码。

public class CircleSeekBar extends View {/* 最小宽度,单位为dp */private static int MIN_WIDTH = 50;/* 最小高度,单位为dp */private static int MIN_HEIGHT = 50;/* 默认模式 */public static int MODE_DEFAULT = 0;/* 笔画模式 */public static int MODE_STROKE = 0;/* 填充模式 */public static int MODE_FILL = 1;/* 笔画&填充模式 */public static int MODE_FILL_AND_STROKE = 2;/* 进度格式化默认值 */private static String PROGRESS_FORMAT_DEFAULT = "##0.0";/* 进度默认最大值 */private static float MAX_PROGRESS_DEFAULT = 100f;/* 开始位置角度默认值 */private static final float START_ANGLE_DEFAULT = 0f;/* 刷新滑动速度默认值 */private static final float VELOCITY_DEFAULT = 3.0f;/* 文字大小默认值,单位为sp */private static final float TEXT_SIZE_DEFAULT = 10.0f;/* 默认文字颜色 */private static final int TEXT_COLOR_DEFAULT = 0xffbf5252;/* 进度条边框宽度默认值,单位为dp */private static final float PROGRESS_WIDTH_DEFAULT = 5.0f;/* 默认进度颜色 */private static final int PROGRESS_COLOR_DEFAULT = 0xff3d85c6;/* 进度条底色默认值,单位为dp */private static final float S_PROGRESS_WIDTH_DEFAULT = 2.0f;/* 默认进度颜色 */private static final int S_PROGRESS_COLOR_DEFAULT = 0xffdddddd;private Context mContext;private Paint mPaint;private Paint mTextPaint;private Paint mProgressPaint;private Paint mSProgressPaint;private int mMode; // 进度模式private float mMaxProgress; // 最大进度private boolean mShowText; // 是否显示文字private float mStartAngle; // 起始角度private float mVelocity; // 速度private float mTextSize; // 字体大小private int mTextColor; // 字体颜色private float mProgressStrokeWidth; // 进度条宽度private int mProgressColor; // 进度颜色private float mSProgressStrokeWidth; // 二级进度宽度private int mSProgressColor; // 二级进度颜色private boolean mFadeEnable; // 是否开启淡入淡出效果private int mStartAlpha; // 开始透明度,0~255private int mEndAlpha; // 结束透明度,0~255private boolean mZoomEnable; // 二级进度缩放private RectF mProgressRect;private RectF mSProgressRect;private Rect mTextBounds;private float mCurrentAngle; // 当前角度private float mTargetAngle; // 目标角度private boolean mUseCenter; // 是否从中心绘制private DecimalFormat mFormat; // 格式化数值public CircleSeekBar(Context context, AttributeSet attrs) {this(context, attrs, 0);}public CircleSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);mContext = context;init(attrs);}private void init(AttributeSet attrs) {if (attrs != null) {TypedArray type = mContext.obtainStyledAttributes(attrs,R.styleable.CircleSeekBar);mMode = type.getInt(R.styleable.CircleSeekBar_mode, MODE_DEFAULT);mMaxProgress = type.getFloat(R.styleable.CircleSeekBar_maxProgress,MAX_PROGRESS_DEFAULT);mShowText = type.getBoolean(R.styleable.CircleSeekBar_showText,true);mStartAngle = type.getFloat(R.styleable.CircleSeekBar_startAngle,START_ANGLE_DEFAULT);mVelocity = type.getFloat(R.styleable.CircleSeekBar_velocity,VELOCITY_DEFAULT);mTextSize = type.getDimension(R.styleable.CircleSeekBar_textSize,DimenUtils.dip2px(mContext, TEXT_SIZE_DEFAULT));mTextColor = type.getColor(R.styleable.CircleSeekBar_textColor,TEXT_COLOR_DEFAULT);mProgressStrokeWidth = type.getDimension(R.styleable.CircleSeekBar_progressWidth,DimenUtils.dip2px(mContext, PROGRESS_WIDTH_DEFAULT));mProgressColor = type.getColor(R.styleable.CircleSeekBar_progressColor,PROGRESS_COLOR_DEFAULT);mSProgressStrokeWidth = type.getDimension(R.styleable.CircleSeekBar_sProgressWidth,DimenUtils.dip2px(mContext, S_PROGRESS_WIDTH_DEFAULT));mSProgressColor = type.getColor(R.styleable.CircleSeekBar_sProgressColor,S_PROGRESS_COLOR_DEFAULT);mFadeEnable = type.getBoolean(R.styleable.CircleSeekBar_fadeEnable,false);mStartAlpha = type.getInt(R.styleable.CircleSeekBar_startAlpha, 255);mEndAlpha = type.getInt(R.styleable.CircleSeekBar_endAlpha, 255);mZoomEnable = type.getBoolean(R.styleable.CircleSeekBar_zoomEnable,false);float progress = type.getFloat(R.styleable.CircleSeekBar_progress,0);progress = progress > mMaxProgress || progress < 0f ? 0f : progress;mTargetAngle = progress / mMaxProgress * 360f;mCurrentAngle = mTargetAngle;type.recycle();} else {mMode = MODE_DEFAULT;mMaxProgress = MAX_PROGRESS_DEFAULT;mStartAngle = START_ANGLE_DEFAULT;mVelocity = VELOCITY_DEFAULT;mTextSize = TEXT_SIZE_DEFAULT;mTextColor = TEXT_COLOR_DEFAULT;mProgressStrokeWidth = PROGRESS_WIDTH_DEFAULT;mProgressColor = PROGRESS_COLOR_DEFAULT;mSProgressStrokeWidth = S_PROGRESS_WIDTH_DEFAULT;mSProgressColor = S_PROGRESS_COLOR_DEFAULT;mTargetAngle = 0f;mCurrentAngle = 0f;mStartAlpha = 255;mEndAlpha = 255;mZoomEnable = false;}mPaint = new Paint();mPaint.setAntiAlias(true);mTextPaint = new Paint(mPaint);mTextPaint.setColor(mTextColor);mTextPaint.setTextSize(mTextSize);mProgressPaint = new Paint(mPaint);mProgressPaint.setColor(mProgressColor);mProgressPaint.setStrokeWidth(mProgressStrokeWidth);mSProgressPaint = new Paint(mProgressPaint);mSProgressPaint.setColor(mSProgressColor);mSProgressPaint.setStrokeWidth(mSProgressStrokeWidth);if (mMode == MODE_FILL_AND_STROKE) {mProgressPaint.setStyle(Style.FILL);mSProgressPaint.setStyle(Style.FILL_AND_STROKE);mUseCenter = true;} else if (mMode == MODE_FILL) {mProgressPaint.setStyle(Style.FILL);mSProgressPaint.setStyle(Style.FILL);mUseCenter = true;} else {mProgressPaint.setStyle(Style.STROKE);mSProgressPaint.setStyle(Style.STROKE);mUseCenter = false;}mProgressRect = new RectF();mTextBounds = new Rect();mFormat = new DecimalFormat(PROGRESS_FORMAT_DEFAULT);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {/* 计算控件宽度与高度 */int widthMode = MeasureSpec.getMode(widthMeasureSpec);int widthSize = MeasureSpec.getSize(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);int width;int height;if (widthMode == MeasureSpec.EXACTLY) {width = widthSize;} else {int desired = (int) (getPaddingLeft()+ DimenUtils.dip2px(mContext, MIN_WIDTH) + getPaddingRight());width = desired;}if (heightMode == MeasureSpec.EXACTLY) {height = heightSize;} else {int desired = (int) (getPaddingTop()+ DimenUtils.dip2px(mContext, MIN_HEIGHT) + getPaddingBottom());height = desired;}setMeasuredDimension(width, height);/* 计算进度显示的矩形框 */float radius = width > height ? height >> 1 : width >> 1;float maxStrokeWidth = mProgressStrokeWidth > mSProgressStrokeWidth ? mProgressStrokeWidth: mSProgressStrokeWidth;radius = radius - getMaxPadding() - maxStrokeWidth;int centerX = width >> 1;int centerY = height >> 1;mProgressRect.set(centerX - radius, centerY - radius, centerX + radius,centerY + radius);mSProgressRect = new RectF(mProgressRect);}@Overrideprotected void onDraw(Canvas canvas) {// 判断当前角度偏移方向if (mCurrentAngle > mTargetAngle) {mCurrentAngle = mCurrentAngle - mVelocity;if (mCurrentAngle < mTargetAngle) {mCurrentAngle = mTargetAngle;}} else if (mCurrentAngle < mTargetAngle) {mCurrentAngle = mCurrentAngle + mVelocity;if (mCurrentAngle > mTargetAngle) {mCurrentAngle = mTargetAngle;}}float ratio = mCurrentAngle / 360f;// 设置透明度if (mFadeEnable) {int alpha = (int) ((mEndAlpha - mStartAlpha) * ratio);mProgressPaint.setAlpha(alpha);}// 设置二级进度缩放效果if (mZoomEnable) {zoomSProgressRect(ratio);}// 绘制二级进度条canvas.drawArc(mSProgressRect, 0, 360f, false, mSProgressPaint);// 绘制进度条canvas.drawArc(mProgressRect, mStartAngle, mCurrentAngle, mUseCenter,mProgressPaint);// 绘制字体if (mShowText) {String text = formatProgress(mCurrentAngle / 360f * mMaxProgress);mTextPaint.getTextBounds(text, 0, text.length(), mTextBounds);canvas.drawText(text, (getWidth() - mTextBounds.width()) >> 1,(getHeight() >> 1) + (mTextBounds.height() >> 1),mTextPaint);}// 如果当前进度不等于目标进度,继续绘制if (mCurrentAngle != mTargetAngle) {invalidate();}}/** * 格式化进度 *  * @param progress * @return */private String formatProgress(float progress) {return mFormat.format(progress);}/** * 获取内边距最大值 *  * @return */private int getMaxPadding() {int maxPadding = getPaddingLeft();int paddingRight = getPaddingRight();int paddingTop = getPaddingTop();int paddingBottom = getPaddingBottom();if (maxPadding < paddingRight) {maxPadding = paddingRight;}if (maxPadding < paddingTop) {maxPadding = paddingTop;}if (maxPadding < paddingBottom) {maxPadding = paddingBottom;}return maxPadding;}/** * 缩放二级进度条 *  * @param ratio */private void zoomSProgressRect(float ratio) {float width = mProgressRect.width();float height = mProgressRect.height();float centerX = mProgressRect.centerX();float centerY = mProgressRect.centerY();float offsetX = width * 0.5f * ratio;float offsetY = height * 0.5f * ratio;float left = centerX - offsetX;float right = centerX + offsetX;float top = centerY - offsetY;float bottom = centerY + offsetY;mSProgressRect.set(left, top, right, bottom);}@Overrideprotected void onDisplayHint(int hint) {if (hint == View.VISIBLE) {mCurrentAngle = 0;invalidate();}super.onDisplayHint(hint);}/** * 设置目标进度 *  * @param progress */public void setProgress(float progress) {progress = progress > mMaxProgress || progress < 0f ? 0f : progress;mTargetAngle = progress / mMaxProgress * 360f;postInvalidate();}/** * 设置目标进度 *  * @param progress *            进度值 * @param isAnim *            是否有动画 */public void setProgressWithAnim(float progress, boolean isAnim) {if (isAnim) {setProgress(progress);} else {progress = progress > mMaxProgress || progress < 0f ? 0f : progress;mCurrentAngle = progress / mMaxProgress * 360f;mTargetAngle = mCurrentAngle;postInvalidate();}}/** * 设置进度画笔着色方式 *  * @param shader */public void setProgressShader(Shader shader) {this.mProgressPaint.setShader(shader);invalidate();}/** * 设置二级进度画笔着色方式 *  * @param shader */public void setSProgressShader(Shader shader) {this.mSProgressPaint.setShader(shader);invalidate();}public void setMaxProgress(float max) {this.mMaxProgress = max;}public float getMaxProgress() {return mMaxProgress;}public int getMode() {return mMode;}public void setMode(int mMode) {this.mMode = mMode;}public float getStartAngle() {return mStartAngle;}public void setStartAngle(float mStartAngle) {this.mStartAngle = mStartAngle;}public float getVelocity() {return mVelocity;}public void setVelocity(float mVelocity) {this.mVelocity = mVelocity;}public float getTextSize() {return mTextSize;}public void setTextSize(float mTextSize) {this.mTextSize = mTextSize;}public int getTextColor() {return mTextColor;}public void setTextColor(int mTextColor) {this.mTextColor = mTextColor;}public float getProgressStrokeWidth() {return mProgressStrokeWidth;}public void setProgressStrokeWidth(float mProgressStrokeWidth) {this.mProgressStrokeWidth = mProgressStrokeWidth;}public int getProgressColor() {return mProgressColor;}public void setProgressColor(int mProgressColor) {this.mProgressColor = mProgressColor;}public float getSProgressStrokeWidth() {return mSProgressStrokeWidth;}public void setSProgressStrokeWidth(float mSProgressStrokeWidth) {this.mSProgressStrokeWidth = mSProgressStrokeWidth;}public int getSProgressColor() {return mSProgressColor;}public void setSProgressColor(int mSProgressColor) {this.mSProgressColor = mSProgressColor;}public boolean isFadeEnable() {return mFadeEnable;}public void setFadeEnable(boolean mFadeEnable) {this.mFadeEnable = mFadeEnable;}public int getStartAlpha() {return mStartAlpha;}public void setStartAlpha(int mStartAlpha) {this.mStartAlpha = mStartAlpha;}public int getEndAlpha() {return mEndAlpha;}public void setEndAlpha(int mEndAlpha) {this.mEndAlpha = mEndAlpha;}public boolean isZoomEnable() {return mZoomEnable;}public void setZoomEnable(boolean mZoomEnable) {this.mZoomEnable = mZoomEnable;}}




更多相关文章

  1. android ndk 开发之 在 框架层使用 jni
  2. Android中各种Adapter的使用方法
  3. Android系列之GreenDao数据升级和加密(三)
  4. 安卓NDK——原生开发工具包
  5. Android(安卓)ListView使用BaseAdapter与ListView的优化
  6. 在Android上实现树形控件
  7. Android开发系列之调用WebService
  8. Android:快速修改ramdisk.img脚本
  9. Android(安卓)Studio中导入依赖库的方法

随机推荐

  1. Genymotion - 速度飞一般的Android虚拟机
  2. Android的MediaPlayer架构介绍
  3. Android与J2ME区别之我见(3)
  4. (推荐)彻底解析Android缓存机制——LruCach
  5. 写给Android开发者的Kotlin入门
  6. Android公共库选型 单元测试 依赖管理等
  7. Android之Handle全面理解
  8. Android使用Intent Filter来响应隐式Inte
  9. 5、控件系列之TOAST(吐司提示)的曾经、现在
  10. ADB WIFI DEBUG(ANDROID STUDIO)