Android 自定义进度条

来源:互联网 发布:mac如何更新系统版本 编辑:程序博客网 时间:2024/06/05 19:31

Android 自定义进度条

为什么要自定义控件:

  1. 特定的显示风格
  2. 处理特有的用户交互
  3. 优化我们的布局
  4. 封装

如何自定义控件:

  1. 自定义属性的声明与获取
  2. 测量onMeasure
  3. 布局onLayout(ViewGroup)
  4. 绘制onDraw
  5. onTouch
  6. onInterceptTouchEvent(ViewGroup)
  7. 状态的恢复与保存

这里写图片描述

从上面这张图来分析需要自定义哪些属性

  • 分析自定义属性:
    • 左边进度条的颜色、宽度
    • 右边进度条的颜色、宽度
    • 中间文字的颜色、字体大小
    • 文字与进度条的间隙
  • 在 res/values/attrs.xml 定义声明
  • 在layout 文件中使用
  • 在View 的构造方法中进行获取

水平进度条

  1. 自定义属性,在values文件夹下建立一个attrs.xml 资源文件,并声明属性
  2. 建立一个自定义View类,并添加两个参数的构造器
  3. 在attrs.xml 文件中声明该view使用那些属性

attrs.xml文件内容

<?xml version="1.0" encoding="utf-8"?><resources>    <!--声明属性-->    <attr name="progress_unreach_color" format="color"></attr>    <attr name="progress_unreach_height" format="dimension"></attr>    <attr name="progress_reach_color" format="color"></attr>    <attr name="progress_reach_height" format="dimension"></attr>    <attr name="progress_text_color" format="color"></attr>    <attr name="progress_text_size" format="dimension"></attr>    <attr name="progress_text_offset" format="dimension"></attr>    <!--在自定义view中使用定义的属性-->    <declare-styleable name="HorizontalProgressbarWithProgress">        <attr name="progress_unreach_color" ></attr>        <attr name="progress_unreach_height" ></attr>        <attr name="progress_reach_color" ></attr>        <attr name="progress_reach_height" ></attr>        <attr name="progress_text_color" ></attr>        <attr name="progress_text_size" ></attr>        <attr name="progress_text_offset" ></attr>    </declare-styleable></resources>

自定义view 一步步实现

  1. 构造器
public HorizontalProgressbarWithProgress(Context context) {    this(context, null);}public HorizontalProgressbarWithProgress(Context context, AttributeSet attrs) {    this(context, attrs, 0);}public HorizontalProgressbarWithProgress(Context context, AttributeSet attrs, int defStyleAttr) {    super(context, attrs, defStyleAttr);    mTextSize = textSize;}
  1. 默认属性值,和上面自定义属性值对应
private static final int DEFAULT_TEXT_SIZE = 10; //spprivate static final int DEFAULT_TEXT_COLOE = 0xFFFC00D1;private static final int DEFAULT_COLOR_UNREACH = 0xFFD3D6DA;private static final int DEFAULT_HEIGHT_UNREACH = 2; //dpprivate static final int DEFAULT_COLOR_REACH = DEFAULT_TEXT_COLOE;private static final int DEFAULT_HEIGHT_REACH = 2; //dpprivate static final int DEFAULT_TEXT_OFFSET = 10; //dp
  1. 两个单位转换工具方法 ——dp2px、sp2dp,实际绘制时使用像素来绘制,因此需要把dp、sp等单位转为sp
// dp 单位转为px    private int dp2px(int dpval) {        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpval, getResources().getDisplayMetrics());    }    // sp 转为px    private int sp2px(int spval) {        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spval, getResources().getDisplayMetrics());    }
  1. 将默认属性值转为像素值
// 像素单位值private int mTextSize = sp2px(DEFAULT_TEXT_SIZE);private int mTextColor = DEFAULT_TEXT_COLOE;private int mUnreachColor = DEFAULT_COLOR_UNREACH;private int mUnreachHeight = dp2px(DEFAULT_HEIGHT_UNREACH);private int mReachColor = DEFAULT_COLOR_REACH;private int mReachHeight = dp2px(DEFAULT_HEIGHT_REACH);private int mTextOffset = dp2px(DEFAULT_TEXT_OFFSET);
  1. 其他类属性
