您的位置:首页 > 移动开发 > Android开发

Android自定义圆形进度条

2017-05-25 16:42 309 查看
自定义View是Android进阶的必备技能,只要掌握了自定义view的三个方法,自定义view其实很简单,下面间通过一个示例来说明



首先需要在values文件夹下建一个attrs的文件



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

<attr name="titletext" format="string" />
<attr name="titletextcolor" format="color" />
<attr name="titletextsize" format="dimension" />
<attr name="drawcolor" format="color" />
<attr name="drawwidth" format="dimension" />
<attr name="textsize" format="dimension" />
<attr name="textcolor" format="color" />

<!--定义样式,声明属性-->
<declare-styleable name="CustomTitleView">
<attr name="titletext" />
<attr name="titletextcolor" />
<attr name="titletextsize" />
</declare-styleable>

<declare-styleable name="CustomRoundProgressBar">
<attr name="drawcolor" />
<attr name="drawwidth" />
<attr name="textsize" />
<attr name="textcolor" />
</declare-styleable>

</resources>


其中attr为定义属性,name为属性名称。

<declare-styleable name="CustomRoundProgressBar">
意为是在CustomRoundProgressBar里面声明属性,此为声明属性的固定写法


xml布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.cdemo.customview.MainActivity">

<!--
<com.cdemo.customview.CustomTitleView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="10dp"
app:titletext="666666"
app:titletextcolor="#ff0000"
app:titletextsize="50sp" />
-->

<com.cdemo.customview.CustomRoundProgressBar
android:id="@+id/customprogressbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
app:drawcolor="#4ACC60"
app:drawwidth="39dp"
app:textcolor="#4ACC60"
app:textsize="50sp" />
</RelativeLayout>


根布局文件里写明命名空间,可以写成xmlns:app=”http://schemas.android.com/apk/res-auto”系统自动识别,也可以写成 xmlns:xxx=”http://schemas.android.com/apk/res/com.cdemo.customview”,其中”xxx”名字你自己定义,“com.cdemo.customview”对应你的包名,这里我使用app命名空间自动识别。

下面开始Java代码编写

package com.cdemo.customview;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

/**
* Created by yangdi on 2017/5/24.
*/

public class CustomRoundProgressBar extends View{

// 进度条宽度
private int drawWidth;
// 进度条颜色
private int drawColor;
// 中间数字颜色
private int textColor;
// 中间数字字体大小
private int textSize;

// 字的高度
private float mTxtHeight;

// 进度条最大值
private int maxProgress;
// 进度条当前值
private int currentProgress;

private Paint mPaint;

String TAG = "CustomRoundProgressBar";

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

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

public CustomRoundProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);

// 获取在xml文件中定义的属性和值
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomRoundProgressBar, defStyleAttr, 0);
drawWidth = typedArray.getDimensionPixelSize(R.styleable.CustomRoundProgressBar_drawwidth, TypedValue.COMPLEX_UNIT_DIP);
drawColor = typedArray.getInt(R.styleable.CustomRoundProgressBar_drawcolor, Color.LTGRAY);
textColor = typedArray.getColor(R.styleable.CustomRoundProgressBar_textcolor,Color.BLACK);
textSize = typedArray.getDimensionPixelSize(R.styleable.CustomRoundProgressBar_textsize, TypedValue.COMPLEX_UNIT_SP);

typedArray.recycle();

mPaint = new Paint();
// 抗锯齿
mPaint.setAntiAlias(true);

Paint.FontMetrics fm = mPaint.getFontMetrics();
mTxtHeight = (int) Math.ceil(fm.descent - fm.ascent);
}


通过TypedArray获取刚刚在xml文件中定义的属性和设定的值

onMeasure方法是自定义View中需要重写的方法,其中int widthMeasureSpec, int heightMeasureSpec是父布局传进来的两个参数,由ViewGroup中的layout_width,layout_height和padding以及View自身的layout_margin共同决定,指定View需要多少空间。

