Android 自定义的圆形变化ProgressBar

Android 自带的ProgressBar中大多比较丑,所以自己定制了一个ProgressBar,可以实现动态的播放,先看截图

技术分享

然后上代码,^_^原谅我不会说别的。

先看主程序布局文件

<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-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:background="@color/simple_bg"
    tools:context=".MainActivity" >

    <lib.vgod.progresscicle.library.DashProgressBar 
        android:id="@+id/simple"
        custom:external_color="#1affffff"
        custom:base_color="#6effffff"
        custom:min="0"
        custom:max="999"
        custom:progress_color="#000000"
        custom:progress_icon="@drawable/star"
        custom:duration="1500"
        android:layout_centerInParent="true"
        android:layout_width="300dp"
        android:layout_height="300dp">

        <RelativeLayout
            android:layout_centerInParent="true"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">

            <TextView
                android:textSize="25sp"
                android:id="@+id/text"
                android:textColor="#000000"
                android:text="@string/stars"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />

            <TextView
                android:textSize="30sp"
                android:layout_centerHorizontal="true"
                android:id="@+id/number"
                android:paddingTop="5dp"
                android:layout_below="@+id/text"
                android:textColor="#000000"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />

        </RelativeLayout>
        
    </lib.vgod.progresscicle.library.DashProgressBar>
    
    <Button 
        android:id="@+id/start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/simple"
        android:layout_alignParentLeft="true"
        android:text="@string/start"/>
    
    <Button 
        android:id="@+id/restart"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/simple"
        android:layout_toRightOf="@id/start"
        android:text="@string/restart"/>

</RelativeLayout>
然后主程序

package lib.vgod.progresscicle;

import lib.vgod.progresscicle.library.DashProgressBar;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.Button;

public class MainActivity extends Activity {

	DashProgressBar dashProgressBar;
	Button startBtn;
	Button restartBtn;
	
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        dashProgressBar = (DashProgressBar)findViewById(R.id.simple);
        startBtn = (Button)findViewById(R.id.start);
        restartBtn = (Button)findViewById(R.id.restart);
        
        startBtn.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View arg0) {
				dashProgressBar.startAnimate();
			}
		});
        
        restartBtn.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View arg0) {
				dashProgressBar.restartAnimate();
			}
		});
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
    
}

这里我自定义了一个绘制板,包括draw(),onSizeChanged()主要是方便在View中draw方法和onSizeChanged调用

package lib.vgod.progresscicle.library;

import android.graphics.Canvas;

/**
 * @author niuwei
 *
 */
public interface Painter {
	void draw(Canvas canvas);
	
	void onSizeChanged(int width, int height);
	
	void setColor(int color);
	
	int getColor();
}
然后是绘制外部圆圈的绘制板

package lib.vgod.progresscicle.library;

import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;

public class ExternalCirclePainter implements Painter{

	private Paint externalPainter;
	private RectF externalCircle;
	private int color;//颜色
    private int externalStrokeWidth = 4;//椭圆宽度
    private int startAngle = 279;//开始角度
    private int finishAngle = 341;//结束角度
    private int width;//宽
    private int height;//高
    private float marginTop = 11;//上边距
	
    /**
     * 初始构造器,构造外部圆圈画笔的颜色
     * @param externalColor
     */
    public ExternalCirclePainter(int externalColor) {
        this.color = externalColor;
        init();
    }

    /**
     * 初始化
     */
    private void init() {
        initExternalCirclePainter();
    }

    /**
     * 初始化画笔
     */
    private void initExternalCirclePainter() {
    	externalPainter = new Paint();
    	externalPainter.setAntiAlias(true);
    	externalPainter.setStrokeWidth(externalStrokeWidth);
        externalPainter.setColor(color);
        externalPainter.setStyle(Paint.Style.STROKE);
    }

