自定义一个圆环进度条:
1.首页Android Studio创建一个项目
2.在项目src/xxx/目录下右键选择创建一个自定义view页面:new->UICompoent->customer view
3.输入自定义名称,选择开发语言
4.确定之后,自动生成3个文件一个是:
第一个是逻辑代码:com.lan.lanidemo.customeview.SuperCircleView.class 第二个是布局: src\main\res\layout\sample_super_circle_view.xml 第三个是view需要使用的属性:src\main\res\values\attrs_super_circle_view.xml (通过这种方式创建属性,多次创建可能造成一些变量重复定义。)
attrs_super_circle_view.xml源码:
其中declare-styeable节点的name属性值一般是你写的view的名字,如这里根据命名默认生成:SuperCirecleView。接下来定义可以在xml中定义的组件属性,在后面我会根据需要增加圆环宽度颜色等。其中format属性指定可接受值的类型,多个类型用“|”分隔。
sample_super_circle_view.xml布局源码
SuperCircleView.class
package com.lan.lanidemo.customeview;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.View;
import com.lan.lanidemo.R;
/**
* TODO: document your custom view class.
*/
public class SuperCircleView extends View {
private String mExampleString; // TODO: use a default from R.string...
private int mExampleColor = Color.RED; // TODO: use a default from R.color...
private float mExampleDimension = 0; // TODO: use a default from R.dimen...
private Drawable mExampleDrawable;
private TextPaint mTextPaint;
private float mTextWidth;
private float mTextHeight;
public SuperCircleView(Context context) {
super( context );
init( null, 0 );
}
public SuperCircleView(Context context, AttributeSet attrs) {
super( context, attrs );
init( attrs, 0 );
}
public SuperCircleView(Context context, AttributeSet attrs, int defStyle) {
super( context, attrs, defStyle );
init( attrs, defStyle );
}
private void init(AttributeSet attrs, int defStyle) {
// Load attributes
final TypedArray a = getContext().obtainStyledAttributes(
attrs, R.styleable.SuperCircleView, defStyle, 0 );
mExampleString = a.getString(
R.styleable.SuperCircleView_exampleString );
mExampleColor = a.getColor(
R.styleable.SuperCircleView_exampleColor,
mExampleColor );
// Use getDimensionPixelSize or getDimensionPixelOffset when dealing with
// values that should fall on pixel boundaries.
mExampleDimension = a.getDimension(
R.styleable.SuperCircleView_exampleDimension,
mExampleDimension );
if (a.hasValue( R.styleable.SuperCircleView_exampleDrawable )) {
mExampleDrawable = a.getDrawable(
R.styleable.SuperCircleView_exampleDrawable );
mExampleDrawable.setCallback( this );
}
a.recycle();
// Set up a default TextPaint object
mTextPaint = new TextPaint();
mTextPaint.setFlags( Paint.ANTI_ALIAS_FLAG );
mTextPaint.setTextAlign( Paint.Align.LEFT );
// Update TextPaint and text measurements from attributes
invalidateTextPaintAndMeasurements();
}
private void invalidateTextPaintAndMeasurements() {
mTextPaint.setTextSize( mExampleDimension );
mTextPaint.setColor( mExampleColor );
mTextWidth = mTextPaint.measureText( mExampleString );
Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
mTextHeight = fontMetrics.bottom;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw( canvas );
// TODO: consider storing these as member variables to reduce
// allocations per draw cycle.
int paddingLeft = getPaddingLeft();
int paddingTop = getPaddingTop();
int paddingRight = getPaddingRight();
int paddingBottom = getPaddingBottom();
int contentWidth = getWidth() - paddingLeft - paddingRight;
int contentHeight = getHeight() - paddingTop - paddingBottom;
// Draw the text.
canvas.drawText( mExampleString,
paddingLeft + (contentWidth - mTextWidth) / 2,
paddingTop + (contentHeight + mTextHeight) / 2,
mTextPaint );
// Draw the example drawable on top of the text.
if (mExampleDrawable != null) {
mExampleDrawable.setBounds( paddingLeft, paddingTop,
paddingLeft + contentWidth, paddingTop + contentHeight );
mExampleDrawable.draw( canvas );
}
}
/**
* Gets the example string attribute value.
*
* @return The example string attribute value.
*/
public String getExampleString() {
return mExampleString;
}
/**
* Sets the view"s example string attribute value. In the example view, this string
* is the text to draw.
*
* @param exampleString The example string attribute value to use.
*/
public void setExampleString(String exampleString) {
mExampleString = exampleString;
invalidateTextPaintAndMeasurements();
}
/**
* Gets the example color attribute value.
*
* @return The example color attribute value.
*/
public int getExampleColor() {
return mExampleColor;
}
/**
* Sets the view"s example color attribute value. In the example view, this color
* is the font color.
*
* @param exampleColor The example color attribute value to use.
*/
public void setExampleColor(int exampleColor) {
mExampleColor = exampleColor;
invalidateTextPaintAndMeasurements();
}
/**
* Gets the example dimension attribute value.
*
* @return The example dimension attribute value.
*/
public float getExampleDimension() {
return mExampleDimension;
}
/**
* Sets the view"s example dimension attribute value. In the example view, this dimension
* is the font size.
*
* @param exampleDimension The example dimension attribute value to use.
*/
public void setExampleDimension(float exampleDimension) {
mExampleDimension = exampleDimension;
invalidateTextPaintAndMeasurements();
}
/**
* Gets the example drawable attribute value.
*
* @return The example drawable attribute value.
*/
public Drawable getExampleDrawable() {
return mExampleDrawable;
}
/**
* Sets the view"s example drawable attribute value. In the example view, this drawable is
* drawn above the text.
*
* @param exampleDrawable The example drawable attribute value to use.
*/
public void setExampleDrawable(Drawable exampleDrawable) {
mExampleDrawable = exampleDrawable;
}
}
5.在这里我是做一圆环进度条:如本文开始图片效果,更新代码如下。
6. 更新自定义属性:
attrs_super_circle_view.xml
7.更新自定义view控制代码:
SuperCircleView.class
package com.lan.lanidemo.customeview;
import android.animation.ValueAnimator;
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.SweepGradient;
import android.graphics.drawable.Drawable;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import com.lan.lanidemo.R;
import static android.content.ContentValues.TAG;
/**
* TODO: document your custom view class.
*/
public class SuperCircleView extends View {
private String mExampleString; // TODO: use a default from R.string...
private int mExampleColor = Color.RED; // TODO: use a default from R.color...
private float mExampleDimension = 0; // TODO: use a default from R.dimen...
private Drawable mExampleDrawable;
private TextPaint mTextPaint;
private float mTextWidth;
private float mTextHeight;
private ValueAnimator valueAnimator;
private int mViewCenterX; //view宽的中心点(可以暂时理解为圆心)
private int mViewCenterY; //view高的中心点(可以暂时理解为圆心)
private int mMinRadio; //最里面白色圆的半径
private float mRingWidth; //圆环的宽度
private int mMinCircleColor; //最里面圆的颜色
private int mRingNormalColor; //默认圆环的颜色
private Paint mPaint;
private int color[] = new int[3]; //渐变颜色
private RectF mRectF; //圆环的矩形区域
private int mSelectRing = 0; //要显示的彩色区域(岁数值变化)
float ringRadius;
private int mMaxValue;
public SuperCircleView(Context context) {
super( context );
Log.d( TAG, "SuperCircleView: >>构造" );
init( null, 0 );
}
public SuperCircleView(Context context, AttributeSet attrs) {
super( context, attrs );
Log.d( TAG, "SuperCircleView: >>构造2" );
init( attrs, 0 );
}
public SuperCircleView(Context context, AttributeSet attrs, int defStyle) {
super( context, attrs, defStyle );
init( attrs, defStyle );
}
private void init(AttributeSet attrs, int defStyle) {
// Load attributes
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.SuperCircleView);
//最里面白色圆的半径
mMinRadio = a.getInteger(R.styleable.SuperCircleView_min_circle_radio, 300);
//圆环宽度
mRingWidth = a.getFloat(R.styleable.SuperCircleView_ring_width, 40);
//最里面的圆的颜色(绿色)
mMinCircleColor = a.getColor(R.styleable.SuperCircleView_circle_color,
getContext().getResources().getColor(R.color.green));
//圆环的默认颜色(圆环占据的是里面的圆的空间)
mRingNormalColor = a.getColor(R.styleable.SuperCircleView_ring_normal_color,
getContext().getResources().getColor(R.color.gray));
//圆环要显示的彩色的区域
mSelectRing = a.getInt(R.styleable.SuperCircleView_ring_color_select, 0);
mMaxValue = a.getInt(R.styleable.SuperCircleView_maxValue, 100);
ringRadius = a.getDimension( R.styleable.SuperCircleView_ring_radius, dp2px(100) );
a.recycle();
//抗锯齿画笔
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
//防止边缘锯齿
mPaint.setAntiAlias(true);
//需要重写onDraw就得调用此
this.setWillNotDraw(false);
//圆环渐变的颜色
color[0] = Color.parseColor("#FFD300");
color[1] = Color.parseColor("#FF0084");
color[2] = Color.parseColor("#16FF00");
}
public int dp2px(float dpValue) {
final float scale = Resources.getSystem().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
//view的宽和高,相对于父布局(用于确定圆心)
int viewWidth = getMeasuredWidth();
int viewHeight = getMeasuredHeight();
mViewCenterX = viewWidth / 2;
mViewCenterY = viewHeight / 2;
mRectF = new RectF(
mViewCenterX - ringRadius,
mViewCenterY - ringRadius,
mViewCenterX + ringRadius,
mViewCenterY + ringRadius
);
}
private void invalidateTextPaintAndMeasurements() {
mTextPaint.setTextSize( mExampleDimension );
mTextPaint.setColor( mExampleColor );
mTextWidth = mTextPaint.measureText( mExampleString );
Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
mTextHeight = fontMetrics.bottom;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw( canvas );
mPaint.setColor(mMinCircleColor);
canvas.drawCircle(mViewCenterX, mViewCenterY, mMinRadio, mPaint);
//画默认圆环
drawNormalRing(canvas);
//画彩色圆环
drawColorRing(canvas);
}
/**
* 画默认圆环
*
* @param canvas
*/
private void drawNormalRing(Canvas canvas) {
Paint ringNormalPaint = new Paint(mPaint);
ringNormalPaint.setStyle(Paint.Style.STROKE);
ringNormalPaint.setStrokeWidth(mRingWidth);
ringNormalPaint.setColor(mRingNormalColor);//圆环默认颜色为灰色
canvas.drawArc(mRectF, 360, 360, false, ringNormalPaint);
}
/**
* 画彩色圆环
*
* @param canvas
*/
private void drawColorRing(Canvas canvas) {
Paint ringColorPaint = new Paint(mPaint);
ringColorPaint.setStyle(Paint.Style.STROKE);
ringColorPaint.setStrokeWidth(mRingWidth);
ringColorPaint.setShader(new SweepGradient(mViewCenterX, mViewCenterX, color, null));
//逆时针旋转90度
canvas.rotate(-90, mViewCenterX, mViewCenterY);
canvas.drawArc(mRectF, 360, mSelectRing, false, ringColorPaint);
ringColorPaint.setShader(null);
}
//***************************************用于更新圆环表示的数值*****************************************************
/**
* 设置当前值
*
* @param value
*/
public void setValue(int value,TextView textView) {
if (value > mMaxValue) {
value = mMaxValue;
}
int start = 0;
int end = value;
startAnimator(start, end, 2000,textView);
}
private void startAnimator(int start, int end, long animTime, final TextView textView) {
valueAnimator = ValueAnimator.ofInt(start, end);
valueAnimator.setDuration(animTime);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Log.i(TAG, "onAnimationUpdate: animation.getAnimatedValue()::"+animation.getAnimatedValue());
int i = Integer.valueOf(String.valueOf(animation.getAnimatedValue()));
textView.setText(i + "");
//每个单位长度占多少度
mSelectRing=(int) (360 * (i / 100f));
Log.i(TAG, "onAnimationUpdate: mSelectRing::"+mSelectRing);
invalidate();
}
});
valueAnimator.start();
}
/**
* Gets the example string attribute value.
*
* @return The example string attribute value.
*/
public String getExampleString() {
return mExampleString;
}
/**
* Sets the view"s example string attribute value. In the example view, this string
* is the text to draw.
*
* @param exampleString The example string attribute value to use.
*/
public void setExampleString(String exampleString) {
mExampleString = exampleString;
invalidateTextPaintAndMeasurements();
}
/**
* Gets the example color attribute value.
*
* @return The example color attribute value.
*/
public int getExampleColor() {
return mExampleColor;
}
/**
* Sets the view"s example color attribute value. In the example view, this color
* is the font color.
*
* @param exampleColor The example color attribute value to use.
*/
public void setExampleColor(int exampleColor) {
mExampleColor = exampleColor;
invalidateTextPaintAndMeasurements();
}
/**
* Gets the example dimension attribute value.
*
* @return The example dimension attribute value.
*/
public float getExampleDimension() {
return mExampleDimension;
}
/**
* Sets the view"s example dimension attribute value. In the example view, this dimension
* is the font size.
*
* @param exampleDimension The example dimension attribute value to use.
*/
public void setExampleDimension(float exampleDimension) {
mExampleDimension = exampleDimension;
invalidateTextPaintAndMeasurements();
}
/**
* Gets the example drawable attribute value.
*
* @return The example drawable attribute value.
*/
public Drawable getExampleDrawable() {
return mExampleDrawable;
}
/**
* Sets the view"s example drawable attribute value. In the example view, this drawable is
* drawn above the text.
*
* @param exampleDrawable The example drawable attribute value to use.
*/
public void setExampleDrawable(Drawable exampleDrawable) {
mExampleDrawable = exampleDrawable;
}
}
8.更新自定义布局:sample_super_circle_view.xml
9.最后在新建一个页面:ThreeActivity, 并在AndroidManifest.xml,设置为启动页面。创建完成后,在xml添加自定义view:activity_three.xml
10. ThreeActivity.class 在启动页设置百分比。 同时增加一个点击事件,点击view可以随机变化百分比值。
package com.lan.lanidemo;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import com.lan.lanidemo.customeview.SuperCircleView;
import java.util.Random;
import androidx.appcompat.app.AppCompatActivity;
public class ThreeActivity extends AppCompatActivity {
private static final String TAG = "ThreeActivity";
private TextView mTextView;
SuperCircleView mSuperCircleView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_three );
initView();
}
private void initView() {
mTextView = (TextView) findViewById( R.id.tv );
mSuperCircleView = findViewById(R.id.superview);
mSuperCircleView.setValue(70, mTextView);
mSuperCircleView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//随机设定圆环大小
int i = new Random().nextInt(100) + 1;
Log.i(TAG, "onClick: i::" + i);
mSuperCircleView.setValue(i, mTextView);
}
});
}
}
11.最后运行,点击之后进度更新。