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

Android自定义view之(CSDN应用splash界面的倒计时View)

2016-08-08 21:54 429 查看
最近有点闲,准确来说是很闲,每天早上要在公车上度过一段时间,一般我就会打开csdn应用看看别人的博客,哎哟~! 看到CSDN欢迎页的倒计时view还不错哦,正好最近在学自定义View,所以就捣腾了一下,以前感觉遥不可及的东西真正做起来也不难哈,菜鸟一个,大牛勿喷^~^!!

先看一下效果图:



思路:

1、肯定是自定义个View了,偷懒下一个,为了不自己定义progress就直接继承ProgressBar,通过getProgress和getMax获取当前进度跟最大值。

2、重写onMeasure,测量View,自定义View的老套路了,测量的规则大概是这样的,如果明确了宽高就用定义的宽高,如果没有也就是wrap_content类型的时候,拿到文本也就是这里的“跳过”字的长度+一个自定义的offset偏移量的东西。

3、在ondraw里面不断的绘制,通过改变弧的startAngle绘制。

4、当达到最大值的时候开始回调(我这里为了测试通过监听动画结束来实现回调了)

atrr文件:

<!-- countdown view-->
<attr name="text" format="string"></attr>
<attr name="progress_color" format="color"></attr>
<attr name="center_bg" format="color"></attr>
<attr name="text_color" format="color"></attr>
<attr name="progress_width" format="dimension"></attr>
<declare-styleable name="count_down" >
<attr name="text" />
<attr name="progress_color" />
<attr name="center_bg" />
<attr name="text_color" />
<attr name="text_size"/>
<attr name="progress_width" />
</declare-styleable>


第一步:自定义一个CountDownView继承ProgressBar,照样是覆盖三个构造方法。

public class CountDownView extends ProgressBar {
/**
* 结束的位置
*/
private int endAngle=270;
private Paint mTextPaint;
private Paint mBgPaint;
/**
* 绘画的半径
*/
private int mRadius=dp2px(30);
/**
* 进度条color默认红色
*/
private int mProgressColor = Color.RED;
/**
* 进度条宽度,默认2dp
*/
private int mProgressWidth = dp2px(2);
/**
* 文本
*/
private String mText="跳过";
/**
* 文本颜色
*/
private int mTextColor=Color.WHITE;
/**
* 文本大小,默认14.5sp
*/
private int mTextSize=sp2px(14.5f);
/**
* 默认中心背景色
*/
private int centerBg=Color.GRAY;
/**
* 当wrapcontent的时候
* 控件的宽高为文字的宽度加上offset
*/
private float offset=15;
public CountDownView(Context context) {
this(context, null);
}

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

public CountDownView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
/**
获取参数和初始化Paint
*/
obtainStyleAttr(context, attrs, defStyleAttr);
////////////////测试
setMax(100);
startAnim();
///////////////测试
}
/**
* 获取参数
* @param context
* @param attrs
* @param defStyleAttr
*/
private void obtainStyleAttr(Context context, AttributeSet attrs, int defStyleAttr) {
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.count_down, defStyleAttr, 0);
int count=a.getIndexCount();
for (int i = 0; i < count; i++) {
int attr=a.getIndex(i);
switch (attr){
case R.styleable.count_down_center_bg:
centerBg=a.getColor(attr,centerBg);
break;
case R.styleable.count_down_text:
mText=a.getString(attr);
break;
case R.styleable.count_down_progress_color:
mProgressColor=a.getColor(attr,mProgressColor);
break;
case R.styleable.count_down_text_color:
mTextColor=a.getColor(attr,mTextColor);
break;
case R.styleable.count_down_text_size:
mTextSize=a.getDimensionPixelSize(attr,mTextSize);
break;
case R.styleable.count_down_progress_width:
mProgressWidth=a.getDimensionPixelSize(attr,mProgressWidth);
break;
}
}
a.recycle();
mTextPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setDither(true);
mTextPaint.setTextSize(mTextSize);
mTextPaint.setColor(mTextColor);
mTextPaint.setStyle(Paint.Style.FILL);

mBgPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
mBgPaint.setDither(true);
mBgPaint.setStyle(Paint.Style.STROKE);
mBgPaint.setStrokeCap(Paint.Cap.ROUND);
mBgPaint.setStrokeJoin(Paint.Join.ROUND);
mBgPaint.setStrokeWidth(mProgressWidth);
mBgPaint.setColor(mProgressColor);
}
}


第二步:重写onMeasure方法告诉父控件大小