    /**
     * 初始化圆圈的大小
     */
    private void initExternalCircle() {
        externalCircle = new RectF();
        externalCircle.set(externalStrokeWidth, externalStrokeWidth * marginTop,
                width - externalStrokeWidth, height - externalStrokeWidth);
    }
    
	@Override	
	public void draw(Canvas canvas) {
		canvas.drawArc(externalCircle, startAngle, finishAngle, false, externalPainter);
	}

	@Override
	public void onSizeChanged(int width, int height) {
		this.width = width;
		this.height = height;
		initExternalCircle();
	}

	@Override
	public void setColor(int color) {
		this.color = color;
	}

	@Override
	public int getColor() {
		return color;
	}
	
}

绘制内部dash圆圈的绘制板,其实主要起作用的是
<span style="color:#ff0000;">internalCirclePaint.setPathEffect(new DashPathEffect(new float[]{dashWidth, dashSpace}, dashSpace));</span>

package lib.vgod.progresscicle.library;

import android.graphics.Canvas;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.RectF;

public class InternalCirclePainter implements Painter{

	private RectF internalCircle;
    private Paint internalCirclePaint;
    private int color;
    private float startAngle = 270;
    private float finishAngle = 359.8f;
    private int width;
    private int height;
    private int internalStrokeWidth = 48;
    private int dashWidth = 5;
    private int dashSpace = 8;
    private float marginTop = 45;
	
    /**
     * 初始构造器,构造外部圆圈画笔的颜色
     * @param externalColor
     */
    public InternalCirclePainter(int externalColor) {
        this.color = externalColor;
        init();
    }

    /**
     * 初始化
     */
    private void init() {
    	initInternalCirclePainter();
    }
    
    /**
     * 初始化绘制圆圈的画笔
     */
    private void initInternalCirclePainter(){
    	internalCirclePaint = new Paint();
    	internalCirclePaint.setAntiAlias(true);
    	internalCirclePaint.setStrokeWidth(internalStrokeWidth);
    	internalCirclePaint.setColor(color);
        internalCirclePaint.setStyle(Paint.Style.STROKE);
        internalCirclePaint.setPathEffect(new DashPathEffect(new float[]{dashWidth, dashSpace}, dashSpace));
    }
    
    /**
     * 初始化内部圆圈的大小
     */
    private void initInternalCircle(){
    	internalCircle = new RectF();
    	float padding = internalStrokeWidth * 1.7f;
    	internalCircle.set(padding, padding + marginTop, width - padding, height - padding);
    }
    
	@Override
	public void draw(Canvas canvas) {
		canvas.drawArc(internalCircle, startAngle, finishAngle, false, internalCirclePaint);		
	}

	@Override
	public void onSizeChanged(int width, int height) {
		this.width = width;
        this.height = height;
        initInternalCircle();
	}

	@Override
	public void setColor(int color) {
		this.color = color;
		internalCirclePaint.setColor(this.color);
	}

	@Override
	public int getColor() {
		return this.color;
	}
	
}

然后是可变化的dash圆圈的接口

package lib.vgod.progresscicle.library;

/**
 * @author niuwei
 *
 */
public interface ProgressPainter extends Painter{
	void setMax(float max);

    void setMin(float min);

    void setValue(float value);
}

可变化的绘制圆圈类

package lib.vgod.progresscicle.library;

import android.graphics.Canvas;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.RectF;

/**
 * @author niuwei
 *
 */
public class ProgressPainterImp implements ProgressPainter{

	private float min;
	private float max;
	private int mColor;
	private Paint mPaint;
	private RectF progressCircle;
	private float startAngle = 270f;
    private float plusAngle = 0;
	private int width;
    private int height;
    private int internalStrokeWidth = 48;
    private int dashWidth = 5;
    private int dashSpace = 8;
    private float marginTop = 45;
    private float padding;
	
    public ProgressPainterImp(int color, float min, float max){
    	mColor = color;
    	this.min = min;
    	this.max = max;
    	init();
    }
    
