当前位置:  首页  >  PHP资讯  >  业界资讯

Android应用中炫酷的横向和环形进度条的实例分享

这篇文章主要介绍了Android应用中炫酷的横向和圆形进度条的实例分享,文中利用了一些GitHub上的插件进行改写,也是一片很好的二次开发教学,需要的朋友可以参考下

一、概述
最近需要用进度条,秉着不重复造轮子的原则,上github上搜索了一番,看了几个觉得比较好看的ProgressBar,比如:daimajia的等。简单看了下代码,基本都是继承自View,彻彻底底的自定义了一个进度条。盯着那绚丽滚动条,忽然觉得,为什么要通过View去写一个滚动条,系统已经提供了ProgressBar以及属于它的特性,我们没必要重新去构建一个,但是系统的又比较丑,不同版本变现还不一定一样。那么得出我们的目标:改变系统ProgressBar的样子。
对没错,我们没有必要去从0打造一个ProgressBar,人家虽然长的不好看,但是特性以及稳定性还是刚刚的,我们只需要为其整下容就ok了。
接下来,我们贴下效果图:
1、横向的进度条

2、圆形的进度条

2016419170129190.jpg (414×620)

没错,这就是我们的进度条效果,横向的模仿了daimajia的进度条样子。不过我们继承子ProgressBar,简单的为其整个容,代码清晰易懂 。为什么说,易懂呢?
横向那个进度条,大家会drawLine()和drawText()吧,那么通过getWidth()拿到控件的宽度,再通过getProgress()拿到进度,按比例控制绘制线的长短,字的位置还不是分分钟的事。

二、实现
 横向的滚动条绘制肯定需要一些属性,比如已/未到达进度的颜色、宽度,文本的颜色、大小等。
 本来呢,我是想通过系统ProgressBar的progressDrawable,从里面提取一些属性完成绘制需要的参数的。但是,最终呢,反而让代码变得复杂。所以最终还是改用自定义属性。 说道自定义属性,大家应该已经不陌生了。
1、HorizontalProgressBarWithNumber
(1)自定义属性
values/attr_progress_bar.xml:

 <&#63;xml version="1.0" encoding="utf-8"&#63;>                   

(2)构造中获取

 public class HorizontalProgressBarWithNumber extends ProgressBar { private static final int DEFAULT_TEXT_SIZE = 10; private static final int DEFAULT_TEXT_COLOR = 0XFFFC00D1; private static final int DEFAULT_COLOR_UNREACHED_COLOR = 0xFFd3d6da; private static final int DEFAULT_HEIGHT_REACHED_PROGRESS_BAR = 2; private static final int DEFAULT_HEIGHT_UNREACHED_PROGRESS_BAR = 2; private static final int DEFAULT_SIZE_TEXT_OFFSET = 10; /** * painter of all drawing things */ protected Paint mPaint = new Paint(); /** * color of progress number */ protected int mTextColor = DEFAULT_TEXT_COLOR; /** * size of text (sp) */ protected int mTextSize = sp2px(DEFAULT_TEXT_SIZE); /** * offset of draw progress */ protected int mTextOffset = dp2px(DEFAULT_SIZE_TEXT_OFFSET); /** * height of reached progress bar */ protected int mReachedProgressBarHeight = dp2px(DEFAULT_HEIGHT_REACHED_PROGRESS_BAR); /** * color of reached bar */ protected int mReachedBarColor = DEFAULT_TEXT_COLOR; /** * color of unreached bar */ protected int mUnReachedBarColor = DEFAULT_COLOR_UNREACHED_COLOR; /** * height of unreached progress bar */ protected int mUnReachedProgressBarHeight = dp2px(DEFAULT_HEIGHT_UNREACHED_PROGRESS_BAR); /** * view width except padding */ protected int mRealWidth; protected boolean mIfDrawText = true; protected static final int VISIBLE = 0; public HorizontalProgressBarWithNumber(Context context, AttributeSet attrs) { this(context, attrs, 0); } public HorizontalProgressBarWithNumber(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setHorizontalScrollBarEnabled(true); obtainStyledAttributes(attrs); mPaint.setTextSize(mTextSize); mPaint.setColor(mTextColor); } /** * get the styled attributes * * @param attrs */ private void obtainStyledAttributes(AttributeSet attrs) { // init values from custom attributes final TypedArray attributes = getContext().obtainStyledAttributes( attrs, R.styleable.HorizontalProgressBarWithNumber); mTextColor = attributes .getColor( R.styleable.HorizontalProgressBarWithNumber_progress_text_color, DEFAULT_TEXT_COLOR); mTextSize = (int) attributes.getDimension( R.styleable.HorizontalProgressBarWithNumber_progress_text_size, mTextSize); mReachedBarColor = attributes .getColor( R.styleable.HorizontalProgressBarWithNumber_progress_reached_color, mTextColor); mUnReachedBarColor = attributes .getColor( R.styleable.HorizontalProgressBarWithNumber_progress_unreached_color, DEFAULT_COLOR_UNREACHED_COLOR); mReachedProgressBarHeight = (int) attributes .getDimension( R.styleable.HorizontalProgressBarWithNumber_progress_reached_bar_height, mReachedProgressBarHeight); mUnReachedProgressBarHeight = (int) attributes .getDimension( R.styleable.HorizontalProgressBarWithNumber_progress_unreached_bar_height, mUnReachedProgressBarHeight); mTextOffset = (int) attributes .getDimension( R.styleable.HorizontalProgressBarWithNumber_progress_text_offset, mTextOffset); int textVisible = attributes .getInt(R.styleable.HorizontalProgressBarWithNumber_progress_text_visibility, VISIBLE); if (textVisible != VISIBLE) { mIfDrawText = false; } 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用于获取公共的属性;然后不同样子的进度条继承分别实现自己的测量和样子,这样结构可能会清晰些~~~

三、使用
布局文件

         

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() { @Log public void handleMessage(android.os.Message msg) { int progress = mProgressBar.getProgress(); mProgressBar.setProgress(++progress); if (progress >= 100) { mHandler.removeMessages(MSG_PROGRESS_UPDATE); } mHandler.sendEmptyMessageDelayed(MSG_PROGRESS_UPDATE, 100); }; }; @Log @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mProgressBar = (HorizontalProgressBarWithNumber) findViewById(R.id.id_progressbar01); mHandler.sendEmptyMessage(MSG_PROGRESS_UPDATE); } } 
内容推荐:免费高清PNG素材下载
吐了个 "CAO" !
扫码关注 PHP1 官方微信号
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved PHP1.CN 第一PHP社区 版权所有 京ICP备19059560号-4