private Paint mPaint = new Paint();private int mRealWidth; // 控件实际可绘制宽度,控件宽度 - padding,                            // 在onMeasure中赋值,在onDraw中使用
  1. 在构造函数中获取属性,这里我单独写一个方法,在构造器中调用
/*** 获取自定义属性* @param attrs*/private void obtainStyleAttrs(AttributeSet attrs) {        TypedArray ta = getContext().obtainStyledAttributes(attrs,                R.styleable.HorizontalProgressbarWithProgress);        mTextSize = (int) ta.getDimension(                R.styleable.HorizontalProgressbarWithProgress_progress_text_size,                mTextSize);        mTextColor = ta.getColor(                R.styleable.HorizontalProgressbarWithProgress_progress_text_color,                mTextColor);        mTextOffset = (int) ta.getDimension(               R.styleable.HorizontalProgressbarWithProgress_progress_text_offset,                mTextOffset);        mUnreachColor = ta.getColor(              R.styleable.HorizontalProgressbarWithProgress_progress_unreach_color,                mUnreachColor);        mUnreachHeight = (int) ta.getDimension(                R.styleable.HorizontalProgressbarWithProgress_progress_unreach_height,                mUnreachHeight);        mReachColor = ta.getColor(                R.styleable.HorizontalProgressbarWithProgress_progress_reach_color,                mReachColor);        mReachHeight = (int) ta.getDimension(                R.styleable.HorizontalProgressbarWithProgress_progress_reach_height,                mReachHeight);}
  1. 尺寸测量