    private void init(){
    	initInternalCirclePainter();
    }
    
    private void initInternalCirclePainter() {
    	mPaint = new Paint();
    	mPaint.setAntiAlias(true);
    	mPaint.setColor(mColor);
    	mPaint.setStrokeWidth(internalStrokeWidth);
    	mPaint.setStyle(Style.STROKE);
    	mPaint.setPathEffect(new DashPathEffect(new float[]{dashWidth, dashSpace}, dashSpace));
    }
    
    private void initInternalCircle() {
    	progressCircle = new RectF();
        padding = internalStrokeWidth * 1.7f;
        progressCircle.set(padding, padding + marginTop, width - padding, height - padding);
    }
    
	@Override
	public void draw(Canvas canvas) {
		canvas.drawArc(progressCircle, startAngle, plusAngle, false	, mPaint);
	}

	@Override
	public void onSizeChanged(int width, int height) {
		this.width = width;
		this.height = height;
		initInternalCircle();
	}

	@Override
	public void setColor(int color) {
		this.mColor = color;
		mPaint.setColor(this.mColor);
	}

	@Override
	public int getColor() {
		return mColor;
	}

	@Override
	public void setMax(float max) {
		this.max = max;
	}
	
	public float getMax(){
		return max;
	}

	@Override
	public void setMin(float min) {
		this.min = min;
	}
	
	public float getMin(){
		return min;
	}

	@Override
	public void setValue(float value) {
		plusAngle = (359.8f * value) / max;
	}

}

绘制中心图片
package lib.vgod.progresscicle.library;

import android.graphics.Canvas;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.RectF;

/**
 * @author niuwei
 *
 */
public class ProgressPainterImp implements ProgressPainter{

	private float min;
	private float max;
	private int mColor;
	private Paint mPaint;
	private RectF progressCircle;
	private float startAngle = 270f;
    private float plusAngle = 0;
	private int width;
    private int height;
    private int internalStrokeWidth = 48;
    private int dashWidth = 5;
    private int dashSpace = 8;
    private float marginTop = 45;
    private float padding;
	
    public ProgressPainterImp(int color, float min, float max){
    	mColor = color;
    	this.min = min;
    	this.max = max;
    	init();
    }
    
    private void init(){
    	initInternalCirclePainter();
    }
    
    private void initInternalCirclePainter() {
    	mPaint = new Paint();
    	mPaint.setAntiAlias(true);
    	mPaint.setColor(mColor);
    	mPaint.setStrokeWidth(internalStrokeWidth);
    	mPaint.setStyle(Style.STROKE);
    	mPaint.setPathEffect(new DashPathEffect(new float[]{dashWidth, dashSpace}, dashSpace));
    }
    
    private void initInternalCircle() {
    	progressCircle = new RectF();
        padding = internalStrokeWidth * 1.7f;
        progressCircle.set(padding, padding + marginTop, width - padding, height - padding);
    }
    
	@Override
	public void draw(Canvas canvas) {
		canvas.drawArc(progressCircle, startAngle, plusAngle, false	, mPaint);
	}

	@Override
	public void onSizeChanged(int width, int height) {
		this.width = width;
		this.height = height;
		initInternalCircle();
	}

	@Override
	public void setColor(int color) {
		this.mColor = color;
		mPaint.setColor(this.mColor);
	}

	@Override
	public int getColor() {
		return mColor;
	}

	@Override
	public void setMax(float max) {
		this.max = max;
	}
	
	public float getMax(){
		return max;
	}

	@Override
	public void setMin(float min) {
		this.min = min;
	}
	
	public float getMin(){
		return min;
	}

	@Override
	public void setValue(float value) {
		plusAngle = (359.8f * value) / max;
	}

}

继承自RelativeLayout的类
package lib.vgod.progresscicle.library;

import lib.vgod.progresscicle.R;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.RelativeLayout;

/**
 * @author niuwei
 *
 */
