請尊重個人勞動成果,轉載註明出處,謝謝!
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);
}
});
}
}