我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android应用中炫酷的横向和环形进度条的实例分享

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android应用中炫酷的横向和环形进度条的实例分享

一、概述
最近需要用进度条,秉着不重复造轮子的原则,上github上搜索了一番,看了几个觉得比较好看的ProgressBar,比如:daimajia的等。简单看了下代码,基本都是继承自View,彻彻底底的自定义了一个进度条。盯着那绚丽滚动条,忽然觉得,为什么要通过View去写一个滚动条,系统已经提供了ProgressBar以及属于它的特性,我们没必要重新去构建一个,但是系统的又比较丑,不同版本变现还不一定一样。那么得出我们的目标:改变系统ProgressBar的样子。
对没错,我们没有必要去从0打造一个ProgressBar,人家虽然长的不好看,但是特性以及稳定性还是刚刚的,我们只需要为其整下容就ok了。
接下来,我们贴下效果图:
1、横向的进度条

2、圆形的进度条

2016419170129190.jpg (414×620)

没错,这就是我们的进度条效果,横向的模仿了daimajia的进度条样子。不过我们继承子ProgressBar,简单的为其整个容,代码清晰易懂 。为什么说,易懂呢?
横向那个进度条,大家会drawLine()和drawText()吧,那么通过getWidth()拿到控件的宽度,再通过getProgress()拿到进度,按比例控制绘制线的长短,字的位置还不是分分钟的事。

二、实现
 横向的滚动条绘制肯定需要一些属性,比如已/未到达进度的颜色、宽度,文本的颜色、大小等。
 本来呢,我是想通过系统ProgressBar的progressDrawable,从里面提取一些属性完成绘制需要的参数的。但是,最终呢,反而让代码变得复杂。所以最终还是改用自定义属性。 说道自定义属性,大家应该已经不陌生了。
1、HorizontalProgressBarWithNumber
(1)自定义属性
values/attr_progress_bar.xml:


<?xml version="1.0" encoding="utf-8"?> 
<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> 

(2)构造中获取


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; 
   
  protected Paint mPaint = new Paint(); 
   
  protected int mTextColor = DEFAULT_TEXT_COLOR; 
   
  protected int mTextSize = sp2px(DEFAULT_TEXT_SIZE); 
   
  protected int mTextOffset = dp2px(DEFAULT_SIZE_TEXT_OFFSET); 
   
  protected int mReachedProgressBarHeight = dp2px(DEFAULT_HEIGHT_REACHED_PROGRESS_BAR); 
   
  protected int mReachedBarColor = DEFAULT_TEXT_COLOR; 
   
  protected int mUnReachedBarColor = DEFAULT_COLOR_UNREACHED_COLOR; 
   
  protected int mUnReachedProgressBarHeight = dp2px(DEFAULT_HEIGHT_UNREACHED_PROGRESS_BAR); 
   
  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); 
    setHorizontalScrollBarEnabled(true); 
    obtainStyledAttributes(attrs); 
    mPaint.setTextSize(mTextSize); 
    mPaint.setColor(mTextColor); 
  } 
   
  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(); 
  } 

嗯,看起来代码挺长,其实都是在获取自定义属性,没什么技术含量。

(3)onMeasure
刚才不是出onDraw里面写写就行了么,为什么要改onMeasure呢,主要是因为我们所有的属性比如进度条宽度让用户自定义了,所以我们的测量也得稍微变下。


@Override 
protected synchronized void onMeasure(int widthMeasureSpec, 
    int heightMeasureSpec) 
{ 
  int heightMode = MeasureSpec.getMode(heightMeasureSpec); 
  if (heightMode != MeasureSpec.EXACTLY) 
  { 
    float textHeight = (mPaint.descent() + mPaint.ascent()); 
    int exceptHeight = (int) (getPaddingTop() + getPaddingBottom() + Math 
        .max(Math.max(mReachedProgressBarHeight, 
            mUnReachedProgressBarHeight), Math.abs(textHeight))); 
    heightMeasureSpec = MeasureSpec.makeMeasureSpec(exceptHeight, 
        MeasureSpec.EXACTLY); 
  } 
  super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
} 

宽度我们不变,所以的自定义属性不涉及宽度,高度呢,只考虑不是EXACTLY的情况(用户明确指定了,我们就不管了),根据padding和进度条宽度算出自己想要的,如果非EXACTLY下,我们进行exceptHeight封装,传入给控件进行测量高度。
测量完,就到我们的onDraw了~~~
(4)onDraw


