Android自定义View实现渐变色进度条

在网上看到一个进度条效果图,非常美观,如下:

进行效果分解:

1.渐变色,看起来颜色变化并不复杂,使用LinearGradient应该可以实现。
2.圆头,无非是画两个圆,外圆使用渐变色的颜色,内圆固定为白色。
3.灰底,还没有走到的进度部分为灰色。
4.进度值,使用文本来显示;
5.弧形的头部,考虑使用直线进行连接,或者使用曲线,例如贝塞尔曲线;

我首先初步实现了进度条的模样,发现样子有了,却不太美观。
反思了一下,我只是个写代码的,对于哪种比例比较美观,是没有清晰的认识的,所以,还是参考原图吧。

然后就进行了精细的测量:

将图像放大4倍,进行测量,然后获取到各部分的比例关系,具体过程就不细说了,说一下测量结果(按比例的):

视图总长300,其中前面留空5,进度长258,然后再留空5,显示文本占26,后面留空6;

高度分为4个:
外圆:10
字高:9
内圆:6
线粗:5
考虑上下各留空10,则视图的高度为30。

考虑到视图整体的效果,可以由用户来设置长度值与高度值,按比例取最小值来进行绘图。
首先计算出一个单位的实际像素数,各部分按比例来显示即可。

还有一个弧形的头部,是怎么实现的呢?
在放大之后,能看出来图形比较简单,看不出有弧度,那么,使用一小段直线连接就可以了。
估算这小段直线:线粗为2,呈30度角,长为8-10即可,连接直线与弧顶,起点在弧顶之左下方。
注意:在进度的起点时,不能画出。避免出现一个很突兀的小尾巴。在2%进度之后,才开始画。

在文字的绘制过程中,遇到一个小问题,就是文字不居中,略微偏下,上网查了下,原因是这样的:我们绘制文本时,使用的这个函数:canvas.drawText(“30%”, x, y, paint);
其中的参数 y 是指字符串baseline的的位置,不是文本的中心。通过计算可以调整为居中,如下:

//计算坐标使文字居中
FontMetrics fontMetrics = mPaint.getFontMetrics();
float fontHeight = fontMetrics.bottom - fontMetrics.top;
float baseY = height/2 + fontHeight/2 - fontMetrics.bottom;

按比例来绘制之后,就确实是原来那个修长优雅的感觉了。
实际运行后,发现字体偏小,不太适合竖屏观看,调大了些。

另外对于参数,做了如下几个自定义属性:
前景色:开始颜色,结束颜色;
进度条未走到时的默认颜色,
字体颜色。

属性xml如下:

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

 <attr name="startColor" format="color" />
 <attr name="endColor" format="color" />
 <attr name="backgroundColor" format="color" />
 <attr name="textColor" format="color" />

 <declare-styleable name="GoodProgressView">
 <attr name="startColor" />
 <attr name="endColor" />
 <attr name="backgroundColor" />
 <attr name="textColor" />
 </declare-styleable>

</resources>

自定义View文件:

package com.customview.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Shader;
import android.graphics.Paint.Cap;
import android.graphics.Paint.FontMetrics;
import android.graphics.Paint.Style;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import com.customview.R;

public class GoodProgressView extends View
{
 private int[] mColors = { Color.RED, Color.MAGENTA};//进度条颜色(渐变色的2个点)
 private int backgroundColor = Color.GRAY;//进度条默认颜色
 private int textColor = Color.GRAY;//文本颜色

 private Paint mPaint;//画笔
 private int progressValue=0;//进度值
// private RectF rect;//绘制范围

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

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

 // 获得我自定义的样式属性
 public GoodProgressView(Context context, AttributeSet attrs, int defStyle)
 {
 super(context, attrs, defStyle);

 // 获得我们所定义的自定义样式属性
 TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.GoodProgressView, defStyle, 0);
 int n = a.getIndexCount();
 for (int i = 0; i < n; i++)
 {
  int attr = a.getIndex(i);
  switch (attr)
  {
  case R.styleable.GoodProgressView_startColor:
  // 渐变色之起始颜色,默认设置为红色
  mColors[0] = a.getColor(attr, Color.RED);
  break;
  case R.styleable.GoodProgressView_endColor:
  // 渐变色之结束颜色,默认设置为品红
  mColors[1] = a.getColor(attr, Color.MAGENTA);
  break;
  case R.styleable.GoodProgressView_backgroundColor:
  // 进度条默认颜色,默认设置为灰色
  backgroundColor = a.getColor(attr, Color.GRAY);
  break;
  case R.styleable.GoodProgressView_textColor:
  // 文字颜色,默认设置为灰色
  textColor = a.getColor(attr, Color.GRAY);
  break;
  }
 }
 a.recycle();

