Android 自定义View——圆形进度

本文介绍了一种自定义安卓View实现圆形进度条的方法,包括属性定义、测量、绘制及进度更新等步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

很多app在刚进入app时的欢迎页后的广告页右上角有一个点击跳过的按钮,印象中好像也见过圆形的点击跳过,还有圆形进度显示,准备在项目中也加入这个效果,于是自己花点时间写了个类似效果的自定义View,先来一发效果图:
此处输入图片的描述

这个View是个圆形,且有环形进度,中心有文字进度,所以我选择继承自原始View,原理就是根据时间的流逝,计算进度,重绘View。
主要分为4个部分:

  • 自定义View属性
  • 重写OnMeasure
  • 重写OnDraw
  • 更新进度
1. 自定义View所需属性

在res/values/attr.xml中

<?xml version="1.0" encoding="utf-8"?>
<resources>


    <declare-styleable name="CustomCircleProgress">
        <attr name="centerText" format="string"/>
        <attr name="centerTextColor" format="color"/>
        <attr name="centerTextSize" format="dimension"/>
        <attr name="circleBackground" format="color" />
        <attr name="ringProgressColor" format="color"/>
        <attr name="ringProgressWidth" format="dimension"/>
        <attr name="ringProgressBackground" format="color"/>
    </declare-styleable>

    <declare-styleable name="CustomView2">
        <attr name="contentText" format="string"/>
        <attr name="contentTextColor" format="color"/>
        <attr name="contentTextSize" format="dimension"/>
    </declare-styleable>
</resources>

定义属性变量,构造方法中获取属性


/*
    * 中心文字
    * */
    private String mCenterText;
    /*
    * 中心文字颜色
    * */
    private int mCenterTextColor;
    /*
    * 中心文字大小
    * */
    private float mCenterTextSize;
    /*
    * 圆形背景
    * */
    private int mCircleBackground;
    /*
    * 圆环进度颜色
    * */
    private int mRingProgressColor;
    /*
    * 圆环进度背景颜色
    * */
    private int mRingProgressBackground;
    /*
    * 圆环进度宽度
    * */
    private float mRingProgressWidth;
     public CustomCircleProgress(Context context, AttributeSet attributeSet, int defStyleAttr){
        super(context,attributeSet,defStyleAttr);
        //        获得定义的自定义样式属性
        TypedArray a = context.obtainStyledAttributes(attributeSet,R.styleable.CustomCircleProgress,defStyleAttr,0);

        mCenterText = a.getString(R.styleable.CustomCircleProgress_centerText);
        mCenterTextColor =     a.getColor(R.styleable.CustomCircleProgress_centerTextColor,Color.WHITE);
        mCenterTextSize = a.getDimensionPixelSize(R.styleable.CustomCircleProgress_centerTextSize,(int) TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_SP, 14, getResources().getDisplayMetrics()));
        mCircleBackground = a.getColor(R.styleable.CustomCircleProgress_circleBackground,Color.BLACK);

        mRingProgressColor = a.getColor(R.styleable.CustomCircleProgress_ringProgressColor,Color.GRAY);
        mRingProgressBackground = a.getColor(R.styleable.CustomCircleProgress_ringProgressBackground,Color.TRANSPARENT);
        mRingProgressWidth = a.getDimensionPixelSize(R.styleable.CustomCircleProgress_ringProgressWidth,(int) TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_DIP, 4, getResources().getDisplayMetrics()));

        a.recycle();
        initialize();

    }
2. 重写OnMeasure

由于继承自View,所以为了使wrap_content属性有效,必须重写OnMeasure。

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);

    //如果宽或高的SpecMode为AT_MOST,就设置为默认宽高
        if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST){
            setMeasuredDimension(mDefaultRadius,mDefaultRadius);
        }else if (widthSpecMode == MeasureSpec.AT_MOST){
            setMeasuredDimension(mDefaultRadius,heightSpecSize);
        }else if (heightSpecMode == MeasureSpec.AT_MOST){
            setMeasuredDimension(widthSpecSize,mDefaultRadius);
        }
        int width = getMeasuredWidth();
        int height = getMeasuredHeight();
        //宽和高取较小的那一个,并设置自定义圆形View尺寸
        int mViewSize = ((height < width ? height : width));
        setMeasuredDimension(mViewSize,mViewSize);
    }