@Override 
  protected synchronized void onDraw(Canvas canvas) 
  { 
    canvas.save(); 
    //画笔平移到指定paddingLeft, getHeight() / 2位置,注意以后坐标都为以此为0,0 
    canvas.translate(getPaddingLeft(), getHeight() / 2); 
    boolean noNeedBg = false; 
    //当前进度和总值的比例 
    float radio = getProgress() * 1.0f / getMax(); 
    //已到达的宽度 
    float progressPosX = (int) (mRealWidth * radio); 
    //绘制的文本 
    String text = getProgress() + "%"; 
    //拿到字体的宽度和高度 
    float textWidth = mPaint.measureText(text); 
    float textHeight = (mPaint.descent() + mPaint.ascent()) / 2; 
    //如果到达最后,则未到达的进度条不需要绘制 
    if (progressPosX + textWidth > mRealWidth) 
    { 
      progressPosX = mRealWidth - textWidth; 
      noNeedBg = true; 
    } 
    // 绘制已到达的进度 
    float endX = progressPosX - mTextOffset / 2; 
    if (endX > 0) 
    { 
      mPaint.setColor(mReachedBarColor); 
      mPaint.setStrokeWidth(mReachedProgressBarHeight); 
      canvas.drawLine(0, 0, endX, 0, mPaint); 
    } 
    // 绘制文本 
    if (mIfDrawText) 
    { 
      mPaint.setColor(mTextColor); 
      canvas.drawText(text, progressPosX, -textHeight, mPaint); 
    } 
    // 绘制未到达的进度条 
    if (!noNeedBg) 
    { 
      float start = progressPosX + mTextOffset / 2 + textWidth; 
      mPaint.setColor(mUnReachedBarColor); 
      mPaint.setStrokeWidth(mUnReachedProgressBarHeight); 
      canvas.drawLine(start, 0, mRealWidth, 0, mPaint); 
    } 
    canvas.restore(); 
  } 
  @Override 
  protected void onSizeChanged(int w, int h, int oldw, int oldh) 
  { 
    super.onSizeChanged(w, h, oldw, oldh); 
    mRealWidth = w - getPaddingRight() - getPaddingLeft(); 
  } 

其实核心方法就是onDraw了,但是呢,onDraw也很简单,绘制线、绘制文本、绘制线,结束。

还有两个简单的辅助方法:


 
protected int dp2px(int dpVal) 
{ 
  return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 
      dpVal, getResources().getDisplayMetrics()); 
} 
 
protected int sp2px(int spVal) 
{ 
  return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 
      spVal, getResources().getDisplayMetrics()); 
} 

好了,到此我们的横向进度就结束了,是不是很简单~~如果你是自定义View,你还得考虑progress的更新,考虑状态的销毁与恢复等等复杂的东西。
接下来看我们的RoundProgressBarWidthNumber圆形的进度条。

2、RoundProgressBarWidthNumber   
圆形的进度条和横向的进度条基本变量都是一致的,于是我就让RoundProgressBarWidthNumber extends HorizontalProgressBarWithNumber 了。
然后需要改变的就是测量和onDraw了:
完整代码:


package com.zhy.view; 
import android.content.Context; 
import android.content.res.TypedArray; 
import android.graphics.Canvas; 
import android.graphics.Paint.Cap; 
import android.graphics.Paint.Style; 
import android.graphics.RectF; 
import android.util.AttributeSet; 
import com.zhy.library.view.R; 
public class RoundProgressBarWidthNumber extends 
    HorizontalProgressBarWithNumber { 
   
  private int mRadius = dp2px(30); 
  public RoundProgressBarWidthNumber(Context context) { 
    this(context, null); 
  } 
  public RoundProgressBarWidthNumber(Context context, AttributeSet attrs) { 
    super(context, attrs); 
    mReachedProgressBarHeight = (int) (mUnReachedProgressBarHeight * 2.5f); 
    TypedArray ta = context.obtainStyledAttributes(attrs, 
        R.styleable.RoundProgressBarWidthNumber); 
    mRadius = (int) ta.getDimension( 
        R.styleable.RoundProgressBarWidthNumber_radius, mRadius); 
    ta.recycle(); 
    mTextSize = sp2px(14); 
    mPaint.setStyle(Style.STROKE); 
    mPaint.setAntiAlias(true); 
    mPaint.setDither(true); 
    mPaint.setStrokeCap(Cap.ROUND); 
  } 
  @Override 
  protected synchronized void onMeasure(int widthMeasureSpec, 
      int heightMeasureSpec) { 
    int heightMode = MeasureSpec.getMode(heightMeasureSpec); 
    int widthMode = MeasureSpec.getMode(widthMeasureSpec); 
    int paintWidth = Math.max(mReachedProgressBarHeight, 
        mUnReachedProgressBarHeight); 
    if (heightMode != MeasureSpec.EXACTLY) { 
      int exceptHeight = (int) (getPaddingTop() + getPaddingBottom() 
          + mRadius * 2 + paintWidth); 
      heightMeasureSpec = MeasureSpec.makeMeasureSpec(exceptHeight, 
          MeasureSpec.EXACTLY); 
    } 
    if (widthMode != MeasureSpec.EXACTLY) { 
      int exceptWidth = (int) (getPaddingLeft() + getPaddingRight() 
          + mRadius * 2 + paintWidth); 
      widthMeasureSpec = MeasureSpec.makeMeasureSpec(exceptWidth, 
          MeasureSpec.EXACTLY); 
    } 
    super.onMeasure(heightMeasureSpec, heightMeasureSpec); 
  } 
  @Override 
  protected synchronized void onDraw(Canvas canvas) { 
    String text = getProgress() + "%"; 
    // mPaint.getTextBounds(text, 0, text.length(), mTextBound); 
    float textWidth = mPaint.measureText(text); 
    float textHeight = (mPaint.descent() + mPaint.ascent()) / 2; 
    canvas.save(); 
    canvas.translate(getPaddingLeft(), getPaddingTop()); 
    mPaint.setStyle(Style.STROKE); 
    // draw unreaded bar 
    mPaint.setColor(mUnReachedBarColor); 
    mPaint.setStrokeWidth(mUnReachedProgressBarHeight); 
    canvas.drawCircle(mRadius, mRadius, mRadius, mPaint); 
    // draw reached bar 
    mPaint.setColor(mReachedBarColor); 
    mPaint.setStrokeWidth(mReachedProgressBarHeight); 
    float sweepAngle = getProgress() * 1.0f / getMax() * 360; 
    canvas.drawArc(new RectF(0, 0, mRadius * 2, mRadius * 2), 0, 
        sweepAngle, false, mPaint); 
    // draw text 
    mPaint.setStyle(Style.FILL); 
    canvas.drawText(text, mRadius - textWidth / 2, mRadius - textHeight, 
        mPaint); 
    canvas.restore(); 
  } 
} 