 mPaint = new Paint();
 progressValue=0;
 }

 public void setProgressValue(int progressValue){

 if(progressValue>100){
  progressValue = 100;
 }
 this.progressValue = progressValue;
 Log.i("customView","log: progressValue="+progressValue);
 }

 public void setColors(int[] colors){
 mColors = colors;
 }

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
 {

 int width = 0;
 int height = 0;
 /**
  * 设置宽度
  */
 int specMode = MeasureSpec.getMode(widthMeasureSpec);
 int specSize = MeasureSpec.getSize(widthMeasureSpec);
 switch (specMode)
 {
 case MeasureSpec.EXACTLY:// 明确指定了
  width = specSize;
  break;
 case MeasureSpec.AT_MOST:// 一般为WARP_CONTENT
  width = getPaddingLeft() + getPaddingRight() ;
  break;
 }

 /**
  * 设置高度
  */
 specMode = MeasureSpec.getMode(heightMeasureSpec);
 specSize = MeasureSpec.getSize(heightMeasureSpec);
 switch (specMode)
 {
 case MeasureSpec.EXACTLY:// 明确指定了
  height = specSize;
  break;
 case MeasureSpec.AT_MOST:// 一般为WARP_CONTENT
  height = width/10;
  break;
 }

 Log.i("customView","log: w="+width+" h="+height);
 setMeasuredDimension(width, height);

 }

 @Override
 protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);

 int mWidth = getMeasuredWidth();
 int mHeight = getMeasuredHeight();

 //按比例计算进度条各部分的值
 float unit = Math.min(((float)mWidth)/300, ((float)mHeight)/30);
 float lineWidth = 5*unit;//线粗
 float innerCircleDiameter = 6*unit;//内圆直径
 float outerCircleDiameter = 10*unit;//外圆直径
 float wordHeight = 12*unit;//字高//9*unit
// float wordWidth = 26*unit;//字长
 float offsetLength = 5*unit;//留空
// float width = 300*unit;//绘画区域的长度
 float height = 30*unit;//绘画区域的高度
 float progressWidth = 258*unit;//绘画区域的长度

 mPaint.setAntiAlias(true);
 mPaint.setStrokeWidth((float) lineWidth );
 mPaint.setStyle(Style.STROKE);
 mPaint.setStrokeCap(Cap.ROUND);

 mPaint.setColor(Color.TRANSPARENT);

 float offsetHeight=height/2;
 float offsetWidth=offsetLength;

 float section = ((float)progressValue) / 100;
 if(section>1)
  section=1;

 int count = mColors.length;
 int[] colors = new int[count];
 System.arraycopy(mColors, 0, colors, 0, count); 

 //底部灰色背景,指示进度条总长度
 mPaint.setShader(null);
 mPaint.setColor(backgroundColor);
 canvas.drawLine(offsetWidth+section * progressWidth, offsetHeight, offsetWidth+progressWidth, offsetHeight, mPaint);

 //设置渐变色区域
 LinearGradient shader = new LinearGradient(0, 0, offsetWidth*2+progressWidth , 0, colors, null,
  Shader.TileMode.CLAMP);
 mPaint.setShader(shader);

 //画出渐变色进度条
 canvas.drawLine(offsetWidth, offsetHeight, offsetWidth+section*progressWidth, offsetHeight, mPaint);

 //渐变色外圆
 mPaint.setStrokeWidth(1);
 mPaint.setStyle(Paint.Style.FILL);
 canvas.drawCircle(offsetWidth+section * progressWidth, offsetHeight, outerCircleDiameter/2, mPaint);

 //绘制两条斜线,使外圆到进度条的连接更自然
 if(section*100>1.8){

  mPaint.setStrokeWidth(2*unit);
  canvas.drawLine(offsetWidth+section * progressWidth-6*unit, offsetHeight-(float)1.5*unit,
   offsetWidth+section * progressWidth-1*unit,offsetHeight-(float)3.8*unit, mPaint);

  canvas.drawLine(offsetWidth+section * progressWidth-6*unit, offsetHeight+(float)1.5*unit,
   offsetWidth+section * progressWidth-1*unit,offsetHeight+(float)3.8*unit, mPaint);
 }

 //白色内圆
 mPaint.setShader(null);
 mPaint.setColor(Color.WHITE);
 canvas.drawCircle(offsetWidth+section * progressWidth, offsetHeight, innerCircleDiameter/2, mPaint);//白色内圆

 //绘制文字--百分比
 mPaint.setStrokeWidth(2*unit);
 mPaint.setColor(textColor);
 mPaint.setTextSize(wordHeight);
 //计算坐标使文字居中
 FontMetrics fontMetrics = mPaint.getFontMetrics();
 float fontHeight = fontMetrics.bottom - fontMetrics.top;
 float baseY = height/2 + fontHeight/2 - fontMetrics.bottom;
 canvas.drawText(""+progressValue+"%", progressWidth+2*offsetWidth, baseY, mPaint);//略微偏下,baseline

 }

}