3. 重写OnDraw
@Override
    protected void onDraw(Canvas canvas) {

        //设置绘制区的矩形,为圆环外界留出圆环宽度一半的大小
        mRectFBound.set(mRingProgressWidth/2,mRingProgressWidth/2,getWidth() - mRingProgressWidth/2,getHeight() - mRingProgressWidth/2);
        mPaint.setAntiAlias(true);

//        画实心背景 半径为矩形区域减去圆环宽度的一半
        mPaint.setColor(mCircleBackground);
        mPaint.setStyle(Paint.Style.FILL);
        canvas.drawCircle(mRectFBound.centerX(),mRectFBound.centerY(),mRectFBound.width()/2 - mRingProgressWidth/2,mPaint);


        //画中心文字 中心文字起点为左下,文字绘制在圆形中间位置
        mPaint.setColor(mCenterTextColor);
        mPaint.setStyle(Paint.Style.FILL);
        if (mShowPercent){
            mCenterText = String.valueOf(mPercent) + "%";
        }
        mPaint.setTextSize(mCenterTextSize);
        mPaint.getTextBounds(mCenterText,0,mCenterText.length(),mTextRect);
        canvas.drawText(mCenterText,mRectFBound.centerX() - mTextRect.width()/2,mRectFBound.centerY() + mTextRect.height()/2,mPaint);

        //        画圆环   因为圆环有宽度,所以是环的中间位置与矩形四边相切
        mPaint.setStrokeWidth(mRingProgressWidth);
        mPaint.setStyle(Paint.Style.STROKE);

        mPaint.setColor(mRingProgressBackground);
        canvas.drawArc(mRectFBound,0,360,false,mPaint);

        mPaint.setColor(mRingProgressColor);
        canvas.drawArc(mRectFBound,0,mArc,false,mPaint);

        updateProgress();
    }
4. 更新进度

先定义一些更新进度需要的变量

  /*
   * 圆环起始弧度,默认360
   * */
    private int mArc = 360;

    /*
    * 当前进度
    * */
    private float mCurrProgress;


    /*
    * 进度开始时刻
    * */
    private long mStartTime ;

    /*
    * 进度是否已开始
    * */
    private boolean mStart = false;

    /**
     * 默认进度完成总时间。
     */
    private int mDuration = 3000;

    /*
    * 进度完成时间倒数
    * */
    private float mDurationReciprocal;

    /*
    * 进度是否渐增,默认是递减
    * */
    private boolean mIncreasing = false;

    /*
    * 是否显示百分比进度,默认不显示
    * */
    private boolean mShowPercent = false;
    /*
    * 百分比进度,默认100%
    * */
    private int mPercent = 100;

这里有三个变量可以在使用view的自己设定mDuration(设置进度总时间)、mIncreasing(设置进度递增或递减)、mShowPercent(是否显示进度百分比)、

 /*
    * 进度开始
    * */
    public void startRun(){
        mStartTime =  AnimationUtils.currentAnimationTimeMillis();
        mStart = true;
        mDurationReciprocal = 1.0f / (float) mDuration;
        updateProgress();
    }

     /*
    * 更新进度
    * */
    private void updateProgress(){
        if (computeProgressOffset()){

            if (mIncreasing){
                mArc = Math.round(360 * mCurrProgress);
                mPercent = Math.round(100 * mCurrProgress);
            }else {
                mArc = Math.round(360 * (1-mCurrProgress));
                mPercent = Math.round(100 * (1-mCurrProgress));
            }

            postInvalidate();
        }
    }

    /*
    * 根据时间流逝计算进度比例
    * */
    private boolean computeProgressOffset(){
        if (!mStart){
            return false;
        }
        int timePass = (int) (AnimationUtils.currentAnimationTimeMillis() - mStartTime);
        if (timePass < mDuration){
            mCurrProgress = timePass * mDurationReciprocal;
        }else {
            mStart = false;
            mCurrProgress = 1;
        }
        return true;
    }