@SuppressLint("NewApi") public class DashProgressBar extends RelativeLayout{
	private final static String TAG = "DashProgressBar";
	
	/**
	 * 内圈的颜色
	 */
	private int externalCircleColor = Color.GRAY;
	
	/**
	 * 外圈的颜色
	 */
	private int internalCircleColor = Color.YELLOW;
	
	/**
	 * Progress的颜色
	 */
	private int progressCircleColor = Color.WHITE;
	
	/**
	 * Progress的最大值
	 */
	private float progressMax;
	
	/**
	 * Progress的最小值
	 */
	private float progressMin = 0;
	
	/**
	 * Progress的当前值
	 */
	private float progressLast = progressMin;
	
	/**
	 * 需要显示的图标
	 */
	private Bitmap iconBitmap;
	
	/**
	 * 动画持续时间
	 */
	private int ANIMATION_DURATION = 1000;
	/**
	 * 动画加速器
	 */
	private AccelerateDecelerateInterpolator mInterpolator;
	/**
	 * 动画
	 */
	private ValueAnimator mValueAnimator;
	/**
	 * 外部执行操作的接口
	 */
	private OnValueChangeListener mOnValueChangeListener;
	
	/**
	 * 画笔元素
	 */
	private ExternalCirclePainter externalCirclePainter;
	private InternalCirclePainter internalCirclePainter;
	private BitmapPainter bitmapPainter;
	private ProgressPainterImp progressPainterImp;
	
	private int heightNormalittation = 10;
	