主xml:

放了两个进度条,一个使用默认值,一个设置了进度条默认颜色与字体颜色:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 xmlns:custom="http://schemas.android.com/apk/res/com.customview"
 android:layout_width="match_parent"
 android:layout_height="match_parent" >

 <com.customview.view.GoodProgressView
 android:id="@+id/good_progress_view1"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:padding="10dp"
 /> 

 <com.customview.view.GoodProgressView
 android:id="@+id/good_progress_view2"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:layout_centerInParent="true"
 custom:backgroundColor="#ffcccccc"
 custom:textColor="#ff000000"
 android:padding="10dp"
 /> 

</RelativeLayout>

Activity文件:

一个使用默认渐变色效果,一个的渐变色使用随机颜色,这样每次运行效果不同,比较有趣一些,另外我们也可以从随机效果中找到比较好的颜色组合。进度的变化,是使用了一个定时器来推进。

package com.customview;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.WindowManager;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import com.customview.view.GoodProgressView;
import android.app.Activity;
import android.graphics.Color;

public class MainActivity extends Activity
{

 GoodProgressView good_progress_view1;
 GoodProgressView good_progress_view2;

 int progressValue=0; 

 @Override
 protected void onCreate(Bundle savedInstanceState)
 {
 super.onCreate(savedInstanceState);
 this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);//去掉信息栏

 setContentView(R.layout.activity_main);
 good_progress_view1 = (GoodProgressView)findViewById(R.id.good_progress_view1);
 good_progress_view2 = (GoodProgressView)findViewById(R.id.good_progress_view2);

 //第一个进度条使用默认进度颜色,第二个指定颜色(随机生成)
 good_progress_view2.setColors(randomColors());

 timer.schedule(task, 1000, 1000); // 1s后执行task,经过1s再次执行
 }

 Handler handler = new Handler() {
 public void handleMessage(Message msg) {
  if (msg.what == 1) {
  Log.i("log","handler : progressValue="+progressValue);

  //通知view,进度值有变化
  good_progress_view1.setProgressValue(progressValue*2);
  good_progress_view1.postInvalidate();

  good_progress_view2.setProgressValue(progressValue);
  good_progress_view2.postInvalidate();

  progressValue+=1;
  if(progressValue>100){
   timer.cancel();
  }
  }
  super.handleMessage(msg);
 };
 }; 

 private int[] randomColors() {
 int[] colors=new int[2];

 Random random = new Random();
 int r,g,b;
 for(int i=0;i<2;i++){
  r=random.nextInt(256);
  g=random.nextInt(256);
  b=random.nextInt(256);
  colors[i]=Color.argb(255, r, g, b);
  Log.i("customView","log: colors["+i+"]="+Integer.toHexString(colors[i]));
 }

 return colors;
 }

 Timer timer = new Timer();
 TimerTask task = new TimerTask() { 

 @Override
 public void run() {
  // 需要做的事:发送消息
  Message message = new Message();
  message.what = 1;
  handler.sendMessage(message);
 }
 }; 

}

最终效果如下:

竖屏时:

横屏时:

