请尊重个人劳动成果,转载注明出处,谢谢!
http://blog.csdn.net/xiaxiazaizai01
在做项目时需要实现一个带空白间隙的圆形加载进度view,并且要求颜色渐变,并简单的实现了手势上滑增加进度,下滑减少进度。老规矩,上效果图
关于自定义view也写了好几篇了,你该问了为什么还写,是不是有点没完没了,哈哈。。。没办法,在用别人的app时遇到耳目一新的功能都想去尝试模仿一下。套路还是和之前的差不多,这里我就不详细的介绍了,只介绍自定义类中的内容,不明白的可以去看我之前的博客。
自定义属性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CustomCircleView">
<attr name="default_circle_stroke_color" format="color"/>
<attr name="reached_ring_color" format="color"/>
<attr name="default_circle_stroke_width" format="dimension"/>
<attr name="reached_ring_stroke_width" format="dimension"/>
<attr name="text_color" format="color"/>
<attr name="text_size" format="dimension"/>
<attr name="radius" format="dimension"/>
</declare-styleable>
</resources>
由于代码中注释的已经很详细了,这里我就不再啰嗦了,直接上代码
public class CustomCircleView extends View {
//常量设置
private static final int DEFAULT_CIRCLE_STROKE_COLOR = Color.parseColor("#DEDEDE");
private static final int REACHED_RING_STROKE_WIDTH = 6;
private static final int DEFAULT_CIRCLE_STROKE_WIDTH = 6;
private static final int TEXT_COLOR = Color.parseColor("#000000");
private static final int TEXT_SIZE = 60;
private static final int RADIUS = 100;
private int defaultColor = DEFAULT_CIRCLE_STROKE_COLOR;//默认圆边框颜色
private int defaultWidth = dp2px(DEFAULT_CIRCLE_STROKE_WIDTH);//默认圆的边框宽度
private int reachedRingWidth = dp2px(REACHED_RING_STROKE_WIDTH);//进度圆环的宽度
private int textColor = TEXT_COLOR;
private int textSize = dp2px(TEXT_SIZE);
private int circleRadius = dp2px(RADIUS);
private Paint defaultCirclePaint;
private Paint reachedRingPaint;
private Paint textPaint;
private Paint textPaint2;
private int ringCount = 25;//圆环的块数
private int ringDistances = 3;//圆环之间的间隔距离
private float everyRingAngle;//每段圆环对应的角度
private int reachedRingCount = 0;
private int dataTextCount;
private String text;
//渐变的颜色值数组
private int[] colorArray = new int[]{Color.parseColor("#FF9900"),Color.parseColor("#FFFF00"),
Color.parseColor("#66FF00")};
//初始化
private Matrix matrix = new Matrix();
public CustomCircleView(Context context) {
this(context,null);
}
public CustomCircleView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public CustomCircleView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//获取自定义属性
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.CustomCircleView);
int indexCount = typedArray.getIndexCount();
for(int i=0;i<indexCount;i++){
int attr = typedArray.getIndex(i);
switch (attr){
case R.styleable.CustomCircleView_default_circle_stroke_color:
defaultColor = typedArray.getColor(attr, defaultColor);
break;
case R.styleable.CustomCircleView_default_circle_stroke_width:
defaultWidth = (int) typedArray.getDimension(attr, defaultWidth);
break;
case R.styleable.CustomCircleView_reached_ring_stroke_width:
reachedRingWidth = (int) typedArray.getDimension(attr, reachedRingWidth);
break;
case R.styleable.CustomCircleView_text_color:
textColor = typedArray.getColor(attr, textColor);
break;
case R.styleable.CustomCircleView_text_size:
textSize = (int) typedArray.getDimension(attr, textSize);
break;
case R.styleable.CustomCircleView_radius:
circleRadius = (int) typedArray.getDimension(attr, circleRadius);
break;
}
}
typedArray.recycle();//回收
//设置画笔
setPaint();
}
private void setPaint() {
defaultCirclePaint = new Paint();
defaultCirclePaint.setAntiAlias(true);//抗锯齿
defaultCirclePaint.setDither(true);//防抖动
defaultCirclePaint.setColor(defaultColor);
defaultCirclePaint.setStyle(Paint.Style.STROKE);
defaultCirclePaint.setStrokeWidth(defaultWidth);
reachedRingPaint = new Paint();
reachedRingPaint.setAntiAlias(true);
reachedRingPaint.setDither(true);
reachedRingPaint.setStyle(Paint.Style.STROKE);
reachedRingPaint.setStrokeWidth(reachedRingWidth);
textPaint = new Paint();
textPaint.setAntiAlias(true);
textPaint.setDither(true);
textPaint.setStyle(Paint.Style.FILL);
textPaint.setTextSize(textSize);
textPaint.setColor(textColor);
textPaint2 = new Paint();
textPaint2.setAntiAlias(true);
textPaint2.setDither(true);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize;
int heightSize;
int paintWidth = Math.max(defaultWidth,reachedRingWidth);//画笔宽度,二者stroke的最大值
if(widthMode != MeasureSpec.EXACTLY){
widthSize = getPaddingLeft() + paintWidth + circleRadius*2 + getPaddingRight();
widthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize,MeasureSpec.EXACTLY);
}
if(heightMode != MeasureSpec.EXACTLY){
heightSize = getPaddingTop() + getPaddingBottom() + paintWidth + circleRadius*2;
heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.translate(getPaddingLeft(),getPaddingTop());
//每一段圆环对应的角度
everyRingAngle = (360*1.0f - ringCount*ringDistances) / ringCount;
for(int i=0;i<ringCount;i++){
//绘制默认分段圆弧,习惯性都是从-90度的方向开始,所以这里-90
canvas.drawArc(new RectF(0,0,circleRadius*2,circleRadius*2),i*(everyRingAngle+ringDistances) - 90, everyRingAngle,false,defaultCirclePaint);
}
/**
* SweepGradient(float cx, float cy,int colors[], float positions[]);
* cx 渲染中心点x 坐标
* cy 渲染中心y 点坐标
* colors 围绕中心渲染的颜色数组,至少要有两种颜色值
* positions 相对位置的颜色数组,可为null, 若为null,颜色沿渐变线均匀分布
*/
SweepGradient sweepGradient = new SweepGradient(circleRadius,circleRadius,colorArray,null);
reachedRingPaint.setShader(sweepGradient);//给图像着色,SweepGradient是Shader的子类
matrix.setRotate(-90,circleRadius,circleRadius);//需设置旋转角度,不然的话颜色值数组中开始颜色和结束颜色值是相反的
sweepGradient.setLocalMatrix(matrix);
// reachedRingPaint.setShadowLayer(10,10,10,Color.BLUE);//设置底层阴影
//注意: 这个方法不支持硬件加速,所以我们要测试时必须先关闭硬件加速。
//加上这一句 setLayerType(LAYER_TYPE_SOFTWARE, null);
// setLayerType(LAYER_TYPE_SOFTWARE,reachedRingPaint);
/**
* setShadowLayer(float radius, float dx, float dy, int shadowColor);
* radius表示阴影的扩散半径;dx和dy表示阴影平面x、y上的偏移值;shadowColor阴影颜色。
*/
//画颜色渐变的进度圆弧
for(int i = 0;i<reachedRingCount;i++){
canvas.drawArc(new RectF(0,0,circleRadius*2,circleRadius*2),i*(everyRingAngle+ringDistances) - 90, everyRingAngle,false,reachedRingPaint);
}
//画中间文字进度
dataTextCount = reachedRingCount*4;
text = dataTextCount + "%";
float textWidth = textPaint.measureText(text);
float textHeight = (textPaint.descent() + textPaint.ascent()) / 2;
canvas.drawText(text, circleRadius - textWidth / 2, circleRadius - textHeight, textPaint);
//画顶部文字说明
String textExplain = "已学课时";
textPaint2.setColor(Color.parseColor("#275D9D"));
textPaint2.setTextSize(dp2px(20));
float textWidth2 = textPaint2.measureText(textExplain);
float textHeight2 = (textPaint2.descent() + textPaint2.ascent()) / 2;
canvas.drawText(textExplain,circleRadius - textWidth2 / 2, circleRadius - textHeight2 - dp2px(50), textPaint2);
canvas.restore();
}
private int yDown;//按下时y坐标
private int yUp;//抬起时y的坐标
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
yDown = (int) event.getY();
break;
case MotionEvent.ACTION_UP:
yUp = (int) event.getY();
if(yDown > yUp && reachedRingCount < ringCount){//表示上滑
up();
}else if(yDown < yUp && reachedRingCount > 0){
down();
}
break;
}
return true;
}
private void down(){
reachedRingCount--;
invalidate();
}
private void up(){
reachedRingCount++;
invalidate();
}
public void startAnima(int mValue){
ValueAnimator animator = ValueAnimator.ofInt(0, mValue / 4);
animator.setDuration(3000);
animator.setInterpolator(new LinearInterpolator());
animator.setRepeatCount(0);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
reachedRingCount = (int) animation.getAnimatedValue();
invalidate();
}
});
animator.start();//开启动画
}
public void setmValue(int data){
this.dataTextCount = data;
}
/**
* dp 2 px
*
* @param dpVal
*/
protected int dp2px(int dpVal)
{
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
dpVal, getResources().getDisplayMetrics());
}
/**
* sp 2 px
*
* @param spVal
* @return
*/
protected int sp2px(int spVal)
{
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
spVal, getResources().getDisplayMetrics());
}
}
最后是在MainActivity中去调用我们的自定义控件
public class MainActivity extends AppCompatActivity {
private CustomCircleView customCircleView;
private Button btnStart;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
customCircleView = (CustomCircleView) findViewById(R.id.customView);
btnStart = (Button) findViewById(R.id.btn_start);
btnStart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
customCircleView.startAnima(100);
}
});
}
}