@Override    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        int widthMode = MeasureSpec.getMode(widthMeasureSpec);        int widthval = MeasureSpec.getSize(widthMeasureSpec);        int height = measureHeight(heightMeasureSpec);        // 设置测量的宽度和高度        setMeasuredDimension(widthval, height);        mRealWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();    }    private int measureHeight(int heightMeasureSpec) {        int result = 0;        int mode = MeasureSpec.getMode(heightMeasureSpec);        int size = MeasureSpec.getSize(heightMeasureSpec);        // 给定确定的值        if (mode == MeasureSpec.EXACTLY) {            result = size;        } else {            // 自己计算值            int textHeight = (int) (mPaint.descent() - mPaint.ascent());            result = getPaddingTop() + // 上边距                    getPaddingBottom() + // 下边距                    Math.max(Math.max(mReachHeight, mUnreachHeight),                            Math.abs(textHeight)); //            // 为该模式,计算的值不能超过给定的size            if (mode == MeasureSpec.AT_MOST) {                result = Math.min(result, size);            }        }        return result;    }
  1. 实现onDraw方法
 @Override    protected synchronized void onDraw(Canvas canvas) {        canvas.save();        // 移动画布        canvas.translate(getPaddingLeft(), getHeight()/2);        boolean noNeedUnrech = false;        String text = getProgress() + "%";        // 进度条进度比例        float radio = getProgress() * 1.0f / getMax();        // 测量文字宽度        int textWidth = (int) mPaint.measureText(text);        // 计算已完成进度条宽度        float progressX = radio * mRealWidth;        // 判断是否绘制为完成进度条        if (progressX + textWidth > mRealWidth) {            progressX = mRealWidth - textWidth;            noNeedUnrech = true;        }        // 绘制已完成进度条        float endX =  progressX - mTextOffset / 2;        if (endX > 0) {            mPaint.setColor(mReachColor);            mPaint.setStrokeWidth(mReachHeight);            canvas.drawLine(0, 0, endX, 0, mPaint);        }        // 绘制文本        mPaint.setColor(mTextColor);        // 让文字基线处于中心        int y = (int) (-(mPaint.descent() + mPaint.ascent())/2);        canvas.drawText(text, progressX, y, mPaint);        if (!noNeedUnrech) {            float  start = (int) (progressX + textWidth + mTextOffset/2);            mPaint.setColor(mUnreachColor);            mPaint.setStrokeWidth(mUnreachHeight);            canvas.drawLine(start, 0, mRealWidth, 0, mPaint);        }        canvas.restore();    }
  1. 在activity中和其他系统控件一样使用

    <?xml version="1.0" encoding="utf-8"?><ScrollView   xmlns:android="http://schemas.android.com/apk/res/android"   <!-- 给自定义属性添加命名空间 -->   xmlns:hyman="http://schemas.android.com/apk/res-auto"   xmlns:tools="http://schemas.android.com/tools"   android:layout_width="match_parent"   android:layout_height="match_parent"   tools:context="com.example.song.progressbar.MainActivity">   <LinearLayout       android:layout_width="match_parent"       android:layout_height="wrap_content"       android:orientation="vertical"       >       <com.example.view.HorizontalProgressbarWithProgress           android:id="@+id/id_prohress01"           android:layout_width="match_parent"           android:layout_height="wrap_content"           android:progress="50"           android:padding="5dp"           android:layout_marginTop="30dp"           hyman:progress_reach_color="#ffff0000"           hyman:progress_text_color="#ffff0000"           hyman:progress_unreach_color="#44ff0000"           /></ScrollView>

圆形进度条

该实现方法和水平进度条类似,只是绘制不同形状,在这里添加了一个圆形半径的属性,且继承自水平进度条,在测量尺寸时,没有像水平进度条那样对测量模式、尺寸进行详细的测量,而是使用resolveSize(except, widthMeasureSpec);方法。

public class RoundProgressbarWithProgress extends HorizontalProgressbarWithProgress {    private int mRadius ;    private int mRealWidth;    private int mMaxPaintWidth;    public RoundProgressbarWithProgress(Context context) {        this(context, null);    }    public RoundProgressbarWithProgress(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public RoundProgressbarWithProgress(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        TypedArray ta = getContext().obtainStyledAttributes(attrs,                R.styleable.RoundProgressbarWithProgress);        mRadius = (int) ta.getDimension(R.styleable.RoundProgressbarWithProgress_radius,                20);        ta.recycle();    }    @Override    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        // 获取两个画笔的最大值,        mMaxPaintWidth = Math.max(mReachHeight, mUnreachHeight);        // 计算期望的控件大小        int except = mRadius * 2 + mMaxPaintWidth + getPaddingLeft() + getPaddingRight();        // 解析控件实际尺寸        int width = resolveSize(except, widthMeasureSpec);        int height = resolveSize(except, heightMeasureSpec);        mRealWidth = Math.max(width, height);        mRadius = (mRealWidth - getPaddingLeft() - getPaddingRight() - mMaxPaintWidth) / 2;        setMeasuredDimension(mRealWidth, mRealWidth);    }    @Override    protected synchronized void onDraw(Canvas canvas) {        canvas.save();        canvas.translate(getPaddingLeft()+mMaxPaintWidth/2, getPaddingLeft() + mMaxPaintWidth/2);        String text = getProgress() + "%";        int textWidth = (int) mPaint.measureText(text);        int textHeight = (int) ((mPaint.descent() +  mPaint.ascent()) / 2);        mPaint.setStyle(Paint.Style.STROKE);        // 绘制未完成进度条的圆        mPaint.setColor(mUnreachColor);        mPaint.setStrokeWidth(mUnreachHeight);        mPaint.setAntiAlias(true);        mPaint.setDither(false);        canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);        // 绘制已完成圆弧        mPaint.setColor(mReachColor);        mPaint.setStrokeWidth(mReachHeight);        float sweepAngle = getProgress() * 1.0f / getMax() * 360;        canvas.drawArc(new RectF(0, 0, mRadius * 2, mRadius * 2),                0, sweepAngle, false, mPaint);        // 绘制文本        mPaint.setColor(mTextColor);        mPaint.setStyle(Paint.Style.FILL);        canvas.drawText(text, mRadius - textWidth/2, mRadius - textHeight/2, mPaint);        canvas.restore();    }}
原创粉丝点击