自定义View:重绘进度条

最近下大工夫功课自定义View这一关。我把自定义View划分为八个类别,写完这八个类别,我就基本上弄清楚自定义控件的门道了。以下是我自己划分的八个类别:

1.使用现有控件布局,对子控件进行格式化和监听,纯代码实现;
2.使用现有控件布局,对子控件进行格式化和监听,带布局文件和属性文件;
3.继承View,自己画一个,纯代码;
4.继承View,自己画一个,带属性文件;
5.继承现有控件,纯代码扩展;
6.继承现有控件,带属性文件扩展;
7.继承ViewGroup或者布局,实现对子控件的操作,纯代码;
8.继承ViewGroup或者布局,实现对子控件的操作,带属性文件。

实际上是四大类。只不过为了使用方便,我比较喜欢那种纯代码的自定义控件,方便移植。带着attrs和布局文件很是不方便。虽然说有依赖可以用,但是怎么也没有一个单文件方便。不过,为了方便布局,属性文件和布局文件也是不可少的。所以一类就分为两种实现方式。

昨天我简单实现了第一种,动态添加子控件,简单实现自己的监听事件,还开放了纯代码的设置接口,使用起来也算是比较灵活。今天这个主要是为了实现一个完全自己定义的进度条,没有实现监听,我先是使用纯代码来控制,后来想着布局方便,就把attrs和属性的获取,单位的转换全加上了。基本上有了这个进度条,自定义控件的门道大致就比较清楚了。

以下是代码:

首先把attrs展示出来:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--预设的属性-->
    <attr name="rule_color" format="color|integer" />
    <attr name="rule_height" format="dimension" />
    <attr name="padding" format="dimension" />
    <attr name="cursor_color" format="color|integer" />
    <attr name="cursor_radius" format="dimension" />

    <!--控件属性集合-->
    <declare-styleable name="ProgressBarView">
        <attr name="rule_color" />
        <attr name="rule_height" />
        <attr name="padding" />
        <attr name="cursor_color" />
        <attr name="cursor_radius" />
    </declare-styleable>
</resources>

然后就是源代码:

/**
 * 自定义progressBar
 * Created by Devin Chen on 2016/12/26.
 */

public class ProgressBarView extends View {
    public static final int DEFAULT_RULE_COLOR = 0xff444444;//默认标尺颜色
    public static final int DEFAULT_RULE_HEIGHT = 1;//默认标尺高度,即线条的粗细
    public static final int DEFAULT_PADDING = 10;//默认边距
    public static final int DEFAULT_CURSOR_COLOR = 0xffff4444;//默认游标颜色
    public static final int DEFAULT_CURSOR_RADIUS = 6;//默认游标半径

    private int mWidth;//控件宽
    private int mHeight;//控件高
    private int mMinWidth = 400;//最小宽度
    private int mMinHeight = 40;//最小高度

    private Paint mPaint;//画笔
    private int progress = 0;//进度值

    private int ruleColor = DEFAULT_RULE_COLOR;//标尺颜色
    private int ruleHeight = dp2px(DEFAULT_RULE_HEIGHT);//标尺高度,即线条的粗细
    private int mPadding = dp2px(DEFAULT_PADDING);//边距
    private int cursorColor = DEFAULT_CURSOR_COLOR;//游标颜色
    private int cursorRadius = dp2px(DEFAULT_CURSOR_RADIUS);//游标半径

    public ProgressBarView(Context context) {
        this(context, null);
    }

