Android 自定义进度条

Android 自定义进度条

文章目录

    • Android 自定义进度条
      • 效果
      • 目录
      • 前言
      • 正文
        • `HorizontalProgressBarWithNumber`
        • 实现
      • 总结
      • 感谢

效果

国际惯例,效果图奉上

Android 自定义进度条_第1张图片

目录

Android 自定义进度条_第2张图片

前言

写在前面,由于之前其实已经写了部分自定义View的方法,所以本来应该按照之前的系列,来进行下载暂停动画进度条,但是我把之前的圆形进度条和开始暂停动画效果合并后,出现了一点小问题,让我发现之前写的自定义View,没有使我真正的了解自定义View,那么我觉得还是有很大的问题;那么之后依旧会努力的写自定义View,初步先写静态的自定义View,之后,加上动画的自定义VIew,后续在加上相应的触摸事件,努力的把自定义VIew的方法方式全部给记录下来;努力的融汇贯通。

正文

HorizontalProgressBarWithNumber

横向的进度条;里面有详细的注释

  1. 首先继承自ProgressBar,这个是对基础的ProgressBar 进行进一步的自定义,

    public class HorizontalProgressBarWithNumber extends ProgressBar{
        ******
    }
    
  2. 当我们想要自定义VIew的时候,先要构思效果,那么就要设置各种属性,如下

/**
	 * 设置各种默认值
	 */
	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 未完成的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  除padding外的视图宽度
	 */
	protected int mRealWidth;

	protected boolean mIfDrawText = true;

	protected static final int VISIBLE = 0;

  1. 自定义构造函数
	public HorizontalProgressBarWithNumber(Context context, AttributeSet attrs)
	{
		this(context, attrs, 0);
	}

	public HorizontalProgressBarWithNumber(Context context, AttributeSet attrs,
			int defStyle)
	{
		super(context, attrs, defStyle);
		obtainStyledAttributes(attrs);//初始化参数
		mPaint.setTextSize(mTextSize);//文本大小
		mPaint.setColor(mTextColor);//文本颜色
	}
  1. 接下来就是初始化的代码:
