Android 自定义进度条

本文详细介绍了一种自定义横向进度条的实现方法,包括继承ProgressBar、设置默认值、获取属性样式、重写onMeasure和onDraw方法等步骤,以及如何在Android中调整进度条的外观和行为。

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

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

public class HorizontalProgressBarWithNumber extends ProgressBar{
******
}
当我们想要自定义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;

自定义构造函数
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);//文本颜色
}

接下来就是初始化的代码:
/**
* 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();
}

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

接下来,我们要重写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

}
其中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;
}

以上全部完成后,就可以开始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();
}

在分享两个转换的方法
/**
* 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 全部实现,奉上全部代码
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();
}

}

实现
xml界面

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号http://www.kinghill.cn/LongHuaDaDao1Hao/index.html

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值