首页 > 其他分享 >Android自定义View实现渐变色进度条

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

时间:2023-06-19 11:35:46浏览次数:39  
标签:自定义 进度条 int 渐变色 float mPaint import android unit


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

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自定义View实现渐变色进度条_android_02

横屏时:

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

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

标签:自定义,进度条,int,渐变色,float,mPaint,import,android,unit
From: https://blog.51cto.com/u_16163453/6512013

相关文章

  • Android代码检查规则Lint的自定义与应用
    前言:在日常的代码开发中,此处相信每个开发人员对代码质量都是高要求,有自己的一套代码规范,但是我们不是单独作战,往往大家都是团队作战,人是最大的变量,各人各异,如何保证团队的代码质量和代码规范呢?靠开发者自觉吗?也许有的团队有严格的CR机制,在MR阶段会进行CR,CR不通过的MR是不允许合入的......
  • react 进度条
    最终结果图还是直接上代码哈,我这里直接用的react的hook写的,最近这一年没怎么有时间更新博客,这两年我换技术栈了,换成react了,中间写了很多组件,后面我有空了全部都更新出来吧,不过都是react的哦,当然有时候vue也在使用哈,一般我都是直接上代码,不存在过多的讲解,因为不是特别难的代码,一......
  • 前端Vue图片上传组件支持单个文件多个文件上传 自定义上传数量 预览删除图片 图片压缩
    前端Vue图片上传组件支持单个文件多个文件上传自定义上传数量预览删除图片图片压缩,下载完整代码请访问uni-app插件市场址:https://ext.dcloud.net.cn/plugin?id=13099效果图如下:1.0.0(2023-06-18)组件初始化使用方法<!--count:最大上传数量 imageList:图片上传选......
  • Android 12 自定义底部导航栏
    1.修改配置文件 frameworks\base\packages\SystemUI\res\values\config.xml<!--Navbarbuttondefaultordering/layout--><stringname="config_navBarLayout"translatable="false">left[.5W];leftrotate,volume_sub,back,home,r......
  • 自定义异常和统一校验参数
    自定义异常@GetterpublicclassBusinessExceptionextendsRuntimeException{/***http状态码*/privateintcode;privateObjectobject;publicBusinessException(Stringmessage,intcode,Objectobject){super(message);......
  • Hexo + Butterfly 自定义页脚
    原文链接:Hexo+Butterfly自定义页脚推荐阅读基于Hexo从零开始搭建个人博客(一):环境准备基于Hexo从零开始搭建个人博客(二):项目初识基于Hexo从零开始搭建个人博客(三):主题安装基于Hexo从零开始搭建个人博客(四):基础配置基于Hexo从零开始搭建个人博客(五):详细......
  • [MAUI]弧形进度条与弧形滑块的交互实现
    @目录弧形基类定义绘制弧弧形进度条(ProgressBar)添加动画宽度补偿文本弧形滑块(Slider)创建控制柄拖动事件处理项目地址进度条(ProgressBar)用于展示任务的进度,告知用户当前状态和预期;滑块(Slider)通过拖动滑块在一个固定区间内进行选择数值范围。进度条和滑块都是进度值在UI界面......
  • backtrader 自定义分析器,解决多股回测难分析困难问题
    backtrader自定义分析器,解决多股回测分析困难问题解决了啥:解决回测后获取关键指标解决多股回测,获取订单分析解决多股回测买卖点可视化标识效果图通过自定义分析器KeyIndicatorAnalyzer,TradeListAnalyzer,获取回测结果数据,通过回测数据可以轻松可视化回测结果。可视化部......
  • centos添加自定义Systemd服务
    #########################https://zhuanlan.zhihu.com/p/415469149          systemctlenable**nable命令相当于在/etc/systemd/system/目录里添加了一个符号链接,指向/usr/lib/systemd/system/里面的**.service开机时,Systemd会执行/etc/systemd/system......
  • springboot中自定义注解在service方法中,aop失效
    问题描述写了个自定义注解,但是该注解只会出现在serviece层中的方法中。启动发现aop未拦截到问题原因:调用service中的xx()方法时,Spring的动态代理帮我们动态生成了一个代理的对象,暂且叫他$XxxxService。所以调用xx()方法实际上是代理对象$XxxxService调用的。但是在xx()方法内调用同......