/**
	 * 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();
	}

  1. 上面当中的参数和属性都在attr.xml中声明的,如下
<resources>

   <declare-styleable name="HorizontalProgressBarWithNumber">
       <attr name="progress_unreached_color" format="color" />
       <attr name="progress_reached_color" format="color" />
       <attr name="progress_reached_bar_height" format="dimension" />
       <attr name="progress_unreached_bar_height" format="dimension" />
       <attr name="progress_text_size" format="dimension" />
       <attr name="progress_text_color" format="color" />
       <attr name="progress_text_offset" format="dimension" />
       <attr name="progress_text_visibility" format="enum">
           <enum name="visible" value="0" />
           <enum name="invisible" value="1" />
       attr>
   declare-styleable>
   
   <declare-styleable name="RoundProgressBarWidthNumber">
       <attr name="radius" format="dimension" />
   declare-styleable>

resources>
  1. 接下来,我们要重写onMeasure方法,在其中获取父控件传递过来的参数

    	protected synchronized void onMeasure(int widthMeasureSpec,
    			int heightMeasureSpec)
    	{
    		int width = MeasureSpec.getSize(widthMeasureSpec);
    		int height = measureHeight(heightMeasureSpec);//高度
    		setMeasuredDimension(width, height);//必须调用该方法来存储View经过测量的到的宽度和高度
    
    		mRealWidth = getMeasuredWidth() - getPaddingRight() - getPaddingLeft();//真正的宽度值是减去左右padding
    	}
    
  2. 其中measureHeight() 方法是获取视图的高度,视图的样式中拥有进度条和文本,要判断文本的高度和进度条的高度;


	/**
	 * 	EXACTLY:父控件告诉我们子控件了一个确定的大小,你就按这个大小来布局。比如我们指定了确定的dp值和macth_parent的情况。 
	 *	AT_MOST:当前控件不能超过一个固定的最大值,一般是wrap_content的情况。 
	 *	UNSPECIFIED:当前控件没有限制,要多大就有多大,这种情况很少出现。
	 * @param measureSpec  
	 * @return  视图的高度
	 */
	private int measureHeight(int measureSpec)
	{
		
		int result = 0;
		int specMode = MeasureSpec.getMode(measureSpec);//父布局告诉我们控件的类型
		int specSize = MeasureSpec.getSize(measureSpec);//父布局传过来的视图大小
		if (specMode == MeasureSpec.EXACTLY)
		{
			result = specSize;
		} else
		{
			/**
			 * mPaint.descent() 最高点的高度
			 * mPaint.ascent() 最低点的高度
			 */
			float textHeight = (mPaint.descent() - mPaint.ascent());// 设置文本的高度
			/**
			 * Math.abs() 返回绝对值
			 *  Math.max 返回最大值
			 *  Math.min 返回最小值
			 */
			result = (int) (getPaddingTop() + getPaddingBottom() + Math.max(
					Math.max(mReachedProgressBarHeight,
							mUnReachedProgressBarHeight), Math.abs(textHeight)));
			if (specMode == MeasureSpec.AT_MOST)
			{
				result = Math.min(result, specSize);
			}
		}
		return result;
	}
  1. 以上全部完成后,就可以开始onDraw()方法了;
	
	/**
	 * 开始画
	 */
	@Override
	protected synchronized void onDraw(Canvas canvas)
	{

		canvas.save();
		/**
		 * 设置偏移后的坐标原点 以原来为基础上偏移后, 例如: (100,100), translate(1,1), 坐标原点(101,101);
		 */
		canvas.translate(getPaddingLeft(), getHeight() / 2);
		boolean noNeedBg = false;
		float radio = getProgress() * 1.0f / getMax();//设置进度
		float progressPosX = (int) (mRealWidth * radio);//设置当前进度的宽度
		String text = getProgress() + "%";//设置文本
//		 mPaint.getTextBounds(text, 0, text.length(), mTextBound);

		float textWidth = mPaint.measureText(text);//返回文本的宽度
		float textHeight = (mPaint.descent() + mPaint.ascent()) / 2;//设置文本的高度

		if (progressPosX + textWidth > mRealWidth)
		{//当文本和当前进度的宽度大于bar的宽度时
			progressPosX = mRealWidth - textWidth;
			noNeedBg = true;
		}

		// draw reached bar   画出bar
		float endX = progressPosX - mTextOffset / 2;//绘制已到达的进度
		if (endX > 0)
		{//绘制已到达的进度
			mPaint.setColor(mReachedBarColor);
			mPaint.setStrokeWidth(mReachedProgressBarHeight);
			canvas.drawLine(0, 0, endX, 0, mPaint);
		}
		// draw progress bar
		// measure text bound
		if (mIfDrawText)
		{//绘制文本
			mPaint.setColor(mTextColor);
			canvas.drawText(text, progressPosX, -textHeight, mPaint);
		}

		// draw unreached bar
		if (!noNeedBg)
		{//绘制未到达的进度条
			float start = progressPosX + mTextOffset / 2 + textWidth;
			mPaint.setColor(mUnReachedBarColor);
			mPaint.setStrokeWidth(mUnReachedProgressBarHeight);
			canvas.drawLine(start, 0, mRealWidth, 0, mPaint);
		}
		canvas.restore();
	}
  1. 在分享两个转换的方法