这个值由高32位和低16位组成,高32位保存的值叫specMode,可以通过如代码中所示的MeasureSpec.getMode()获取;低16位为specSize,同样可以由MeasureSpec.getSize()获取。

MeasureSpec的specMode,一共三种类型:

EXACTLY:一般是设置了明确的值,如:200dp或者是MATCH_PARENT

AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT

UNSPECIFIED:表示子布局想要多大就多大,很少使用

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

int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);

int width = 0;
int height = 0;

if(widthMode == MeasureSpec.EXACTLY){
width = widthSize;
}else{
// 这里不考虑wrap_content的情况,如有需要可自己补上
}

if(heightMode == MeasureSpec.EXACTLY){
height = heightSize;
}else{
// 这里不考虑wrap_content的情况,如有需要可自己补上
}

// 设置最终宽高
setMeasuredDimension(width, height);
}


以上写的代码全部服务于setMeasuredDimension函数指定最终需要的空间大小。

下面是 onDraw函数,也是自定义View需要重写的方法:

@Override
protected void onDraw(Canvas canvas) {

// 获取视图宽度/2
int viewWidth = getWidth()/2;
// 获取视图高度/2
int viewHeight = getHeight()/2;
// 圆形进度条半径为宽度的3倍
int radius = drawWidth*3;

// 画最外层圆圈
mPaint.setColor(Color.LTGRAY);
// 设置画笔宽度
mPaint.setStrokeWidth(drawWidth);
// 绘制空心效果
mPaint.setStyle(Paint.Style.STROKE);
// 画板底色
canvas.drawColor(Color.WHITE);
canvas.drawCircle(viewWidth, viewHeight, radius, mPaint);

// 画中间进度数字
mPaint.setStrokeWidth(0);/* 需要注意的是,上面设置过一次画笔宽度,现在需要设置为0,否则画出的字挤在一起*/
// 画笔颜色
mPaint.setColor(textColor);
mPaint.setTextSize(textSize);
// 字体
mPaint.setTypeface(Typeface.DEFAULT_BOLD);
// 通过画布测量字体所占长度
float textWidth = mPaint.measureText(currentProgress+"%");
canvas.drawText(currentProgress+"%", viewWidth - textWidth/2, viewHeight + textSize/3, mPaint);

// 画进度
mPaint.setColor(drawColor);
mPaint.setStrokeWidth(drawWidth);
// 绘制进度
RectF oval = new RectF(viewWidth - radius, viewHeight - radius, viewWidth + radius, viewHeight + radius);
canvas.drawArc(oval, 0, getProgress(), false, mPaint);
}


我用的方法是,先画一个圆环作为底色,再到同样的位置画一个圆环,作为实时进度,中间画一个指示数字,根据任务执行的进度来更新圆环位置

public void setMaxProgress(int maxProgress) {
this.maxProgress = maxProgress;
}

public void setCurrentProgress(int currentProgress) {
this.currentProgress = currentProgress;
postInvalidate();
}

private int getProgress(){
if(currentProgress < maxProgress){
return 360*currentProgress/maxProgress;
}else{
return 360;
}
}


MainAcyivity代码

package com.cdemo.customview;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

private CustomRoundProgressBar customRoundProgressBar;
private int maxProgress = 100;
int currentProgress = 0;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

customRoundProgressBar = (CustomRoundProgressBar) findViewById(R.id.customprogressbar);
customRoundProgressBar.setMaxProgress(maxProgress);
mHandler.sendEmptyMessage(0x11);
}

Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0x11:
currentProgress++;
customRoundProgressBar.setCurrentProgress(currentProgress);
sendEmptyMessageDelayed(0x11, 200);
if (currentProgress >= maxProgress) {
currentProgress = maxProgress;
removeMessages(0x11);
}
break;
}
}
};

@Override
protected void onDestroy() {
super.onDestroy();
if(mHandler != null){
mHandler.removeCallbacksAndMessages(null);
}
}
}


Activity里面的代码主要就是开一个子线程模拟任务进度,然后更新圆环的进度,直到完成



源码下载
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android
相关文章推荐