	public DashProgressBar(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context, attrs);
	}
	
	public DashProgressBar(Context context, AttributeSet attrs, int defStyle){
		super(context, attrs, defStyle);
		init(context, attrs);
	}
	
	/**
	 * 初始化函数
	 */
	private void init(Context context, AttributeSet attributeSet){
		setWillNotDraw(false);
		initAttributes(attributeSet);
		initPainter();
		initData();
	}
	
	/**
	 * 初始化变量
	 */
	private void initData(){
		mInterpolator = new AccelerateDecelerateInterpolator();
		initAnimate();
	}
	
	/**
	 * 初始化动画
	 */
	private void initAnimate(){
		mValueAnimator = new ValueAnimator();
		mValueAnimator.setInterpolator(mInterpolator);
		mValueAnimator.addUpdateListener(new ValueAnimationChangeListener());
	}
	
	/**
	 * 初始化属性
	 */
	private void initAttributes(AttributeSet attributeSet){
		TypedArray ta = getContext().obtainStyledAttributes(attributeSet, R.styleable.DashedCircleProgress);
		externalCircleColor = ta.getColor(R.styleable.DashedCircleProgress_external_color, externalCircleColor);
		internalCircleColor = ta.getColor(R.styleable.DashedCircleProgress_base_color, internalCircleColor);
		progressCircleColor = ta.getColor(R.styleable.DashedCircleProgress_progress_color, progressCircleColor);
		progressMax = ta.getFloat(R.styleable.DashedCircleProgress_max, progressMax);
		ANIMATION_DURATION = ta.getInt(R.styleable.DashedCircleProgress_duration, ANIMATION_DURATION);
		iconBitmap = BitmapFactory.decodeResource(getResources(), ta.getResourceId(R.styleable.DashedCircleProgress_progress_icon, 0));
		ta.recycle();
	}
	
	/**
	 * 初始化画笔
	 */
	private void initPainter(){
		internalCirclePainter = new InternalCirclePainter(internalCircleColor);
		externalCirclePainter = new ExternalCirclePainter(externalCircleColor);
		bitmapPainter = new BitmapPainter(iconBitmap);
		progressPainterImp = new ProgressPainterImp(progressCircleColor, progressMin, progressMax);
	}
	
	/**
	 * 开始动画
	 */
	public void startAnimate(float max){
		if (mValueAnimator != null) {
			mValueAnimator.setFloatValues(progressLast, max);
			mValueAnimator.setDuration(ANIMATION_DURATION);
			mValueAnimator.start();
		}
	}
	
	/**
	 * 开始动画
	 */
	public void startAnimate(){
		if (mValueAnimator != null) {
			mValueAnimator.setFloatValues(progressLast, progressMax);
			mValueAnimator.setDuration(ANIMATION_DURATION);
			mValueAnimator.start();
		}
	}
	
	/**
	 * 重新开始动画
	 */
	public void restartAnimate(){
		progressLast = progressMin;
		startAnimate(progressMax);
	}
	
	/**
	 * 设置动画执行到什么时候
	 */
	public void setValue(int value){
		if (value <= progressMax && value >= progressMin) {
			progressLast = progressMin;
			startAnimate(value);
		}
	}
	
	
	/**
	 * 动画监听器,更新内部绘制画面和外部接口回调
	 * @author niuwei
	 *
	 */
	private class ValueAnimationChangeListener implements ValueAnimator.AnimatorUpdateListener {

		@Override
		public void onAnimationUpdate(ValueAnimator valueAnimator) {
			Float value = (Float)valueAnimator.getAnimatedValue();
			progressPainterImp.setValue(value);
			
			if (mOnValueChangeListener != null) {
				mOnValueChangeListener.onChange(value);
			}
			
			progressLast = value;
		}
	}
	
	/**
	 * 接口,当动画改编的过程中UI线程可以进行一些操作 
	 */
	private interface OnValueChangeListener{
		void onChange(float change);
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		super.onLayout(changed, l, t, r, b);
		Log.d(TAG, "onLayout()");
		
		final int count = getChildCount();
		int maxWidth = getWidth() / 2;
		int maxHeight = getHeight() / 2;
		for (int i = 0; i < count; i++) {
			final View child = getChildAt(i);
			int mesaureWidth = child.getMeasuredWidth();
			int measureHeight = child.getMeasuredHeight();
			ViewGroup.LayoutParams layoutParams = child.getLayoutParams();
			
			RelativeLayout.LayoutParams relativeLayoutParams =
                    (RelativeLayout.LayoutParams) child.getLayoutParams();
            relativeLayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
            child.setLayoutParams(relativeLayoutParams);
			
			if (mesaureWidth > maxWidth) {
                layoutParams.width = maxWidth;
            }

            if (measureHeight > maxHeight) {
                layoutParams.height = maxHeight;
            }
		}
	}

	@Override
	protected void onDraw(Canvas canvas){
		super.onDraw(canvas);
		Log.d(TAG, "onDraw()");
		
		externalCirclePainter.draw(canvas);
		internalCirclePainter.draw(canvas);
		bitmapPainter.draw(canvas);
		progressPainterImp.draw(canvas);
		invalidate();
	}
	
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec + heightNormalittation);
		Log.d(TAG, "onMeasure()");
	}

	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
		Log.d(TAG, "onSizeChanged()");
		
		externalCirclePainter.onSizeChanged(w, h);
		internalCirclePainter.onSizeChanged(w, h);
		bitmapPainter.onSizeChanged(w, h);
		progressPainterImp.onSizeChanged(w, h);	
	}
	
	public void setmOnValueChangeListener(
			OnValueChangeListener mOnValueChangeListener) {
		this.mOnValueChangeListener = mOnValueChangeListener;
	}
	
}


最后是自定义的属性

<?xml version="1.0" encoding="utf-8"?>
<resources>
   	<declare-styleable name="DashedCircleProgress">
  	    <attr name="external_color" format="color" />
        <attr name="base_color" format="color" />
        <attr name="progress_color" format="color" />
        <attr name="max" format="float" />
        <attr name="min" format="float" />
        <attr name="progress_icon" format="reference" />
        <attr name="duration" format="integer" />
   	</declare-styleable>
</resources>

大功告成,需要资源可以点击下载 下载

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。