package com.example.wireframe; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Rect; import android.graphics.RectF; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.view.animation.Animation; import android.view.animation.Transformation; /* * 1.该view在设置属性之后时候会有数字和圆圈不断增长的效果 * 2.该view在按下和放开状态下显示不同的样式。 */ public class CircleBar extends View { private static final String TAG = "cn.netmoon.netmoondevicemanager.widget.CircleBar"; private RectF mColorWheelRectangle = new RectF();// 圆圈的矩形范围 private Paint mDefaultWheelPaint; // 绘制底部灰色圆圈的画笔 private Paint mColorWheelPaint;// 绘制蓝色扇形的画笔 private Paint textPaint; // 中间文字的画笔 private float mColorWheelRadius;// 圆圈普通状态下的半径 private float circleStrokeWidth;// 圆圈的线条粗细 private float pressExtraStrokeWidth;// 按下状态下增加的圆圈线条增加的粗细 private String mText;// 中间文字内容 private int mCount; // 为了达到数字增加效果而添加的变量,他和mText其实代表一个意思 private float mSweepAnglePer;// 为了达到蓝色扇形增加效果而添加的变量,他和mSweepAngle其实代表一个意思 private float mSweepAngle;// 扇形弧度 private int mTextSize;// 文字颜色 BarAnimation anim;// 动画类 public CircleBar(Context context) { super(context); init(null, 0); } public CircleBar(Context context, AttributeSet attrs) { super(context, attrs); init(attrs, 0); } public CircleBar(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(attrs, defStyle); } private void init(AttributeSet attrs, int defStyle) { circleStrokeWidth = dip2px(getContext(), 10); pressExtraStrokeWidth = dip2px(getContext(), 2); // 构造方法调用之后,第一个调用的是init方法,在该方法 // 中初始化了各种画笔的颜色,风格等,字体大小和线条 // 粗细则使用了我自己定义的工具函数dip2px(),这样做的 // 目的是在不同分辨率的手机上,相同数值的最终显示效果 // 差别不大,比如字体大小mTextSize的初始化: mTextSize = dip2px(getContext(), 40); mColorWheelPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mColorWheelPaint.setColor(0xFF29a6f6); mColorWheelPaint.setStyle(Paint.Style.STROKE); mColorWheelPaint.setStrokeWidth(circleStrokeWidth); mDefaultWheelPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mDefaultWheelPaint.setColor(0xFFeeefef); mDefaultWheelPaint.setStyle(Paint.Style.STROKE); mDefaultWheelPaint.setStrokeWidth(circleStrokeWidth); textPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.LINEAR_TEXT_FLAG); textPaint.setColor(0xFF333333); textPaint.setStyle(Paint.Style.FILL_AND_STROKE); textPaint.setTextAlign(Align.LEFT); textPaint.setTextSize(mTextSize); mText = "0"; mSweepAngle = 0; anim = new BarAnimation(); anim.setDuration(2000); } @Override protected void onDraw(Canvas canvas) { canvas.drawArc(mColorWheelRectangle, -90, 360, false, mDefaultWheelPaint); // 在onDraw方法中我们绘制了圆圈、扇形以及文字, // 但是绘制需要用到的一些坐标值是经过计算得出的,比如绘制扇形: canvas.drawArc(mColorWheelRectangle, -90, mSweepAnglePer, false, mColorWheelPaint); Rect bounds = new Rect(); String textstr = mCount + ""; textPaint.getTextBounds(textstr, 0, textstr.length(), bounds); canvas.drawText(textstr + "", (mColorWheelRectangle.centerX()) - (textPaint.measureText(textstr) / 2), mColorWheelRectangle.centerY() + bounds.height() / 2, textPaint); } // mColorWheelRectangle是一个矩形, // 这个矩形的上下左右边界都是在onMeasure方法中根据控件所分配的大小得出来的。 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec); int width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec); int min = Math.min(width, height); setMeasuredDimension(min, min); mColorWheelRadius = min - circleStrokeWidth - pressExtraStrokeWidth; mColorWheelRectangle.set(circleStrokeWidth + pressExtraStrokeWidth, circleStrokeWidth + pressExtraStrokeWidth, mColorWheelRadius, mColorWheelRadius); } @Override public void setPressed(boolean pressed) { Log.i(TAG, "call setPressed "); if (pressed) { mColorWheelPaint.setColor(0xFF165da6); textPaint.setColor(0xFF070707); mColorWheelPaint.setStrokeWidth(circleStrokeWidth + pressExtraStrokeWidth); mDefaultWheelPaint.setStrokeWidth(circleStrokeWidth + pressExtraStrokeWidth); textPaint.setTextSize(mTextSize - pressExtraStrokeWidth); } else { mColorWheelPaint.setColor(0xFF29a6f6); textPaint.setColor(0xFF333333); mColorWheelPaint.setStrokeWidth(circleStrokeWidth); mDefaultWheelPaint.setStrokeWidth(circleStrokeWidth); textPaint.setTextSize(mTextSize); } super.setPressed(pressed); this.invalidate(); } public void startCustomAnimation() { this.startAnimation(anim); } public void setText(String text) { mText = text; this.startAnimation(anim); } public void setSweepAngle(float sweepAngle) { mSweepAngle = sweepAngle; } public class BarAnimation extends Animation { /** * Initializes expand collapse animation, has two types, collapse (1) * and expand (0). * * @param view * The view to animate * @param type * The type of animation: 0 will expand from gone and 0 size * to visible and layout size defined in xml. 1 will collapse * view and set to gone */ public BarAnimation() {} @Override protected void applyTransformation(float interpolatedTime, Transformation t) { super.applyTransformation(interpolatedTime, t); // 这个动画类利用了applyTransformation参数中 // 的interpolatedTime参数(从0到1)的变化特点,实现 // 了该View的某个属性随时间改变而改变。原理是在每 // 次系统调用animation的applyTransformation()方法时, // 改变mSweepAnglePer,mCount的值,然后调用postInvalidate()不停的绘制view。 if (interpolatedTime < 1.0f) { mSweepAnglePer = interpolatedTime * mSweepAngle; mCount = (int) (interpolatedTime * Float.parseFloat(mText)); } else { // mSweepAnglePer,mCount这两个属性只是动画过程中要用到的临时属性, // mText和mSweepAngle才是动画结束之后表示扇形弧度和中间数值的真实值。 mSweepAnglePer = mSweepAngle; mCount = Integer.parseInt(mText); } postInvalidate(); } } public static int dip2px(Context context, float dipValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dipValue * scale + 0.5f); } }
时间: 2024-10-24 14:38:30