@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
/**
* 当为wrap_content的时候,返回的大小为 (文本的长度+红色进度条的宽度*2+一个自定义的offset)
*/
int result= (int) (getPaddingLeft()+getPaddingRight()+mTextPaint.measureText(mText)+mProgressWidth*2)+dp2px(offset);
/**
* 传入一个result,当widthMode为MeasureSpec.UNSPECIFIED的时候返回result,
* 否则为设定的值
*/
MeasureSpec.UNSPECIFIED
int width=resolveSize(result, widthMeasureSpec);
int height=resolveSize(result,heightMeasureSpec);
result=Math.min(width,height);
setMeasuredDimension(result, result);
mRadius=getMeasuredWidth()/2;

}


点进resolveSize方法的代码你就懂了,

public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
final int specMode = MeasureSpec.getMode(measureSpec);
final int specSize = MeasureSpec.getSize(measureSpec);
final int result;
switch (specMode) {
case MeasureSpec.AT_MOST:
if (specSize < size) {
result = specSize | MEASURED_STATE_TOO_SMALL;
} else {
result = size;
}
break;
case MeasureSpec.EXACTLY:
result = specSize;
break;
case MeasureSpec.UNSPECIFIED:
default:
result = size;
}
return result | (childMeasuredState & MEASURED_STATE_MASK);
}


是不是很熟悉。嘻嘻!~ 没事多ctrl+左键一下就可以了~

第三步:来到了最关键的代码了

@Override
protected synchronized void onDraw(Canvas canvas) {
//画中间的圈
mTextPaint.setColor(centerBg);
canvas.drawCircle(mRadius, mRadius, mRadius-mProgressWidth/2, mTextPaint);
//绘制文本
mTextPaint.setColor(mTextColor);
int baseX= (int) (mRadius-mTextPaint.measureText(mText)/2);
int baseY= (int) (mRadius+(mTextPaint.getFontMetrics().bottom-mTextPaint.getFontMetrics().top)/2
-mTextPaint.getFontMetrics().bottom);
canvas.drawText(mText, baseX, baseY, mTextPaint);
/**
* 绘制进度条,
* 比如从startAngle从-90度的位置开始,endAngle为270位置(y的负轴位置)
* 需要绘制的角度为
* 270--90(开始)=360;
* 270-0(开始)=270
* 270-90(开始)=180
* 所以sweepAngle=startAngle-270(endAngle)
*/
int startAngle= (int) ((getProgress()*1.0f/getMax())*360)-90;
canvas.drawArc(new RectF(0+mProgressWidth/2, 0+mProgressWidth/2,mRadius*2-mProgressWidth/2, mRadius*2-mProgressWidth/2), startAngle, endAngle-startAngle, false, mBgPaint);
}


附带sp2px,dp2px的方法

/**
* dp2px
* -90 360  0 270 90 180
* @param value
* @return px
*/
private int dp2px(float value) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, getResources().getDisplayMetrics());
}

/**
* sp2px
*
* @param value
* @return px
*/
private int sp2px(float value) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, value, getResources().getDisplayMetrics());
}


第四步:打工告成了,测试一下(我们这就偷偷懒,用属性动画测试了,不过开发中可以换子线程+Handler或者别的方式实现,因为属性动画不兼容低版本)

public CountDownView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
obtainStyleAttr(context, attrs, defStyleAttr);
////////////////测试
setMax(100);
startAnim();
}
private void startAnim() {
ValueAnimator a=ValueAnimator.ofInt(0, getMax() + 1);
a.setDuration(5000);
a.setInterpolator(new LinearInterpolator());
a.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (int) animation.getAnimatedValue();
setProgress(value);
postInvalidate();
}
});
a.start();
a.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {

}

@Override
public void onAnimationEnd(Animator animation) {
Toast.makeText(getContext(), "计时完成,进入主页", Toast.LENGTH_SHORT).show();
}

@Override
public void onAnimationCancel(Animator animation) {

}

@Override
public void onAnimationRepeat(Animator animation) {

}
});
}


其实很简单吧,加起来才几十行代码,以前总感觉自定义view离我是那么的远,只要动手敲敲,摸索着前进,一定会有结果的~!!!

最后附上CountDownView代码:

package com.cisetech.customer.customer.Animation;

import android.animation.Animator;
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.util.AttributeSet;
import android.util.TypedValue;
import android.view.animation.LinearInterpolator;
import android.widget.ProgressBar;
import android.widget.Toast;

import com.cisetech.customer.customer.R;

/**
* Author:Yqy
* Date:2016-08-08
* Desc:倒计时View
* Company:cisetech
*/
public class CountDownView extends ProgressBar {
/**
* 结束的位置
*/
private int endAngle=270;
private Paint mTextPaint;
private Paint mBgPaint;
/**
* 绘画的半径
*/
private int mRadius=dp2px(30);
/**
* 进度条color默认红色
*/
private int mProgressColor = Color.RED;
/**
* 进度条宽度,默认2dp
*/
private int mProgressWidth = dp2px(2);
/**
* 文本
*/
private String mText="跳过";
/**
* 文本颜色
*/
private int mTextColor=Color.WHITE;
/**
* 文本大小,默认14.5sp
*/
private int mTextSize=sp2px(14.5f);
/**
* 默认中心背景色
*/
private int centerBg=Color.GRAY;
/**
* 当wrapcontent的时候
* 控件的宽高为文字的宽度加上offset
*/
private float offset=15;

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

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

public CountDownView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
obtainStyleAttr(context, attrs, defStyleAttr);
////////////////测试
setMax(100);
startAnim();
}