    public ProgressBarView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ProgressBarView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        obtainStyledAttrs(attrs);
        initialize();
    }

    /**
     * 获取布局的属性
     *
     * @param attrs
     */
    private void obtainStyledAttrs(AttributeSet attrs) {
        TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.ProgressBarView);

        ruleColor = array.getColor(R.styleable.ProgressBarView_rule_color, ruleColor);
        ruleHeight = (int) array.getDimension(R.styleable.ProgressBarView_rule_height, ruleHeight);
        mPadding = (int) array.getDimension(R.styleable.ProgressBarView_padding, mPadding);
        cursorColor = array.getColor(R.styleable.ProgressBarView_cursor_color, cursorColor);
        cursorRadius = (int) array.getDimension(R.styleable.ProgressBarView_cursor_radius, cursorRadius);

        array.recycle();
    }

    /**
     * 初始化
     */
    private void initialize() {
        initPaint();
    }

    private void initPaint() {
        mPaint = new Paint();
        mPaint.setStrokeWidth(ruleHeight);
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.STROKE);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = getDefaultWidth(widthMeasureSpec);
        mHeight = getDefaultHeight(heightMeasureSpec);
        setMeasuredDimension(mWidth, mHeight);
    }

    /**
     * 得到默认的控件宽度
     *
     * @param widthMeasureSpec
     * @return
     */
    private int getDefaultWidth(int widthMeasureSpec) {
        int hMode = MeasureSpec.getMode(widthMeasureSpec);
        int hSize = MeasureSpec.getSize(widthMeasureSpec);
        //如果用户设定了精确值。则按照精确值取值,否则返回最小宽度
        if (hMode == MeasureSpec.EXACTLY) {
            //宽度不能小于设定的最小值
            if (hSize < mMinWidth) {
                hSize = mMinWidth;
            }
            return hSize;
        } else {
            return mMinWidth;
        }
    }

    /**
     * 得到默认的控件高度
     *
     * @param heightMeasureSpec
     * @return
     */
    private int getDefaultHeight(int heightMeasureSpec) {
        int hMode = MeasureSpec.getMode(heightMeasureSpec);
        int hSize = MeasureSpec.getSize(heightMeasureSpec);

        if (hMode == MeasureSpec.EXACTLY) {
            if (hSize < mMinHeight) {
                hSize = mMinHeight;
            }
            return hSize;
        } else {
            return mMinHeight;
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.save();
        mPaint.setColor(ruleColor);
        mPaint.setStyle(Paint.Style.STROKE);
        //绘制标尺
        canvas.drawLine(mPadding, getHeight() / 2, mWidth - mPadding, mHeight / 2, mPaint);
        mPaint.setColor(cursorColor);
        mPaint.setStyle(Paint.Style.FILL);
        //绘制游标
        canvas.drawCircle(mPadding + (mWidth - mPadding * 2) * (progress / 100.0f), mHeight / 2, cursorRadius, mPaint);
        canvas.restore();
    }

    /**
     * 获取进度值
     *
     * @return
     */
    public int getProgress() {
        return progress;
    }

    /**
     * 设置进度值
     *
     * @param progress
     */
    public void setProgress(int progress) {
        if (progress <= 100) {
            this.progress = progress;
            invalidate();
        }
    }

    /**
     * sp转换成px
     *
     * @param dpValue
     * @return
     */
    private int dp2px(int dpValue) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, getResources().getDisplayMetrics());
    }

    /**
     * sp转换成px
     *
     * @param spValue
     * @return
     */
    private int sp2px(int spValue) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spValue, getResources().getDisplayMetrics());
    }
}

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_custom_view2"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#c7e2e2e2"
    android:orientation="vertical"
    tools:context="com.devin.customviewdemo.activity.CustomView2Activity">

    <Button
        android:id="@+id/btn_1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="开始" />
    <TextView
        android:id="@+id/txt_val"
        android:layout_width="wrap_content"
        android:layout_gravity="center"
        android:gravity="center"
        android:textColor="#da650b"
        android:textSize="20sp"
        android:layout_height="wrap_content" />

    <com.devin.customviewdemo.customview.ProgressBarView
        android:id="@+id/progress_1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#00ffffff"
        app:cursor_color="#cf870b"
        app:cursor_radius="5dp"
        app:padding="10dp"
        app:rule_color="#924daae8"
        app:rule_height="2dp" />
</LinearLayout>

最后是在程序中使用。为了看到效果,我们需要线程休眠。

/**
 * 自定义进度条的使用
 */
public class CustomView2Activity extends AppCompatActivity {

    @Bind(R.id.progress_1)
    ProgressBarView progress1;
    @Bind(R.id.btn_1)
    Button btn1;
    @Bind(R.id.txt_val)
    TextView txtVal;
    private int progress = 0;
    private Runnable mRunnable = new Runnable() {
        @Override
        public void run() {
            mHandler.sendEmptyMessage(0);
        }
    };
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (progress >= 100) {
                removeCallbacks(mRunnable);
            }
            progress1.setProgress(progress++);
            txtVal.setText(""+progress1.getProgress());
            postDelayed(mRunnable, 200);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_custom_view2);
        ButterKnife.bind(this);
        initView();
    }

    private void initView() {

    }

    @OnClick(R.id.btn_1)
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_1:
                progress = 0;
                mRunnable.run();
                break;
            default:
                break;
        }
    }
}

运行结果:




类似这样的自定义进度条,多数人会选择直接继承ProgressBar,而我是喜欢纯粹的用View来绘制。两者基本上是一样的,只不过不具备progressBar的优点。进度条还可以用两个控件叠加来绘制,这样需要继承帧布局,不过这样扩展起来要更方便一些。留待后一步研究。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值