/**
	 * 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());

	}
  1. 最后自定义View 全部实现,奉上全部代码
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 未完成的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  除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);
		obtainStyledAttributes(attrs);//初始化参数
		mPaint.setTextSize(mTextSize);//文本大小
		mPaint.setColor(mTextColor);//文本颜色
	}

	@Override
	protected synchronized void onMeasure(int widthMeasureSpec,
			int heightMeasureSpec)
	{
		int width = MeasureSpec.getSize(widthMeasureSpec);
		int height = measureHeight(heightMeasureSpec);//高度
		setMeasuredDimension(width, height);//必须调用该方法来存储View经过测量的到的宽度和高度

		mRealWidth = getMeasuredWidth() - getPaddingRight() - getPaddingLeft();//真正的宽度值是减去左右padding
	}


	/**
	 * 	EXACTLY:父控件告诉我们子控件了一个确定的大小,你就按这个大小来布局。比如我们指定了确定的dp值和macth_parent的情况。 
	 *	AT_MOST:当前控件不能超过一个固定的最大值,一般是wrap_content的情况。 
	 *	UNSPECIFIED:当前控件没有限制,要多大就有多大,这种情况很少出现。
	 * @param measureSpec  
	 * @return  视图的高度
	 */
	private int measureHeight(int measureSpec)
	{
		
		int result = 0;
		int specMode = MeasureSpec.getMode(measureSpec);//父布局告诉我们控件的类型
		int specSize = MeasureSpec.getSize(measureSpec);//父布局传过来的视图大小
		if (specMode == MeasureSpec.EXACTLY)
		{
			result = specSize;
		} else
		{
			/**
			 * mPaint.descent() 最高点的高度
			 * mPaint.ascent() 最低点的高度
			 */
			float textHeight = (mPaint.descent() - mPaint.ascent());// 设置文本的高度
			/**
			 * Math.abs() 返回绝对值
			 *  Math.max 返回最大值
			 *  Math.min 返回最小值
			 */
			result = (int) (getPaddingTop() + getPaddingBottom() + Math.max(
					Math.max(mReachedProgressBarHeight,
							mUnReachedProgressBarHeight), Math.abs(textHeight)));
			if (specMode == MeasureSpec.AT_MOST)
			{
				result = Math.min(result, specSize);
			}
		}
		return result;
	}

	/**
	 * 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();
	}

	
	/**
	 * 开始画
	 */
	@Override
	protected synchronized void onDraw(Canvas canvas)
	{

		canvas.save();
		/**
		 * 设置偏移后的坐标原点 以原来为基础上偏移后, 例如: (100,100), translate(1,1), 坐标原点(101,101);
		 */
		canvas.translate(getPaddingLeft(), getHeight() / 2);
		boolean noNeedBg = false;
		float radio = getProgress() * 1.0f / getMax();//设置进度
		float progressPosX = (int) (mRealWidth * radio);//设置当前进度的宽度
		String text = getProgress() + "%";//设置文本
//		 mPaint.getTextBounds(text, 0, text.length(), mTextBound);

		float textWidth = mPaint.measureText(text);//返回文本的宽度
		float textHeight = (mPaint.descent() + mPaint.ascent()) / 2;//设置文本的高度

		if (progressPosX + textWidth > mRealWidth)
		{//当文本和当前进度的宽度大于bar的宽度时
			progressPosX = mRealWidth - textWidth;
			noNeedBg = true;
		}

		// draw reached bar   画出bar
		float endX = progressPosX - mTextOffset / 2;//设置文本补偿宽度
		if (endX > 0)
		{
			mPaint.setColor(mReachedBarColor);
			mPaint.setStrokeWidth(mReachedProgressBarHeight);
			canvas.drawLine(0, 0, endX, 0, mPaint);
		}
		// draw progress bar
		// measure text bound
		if (mIfDrawText)
		{
			mPaint.setColor(mTextColor);
			canvas.drawText(text, progressPosX, -textHeight, mPaint);
		}

		// draw unreached bar
		if (!noNeedBg)
		{
			float start = progressPosX + mTextOffset / 2 + textWidth;
			mPaint.setColor(mUnReachedBarColor);
			mPaint.setStrokeWidth(mUnReachedProgressBarHeight);
			canvas.drawLine(start, 0, mRealWidth, 0, mPaint);
		}

		canvas.restore();

	}

	/**
	 * 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());

	}
	
	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
		mRealWidth = w - getPaddingRight() - getPaddingLeft();
	}

}

实现

  1. xml界面



            

  1. mainActivity() 实现
public class MainActivity extends Activity {
private HorizontalProgressBarWithNumber mProgressBar;

private Handler mHandler = new Handler() {
		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);
		};
	};
	@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);
	}

  1. 以上就是全部功能的实现了

总结

自定义View

  1. 先构思效果
  2. 根据效果 , 声明配置相应参数
  3. 想好怎么计算View的宽度和高度
  4. 如果画出来
  5. 开始做吧

感谢

这个是从鸿阳大神的Github上找到的例子,嗯,值得学习,感谢鸿阳大神;

你可能感兴趣的:(Android,Java)