源码下载:Android渐变色进度条

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android中Toolbar随着ScrollView滑动透明度渐变效果实现

    Android中Toolbar随着ScrollView滑动透明度渐变效果实现 一.思路:监听ScrollView的滑动事件 不断的修改Toolbar的透明度 二.注意 1.ScrollView 6.0以前没有scrollView.setOnScrollChangeListener(l)方法  所以要自定义ScrollView 在onScrollChanged()中监听 2.ScrollView 6.0(23)以前没有scrollView.setOnScrollChangeListener()方法

  • Android开发之ListView的head消失页面导航栏的渐变出现和隐藏

    1.Fragment页面xml布局: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"

  • android自定义进度条渐变色View的实例代码

    最近在公司,项目不是很忙了,偶尔看见一个兄台在CSDN求助,帮忙要一个自定义的渐变色进度条,我当时看了一下进度条,感觉挺漂亮的,就尝试的去自定义view实现了一个,废话不说,先上图吧! 这个自定义的view,完全脱离了android自带的ProgressView,并且没使用一张图片,这样就能更好的降低程序代码上的耦合性! 下面我贴出代码  ,大概讲解一下实现思路吧! 复制代码 代码如下: package com.spring.progressview; import android.conten

  • Android ScrollView滑动实现仿QQ空间标题栏渐变

    今天来研究的是ScrollView-滚动视图,滚动视图又分横向滚动视图(HorizontalScrollView)和纵向滚动视图(ScrollView),今天主要研究纵向的.相信大家在开发中经常用到,ScrollView的功能已经很强大了,但是仍然满足不了我们脑洞大开的UI设计师们,所以我们要自定义-本篇文章主要讲监听ScrollView的滑动实现仿QQ空间标题栏渐变,先看一下效果图: 好了我们切入主题. 有可能你不知道的那些ScrollView属性  •android:scrollbars 设

  • Android自定义渐变式炫酷ListView下拉刷新动画

    本文实例为大家分享了自定义渐变式炫酷动画的ListView下拉刷新,供大家参考,具体内容如下 主要要点 listview刷新过程中主要有三个步骤当前:状态为下拉刷新,当前状态为下拉刷新,当前状态为放开刷新,当前状态为正在刷新:主要思路为三个步骤分别对应三个自定义的view:即ibuRefreshFirstStepView,ibuRefreshSecondStepView,ibuRefreshThirdStepView. 效果图 ibuRefreshFirstStepView代码,例如: priv

  • Android ListView滑动改变标题栏背景渐变效果

    先上ListView滑动改变标题栏背景渐变效果图,透明转变成不透明效果: 图1: 图2: 图3: 图4: 我用的是小米Note手机,状态栏高度是55px,后面会提到,这里先做个说明: 下面的内容包含了所有代码和一些测试数据: 代码: 代码很简单,也做了注释,这里就不废话了. 先来布局文件: activity的布局 activity_main_10 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/androi

  • Android Textview实现颜色渐变滚动效果

    本文实例为大家分享了Android颜色渐变滚动展示的具体代码,供大家参考,具体内容如下 public class FlashTextView extends android.support.v7.widget.AppCompatTextView { private Paint mPaint; private int mViewWidth; private LinearGradient mLinearGradient; private Matrix mGradientMatrix; private

  • Android UI设计系列之自定义ListView仿QQ空间阻尼下拉刷新和渐变菜单栏效果(8)

    好久没有写有关UI的博客了,刚刚翻了一下之前的博客,最近一篇有关UI的博客:Android UI设计系列之自定义Dialog实现各种风格的对话框效果(7) ,实现各种风格效果的对话框,在那篇博客写完后由于公司封闭开发封网以及其它原因致使博客中断至今,中断这么久很是惭愧,后续我会尽量把该写的都补充出来.近来项目有个需求,要做个和QQ空间类似的菜单栏透明度渐变和下拉刷新带有阻尼回弹的效果.于是花点时间动手试了试,基本上达到了QQ空间的效果,截图如下: 通过观察QQ空间的运行效果,发现当往上滚动时菜单

  • Android中recyclerView底部添加透明渐变效果

    前言 最近实现一个recyclerView透明渐变的效果,遇到了一些坑,尝试了一些方法,这里记录一下. 效果图 图片在上面显示2列,文字在下面显示1列:底部要有个透明渐变的效果,直到完全看不到. gridLayoutManager动态设置列数 大概是分两类,一类以图片为item 一行2个,一类以文字为item 一行一个. 这个第一反应是用viewType去区分图片类型,但是由于起初不知道gridLayout可以动态列数.就在上面两列,下面一列上为难起来了. 如果统一用一列吧,那就把两个image

  • Android自定义view渐变圆形动画

    本文实例为大家分享了Android自定义view渐变圆形动画的具体代码,供大家参考,具体内容如下 直接上效果图 自定义属性 attrs.xml文件 <resources> <declare-styleable name="ProgressRing"> <!--进度起始色--> <attr name="pr_progress_start_color" format="color" /> <!--

随机推荐