首先获取它的专有属性mRadius,然后根据此属性去测量,测量完成绘制;
绘制的过程呢?
先绘制一个细一点的圆,然后绘制一个粗一点的弧度,二者叠在一起就行。文本呢,绘制在中间~~~总体,没什么代码量。
好了,两个进度条就到这了,是不是发现简单很多。总体设计上,存在些问题,如果抽取一个BaseProgressBar用于获取公共的属性;然后不同样子的进度条继承分别实现自己的测量和样子,这样结构可能会清晰些~~~

三、使用
布局文件


<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" 
  xmlns:tools="http://schemas.android.com/tools" 
  xmlns:zhy="http://schemas.android.com/apk/res-auto" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" > 
  <LinearLayout 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:orientation="vertical" 
    android:padding="25dp" > 
    <com.zhy.view.HorizontalProgressBarWithNumber 
      android:id="@+id/id_progressbar01" 
      android:layout_width="match_parent" 
      android:layout_height="wrap_content" 
      android:layout_marginTop="50dip" 
      android:padding="5dp" /> 
    <com.zhy.view.HorizontalProgressBarWithNumber 
      android:layout_width="match_parent" 
      android:layout_height="wrap_content" 
      android:layout_marginTop="50dip" 
      android:padding="5dp" 
      android:progress="50" 
      zhy:progress_text_color="#ffF53B03" 
      zhy:progress_unreached_color="#ffF7C6B7" /> 
    <com.zhy.view.RoundProgressBarWidthNumber 
      android:id="@+id/id_progress02" 
      android:layout_width="match_parent" 
      android:layout_height="wrap_content" 
      android:layout_marginTop="50dip" 
      android:padding="5dp" 
      android:progress="30" /> 
    <com.zhy.view.RoundProgressBarWidthNumber 
      android:layout_width="match_parent" 
      android:layout_height="wrap_content" 
      android:layout_marginTop="50dip" 
      android:padding="5dp" 
      android:progress="50" 
      zhy:progress_reached_bar_height="20dp" 
      zhy:progress_text_color="#ffF53B03" 
      zhy:radius="60dp" /> 
  </LinearLayout> 
</ScrollView> 

MainActivity


package com.zhy.sample.progressbar; 
import android.app.Activity; 
import android.os.Bundle; 
import android.os.Handler; 
import com.zhy.annotation.Log; 
import com.zhy.view.HorizontalProgressBarWithNumber; 
public class MainActivity extends Activity { 
  private HorizontalProgressBarWithNumber mProgressBar; 
  private static final int MSG_PROGRESS_UPDATE = 0x110; 
  private Handler mHandler = new Handler() { 
    @Log 
    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); 
    }; 
  }; 
  @Log 
  @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); 
  } 
} 
您可能感兴趣的文章:Android实现环形进度条Android自定义环形LoadingView效果Android自定义View实现环形进度条的思路与实例Android实现计步进度的环形ProgressAndroid实现环形进度条的实例Android实现环形进度条代码Android中制作进度框和环形进度条的简单实例分享Android环形进度条(安卓默认形式)实例代码android自定义环形对比图效果


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android应用中炫酷的横向和环形进度条的实例分享

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android应用中炫酷的横向和环形进度条的实例分享

一、概述 最近需要用进度条,秉着不重复造轮子的原则,上github上搜索了一番,看了几个觉得比较好看的ProgressBar,比如:daimajia的等。简单看了下代码,基本都是继承自View,彻彻底底的自定义了一个进度条。盯着那绚丽滚动条
2022-06-06

Android中制作进度框和环形进度条的简单实例分享

进度框import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.grap
2022-06-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录