由于是在一定的时间内更新完成进度(进度0~1),所以可以在更新进度的时候先通过 当前系统时间 - 开始更新进度时间 计算已经过去的时间timePass,这样就根据时间的流逝计算占总进度的比例,每次更新进度时调用computeProgressOffset(),判断是否完成进度,如果未完成(即时间未到),设置已完成进度,返回true,调用postInvalidate()更新View;更新完后又会调用updateProgress(),如此循环;一旦timePass超过或等于设定时间,就完成了,设置进度为1,此时仍需要更新一次,所以computeProgressOffset()仍然返回true,但更新标识mStart应为false了,以此终止更新。
这样简单的圆形进度基本功能就实现了。

内容概要:本文档详细介绍了基于Python的在线二手电子产品回收系统的设计与实现。项目旨在通过构建一个可靠、安全、透明的平台,提高废旧电子产品的回收率,推动资源的合理再利用,提供安全可靠的交易平台,加强环保意识,促进二手市场的发展,并实现数据驱动的智能化服务。项目面临的主要挑战包括废旧电子产品的检测与评估、信息不对称与交易风险、市场需求的预测与定价、用户体验优化及平台的安全性与数据保护。解决方案涵盖智能化评估与回收定价、高效的二手产品处理流程、完善的售后保障体系、创新的市场需求分析、全程透明化与安全性保障以及定制化用户体验。系统采用微服务架构,包括用户管理、商品评估、交易管理、数据分析、支付与结算等模块。项目还涉及前端界面设计、API接口开发、数据库设计与实现、模型训练与优化、部署与应用等方面。 适合人群:具备一定编程基础,特别是对Python和Web开发有一定了解的研发人员,以及对二手电子产品回收和环保事业感兴趣的从业者。 使用场景及目标:①帮助用户方便地将闲置电子产品回收、交易或再利用,提高废旧电子产品的回收率;②通过智能化的数据分析为用户提供价格评估、市场需求分析等服务,提高回收效率;③提供安全可靠的交易平台,确保交易的公平性和安全性;④推动二手市场的健康发展,为消费者提供经济实惠的产品选择;⑤增强公众的环保意识,推动社会向绿色、低碳方向发展。 其他说明:本文档不仅提供了系统的功能模块设计、数据库表结构、API接口规范,还展示了具体代码实现和GUI界面设计,为开发者提供了全面的技术参考。此外,项目强调了数据安全和隐私保护的重要性,确保平台在运行过程中能够有效保护用户信息。项目未来改进方向包括增强模型的精准度、拓展国际市场、提供更多支付和融资选项、跨平台数据集成与分析、更加智能的回收流程以及强化社交化与社区功能。
内容概要:本文档详细介绍了基于C语言和单片机设计的固态继电器驱动空调温控系统,涵盖了从硬件电路设计、程序设计、GUI设计到代码详解的完整流程。项目旨在实现高效精准的温度控制、提升系统可靠性和寿命、灵活的参数设置和人机交互、降低能耗、模块化设计便于扩展与维护,以及促进智能家居与工业自动化发展。项目通过高精度温度采集与滤波算法、固态继电器驱动与保护电路设计、滞环控制算法、多层次软件模块化设计等创新点,确保系统的高效节能、智能化和高可靠性。; 适合人群:具备一定单片机和C语言编程基础的研发人员,尤其是从事嵌入式系统设计、智能家居和工业自动化领域的工程师。; 使用场景及目标:①实现高效精准的温度控制,确保室内温度维持在理想范围;②提升系统可靠性和寿命,减少故障率和维护成本;③支持灵活的参数设置和用户友好的人机交互界面,提升用户体验;④降低能耗,实现节能控制,推动绿色建筑和节能环保产业的发展;⑤通过模块化设计,便于后续功能升级和系统扩展,如远程监控、数据分析等智能化功能。; 其他说明:项目设计充分考虑了实际应用中的挑战,如温度采集的精度与稳定性、电气兼容性、系统响应速度与控制稳定性、软件设计的资源优化与抗干扰等,提出了针对性的解决方案。系统不仅适用于家庭智能空调,还能广泛应用于工业、商业建筑、医疗环境及农业温室等多个领域。未来改进方向包括智能温度预测与自适应控制、多传感器融合技术应用、远程监控与云平台集成、低功耗与绿色节能优化等。通过该系统,不仅能够精确控制室内温度,保障舒适环境,还能有效节能,延长设备使用寿命,具有重要的实际应用价值和推广意义。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值