private void startAnim() {
ValueAnimator a=ValueAnimator.ofInt(0, getMax() + 1);
a.setDuration(5000);
a.setInterpolator(new LinearInterpolator());
a.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (int) animation.getAnimatedValue();
setProgress(value);
postInvalidate();
}
});
a.start();
a.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {

}

@Override
public void onAnimationEnd(Animator animation) {
Toast.makeText(getContext(), "完成", Toast.LENGTH_SHORT).show();
}

@Override
public void onAnimationCancel(Animator animation) {

}

@Override
public void onAnimationRepeat(Animator animation) {

}
});
}

/**
* 获取参数
* @param context
* @param attrs
* @param defStyleAttr
*/
private void obtainStyleAttr(Context context, AttributeSet attrs, int defStyleAttr) {
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.count_down, defStyleAttr, 0);
int count=a.getIndexCount();
for (int i = 0; i < count; i++) {
int attr=a.getIndex(i);
switch (attr){
case R.styleable.count_down_center_bg:
centerBg=a.getColor(attr,centerBg);
break;
case R.styleable.count_down_text:
mText=a.getString(attr);
break;
case R.styleable.count_down_progress_color:
mProgressColor=a.getColor(attr,mProgressColor);
break;
case R.styleable.count_down_text_color:
mTextColor=a.getColor(attr,mTextColor);
break;
case R.styleable.count_down_text_size:
mTextSize=a.getDimensionPixelSize(attr,mTextSize);
break;
case R.styleable.count_down_progress_width:
mProgressWidth=a.getDimensionPixelSize(attr,mProgressWidth);
break;
}
}
a.recycle();
mTextPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setDither(true);
mTextPaint.setTextSize(mTextSize);
mTextPaint.setColor(mTextColor);
mTextPaint.setStyle(Paint.Style.FILL);

mBgPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
mBgPaint.setDither(true);
mBgPaint.setStyle(Paint.Style.STROKE);
mBgPaint.setStrokeCap(Paint.Cap.ROUND);
mBgPaint.setStrokeJoin(Paint.Join.ROUND);
mBgPaint.setStrokeWidth(mProgressWidth);
mBgPaint.setColor(mProgressColor);
}

@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
/**
* 当为wrap_content的时候,返回的大小为 (文本的长度+红色进度条的宽度*2+一个自定义的offset)
*/
int result= (int) (getPaddingLeft()+getPaddingRight()+mTextPaint.measureText(mText)+mProgressWidth*2)+dp2px(offset);
/**
* 传入一个result,当widthMode为MeasureSpec.UNSPECIFIED的时候返回result,
* 否则为设定的值
*/
int width=resolveSize(result, widthMeasureSpec);
int height=resolveSize(result,heightMeasureSpec);
result=Math.min(width,height);
setMeasuredDimension(result, result);
mRadius=getMeasuredWidth()/2;

}
@Override
protected synchronized void onDraw(Canvas canvas) {
//画中间的圈
mTextPaint.setColor(centerBg);
canvas.drawCircle(mRadius, mRadius, mRadius-mProgressWidth/2, mTextPaint);
//绘制文本
mTextPaint.setColor(mTextColor);
int baseX= (int) (mRadius-mTextPaint.measureText(mText)/2);
int baseY= (int) (mRadius+(mTextPaint.getFontMetrics().bottom-mTextPaint.getFontMetrics().top)/2
-mTextPaint.getFontMetrics().bottom);
canvas.drawText(mText, baseX, baseY, mTextPaint);
/**
* 绘制进度条,
* 比如从startAngle从-90度的位置开始,endAngle为270位置(y的负轴位置)
* 需要绘制的角度为
* 270--90(开始)=360;
* 270-0(开始)=270
* 270-90(开始)=180
* 所以sweepAngle=startAngle-270(endAngle)
*/
int startAngle= (int) ((getProgress()*1.0f/getMax())*360)-90;
canvas.drawArc(new RectF(0+mProgressWidth/2, 0+mProgressWidth/2,mRadius*2-mProgressWidth/2, mRadius*2-mProgressWidth/2), startAngle, endAngle-startAngle, false, mBgPaint);
}
/**
* dp2px
* -90 360  0 270 90 180
* @param value
* @return px
*/
private int dp2px(float value) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, getResources().getDisplayMetrics());
}

/**
* sp2px
*
* @param value
* @return px
*/
private int sp2px(float value) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, value, getResources().getDisplayMetrics());
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: