我在一个[博客](http://blog.csdn.net/qq_31715429/article/details/54668668)上面看到了小米时钟实现.特别感兴趣.就认真的看了一遍.并自己敲了一遍.下面说下我自己的理解和我的一些改进的地方效果真的特别棒就发布了自己的时钟应用--下载地址
先上图(电脑没有gif截图软件.大家凑合看.哪个软件好也可以给我推荐下)
![图一](https://img-blog.csdn.net/20170310112803575?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZHhqMTUxMzY0MTM2/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
话不多说,首先自定义控件XimiClockView继承view 并做一些初始化的操作
public XimiClockView(Context context) {
super(context);
init(context, null);
}
public XimiClockView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public XimiClockView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public XimiClockView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
/**
* 进行一些初始化的操作
*
* @param context
* @param attrs
*/
private void init(Context context, AttributeSet attrs) {
if (attrs == null) return;
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyClockView);
backGroundColor = array.getColor(R.styleable.MyClockView_clockBackColor, Color.parseColor("#2078A8"));
drakColor = array.getColor(R.styleable.MyClockView_clockDarkColor, Color.parseColor("#96C2D8"));
lightColor = array.getColor(R.styleable.MyClockView_clockLightColor, Color.parseColor("#ffffff"));
array.recycle();
setBackgroundColor(backGroundColor);
textPaint = new Paint();
textPaint.setColor(drakColor);
textPaint.setTextSize(25);
textRect = new Rect();
}
然后在onSizeChange方法中调用获取时钟半径,和一些其他的计算
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mRadius = Math.min(w - getPaddingLeft() - getPaddingRight(), h - getPaddingTop() - getPaddingBottom()) / 2;
defaultPadding = 0.12f * mRadius;
mCircleWidth = 0.012f * mRadius;
mCircleRingWidth = 0.12f*mRadius;
paddingLeft = w / 2 - mRadius + getPaddingLeft() + defaultPadding;
paddingTop = h / 2 - mRadius + getPaddingTop() + defaultPadding;
paddingRight = w / 2 - mRadius + getPaddingRight() + defaultPadding;
paddingBottom = h / 2 - mRadius + getPaddingBottom() + defaultPadding;
mSweepGradient = new SweepGradient(w / 2,h / 2 ,new int[]{drakColor,lightColor},new float[]{0.75f,1f});
}
画Text
@Override
protected void onDraw(Canvas canvas) {
mCanvas = canvas;
drawTimeText();
}
/**
* 画12 /3 /6 /9时间
*/
private void drawTimeText() {
String str = "12";
textPaint.getTextBounds(str, 0, str.length(), textRect);
int lengthTextWidth = textRect.width();
int textHeight = textRect.height();
int width=getWidth();
int height=getHeight();
mCanvas.save();
mCanvas.drawText(str, width / 2-lengthTextWidth/2,paddingTop+textHeight,textPaint);
str="3";
textPaint.getTextBounds(str, 0, str.length(), textRect);
int smallTextWidth = textRect.width();
mCanvas.drawText("3",width-paddingRight-lengthTextWidth/2-smallTextWidth/2,height/2+textHeight/2,textPaint);
mCanvas.drawText("6",width / 2-smallTextWidth/2,height-paddingBottom,textPaint);
mCanvas.drawText("9",paddingLeft,height/2+textHeight/2,textPaint);
mCanvas.restore();
}
崩溃写完了,我发表时候提示我没有登录,然后发表异常,然后我一刷新没了,,,又重新写一遍,
接下来画圆弧
/**
* 画圆弧
*/
private void drawCircleArc() {
mCanvas.save();
mCirclePaint.setStyle(Paint.Style.STROKE);
mCirclePaint.setStrokeWidth(mCircleWidth);
mCircleRectF.set(paddingLeft + textRect.width() / 2, paddingTop + textRect.height() / 2, getWidth() - paddingRight - textRect.width() / 2, getHeight() - paddingBottom - textRect.height() / 2);
for (int x = 0; x < 4; x++) {
mCanvas.drawArc(mCircleRectF, 5 + 90 * x, 80, false, mCirclePaint);
}
mCanvas.restore();
}
画刻度
/**
* 画圆环和刻度
*/
private void drawCircleRing() {
mCanvas.save();
mCircleRingRectF.set(paddingLeft+textRect.height()/2+1.5f*mCircleRingWidth,paddingTop+textRect.height()/2+1.5f*mCircleRingWidth,getWidth()-paddingRight-textRect.height()/2-1.5f*mCircleRingWidth,
getHeight()-paddingBottom-textRect.height()/2-1.5f*mCircleRingWidth);
mMatrix.setRotate(mDegreeS-90,getWidth()/2,getHeight()/2);
mSweepGradient.setLocalMatrix(mMatrix);
mCircleRingPaint.setStyle(Paint.Style.STROKE);
mCircleRingPaint.setStrokeWidth(mCircleRingWidth);
mCircleRingPaint.setShader(mSweepGradient);
mCanvas.drawArc(mCircleRingRectF,0,360,false,mCircleRingPaint);
mScaleLinePaint.setStrokeWidth(0.1f*mCircleRingWidth);
for (int i=0;i<200;i++){
mCanvas.drawLine(getWidth()/2,paddingTop+textRect.height()/2+mCircleRingWidth,getWidth()/2,paddingTop+textRect.height()/2+2*mCircleRingWidth,mScaleLinePaint);
mCanvas.rotate(1.8f,getWidth()/2,getHeight()/2);
}
mCanvas.restore();
}
画秒针
/**
* 画秒针
* 秒针针是不规则的图形
* 就用到了Path这个类
*
*/
private void drawSoundHand() {
mCanvas.save();
path.reset();
mCanvas.rotate(mDegreeS,getWidth()/2,getHeight()/2);
path.moveTo(getWidth()/2,paddingTop+textRect.height()/2+0.27f*mRadius);
path.lineTo(getWidth()/2+0.03f*mRadius,paddingTop+textRect.height()/2+0.31f*mRadius);
path.lineTo(getWidth()/2-0.03f*mRadius,paddingTop+textRect.height()/2+0.31f*mRadius);
path.close();
mCanvas.drawPath(path,mSoundHandPaint);
mCanvas.restore();
}
画时针
/**
* 画时针
*/
private void drawHourHand() {
mCanvas.save();
mCanvas.rotate(mDegreeH,getWidth()/2,getHeight()/2);
hourHandPath.reset();
hourHandPath.moveTo(getWidth()/2-0.02f*mRadius,getHeight()/2);
hourHandPath.lineTo(getWidth()/2-0.01f*mRadius,getHeight()/2-0.35f*mRadius);
hourHandPath.quadTo(getWidth()/2,getHeight()/2-0.38f*mRadius,getWidth()/2+0.01f*mRadius,getHeight()/2-0.35f*mRadius);
hourHandPath.lineTo(getWidth()/2+0.02f*mRadius,getHeight()/2);
hourHandPath.close();
mCanvas.drawPath(hourHandPath,mHourHandPaint);
mCanvas.restore();
}
画分针
/**
* 画分针
*/
private void drawMinnuteHand() {
mCanvas.save();
mCanvas.rotate(mDegreeM,getWidth()/2,getHeight()/2);
mMinutePath.reset();
mMinutePath.moveTo(getWidth()/2-0.012f*mRadius,getHeight()/2);
mMinutePath.lineTo(getWidth()/2-0.006f*mRadius,getHeight()/2-0.40f*mRadius);
mMinutePath.quadTo(getWidth()/2,getHeight()/2-0.43f*mRadius,getWidth()/2+0.006f*mRadius,getHeight()/2-0.40f*mRadius);
mMinutePath.lineTo(getWidth()/2+0.012f*mRadius,getHeight()/2);
mMinutePath.close();
mCanvas.drawPath(mMinutePath,mMinutePaint);
mCanvas.drawCircle(getWidth()/2,getHeight()/2,0.03f*mRadius,mMinutePaint);
mCanvas.drawCircle(getWidth()/2,getHeight()/2,0.015f*mRadius,mCircleMinPaint);
mCanvas.restore();
}
上我的measure的方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getMeasure(widthMeasureSpec), getMeasure(heightMeasureSpec));
}
private int getMeasure(int measureSpec) {
int result;
int mode = MeasureSpec.getMode(measureSpec);
int size = MeasureSpec.getSize(measureSpec);
if (mode == MeasureSpec.EXACTLY) {
result = size;
} else {
result = 800;
if (mode == MeasureSpec.AT_MOST) {
result = Math.min(result, size);
}
}
return result;
}
这个时候开始做3D效果的旋转 ,重写ontouch来实现
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (valueAnimator!=null&&valueAnimator.isRunning()){//注意加上判断否者会触瓶会卡顿的哦
valueAnimator.cancel();
}
getEvent(event);
break;
case MotionEvent.ACTION_MOVE:
getEvent(event);
break;
}
return true;
}
private void getEvent(MotionEvent event) {
//获取x,y轴的坐标点
float x = event.getX();
float y = event.getY();
float rotateX = -(y - getWidth() / 2);
float rotateY = (x - getHeight() / 2);
//计算比例
float[] scale = getScale(rotateX, rotateY);
//计算偏移量
rotX = scale[0] * mMaxTranslateRotate;
rotY = scale[1] * mMaxTranslateRotate;
}
//
private float[] getScale(float x, float y) {
float[] scale = new float[2];
float rotateX = x / mRadius;
float rotateY = y / mRadius;
if (rotateX > 1)
rotateX = 1;
else if (rotateX < -1)
rotateX = -1;
if (rotateY > 1)
rotateY = 1;
else if (rotateY < -1)
rotateY = -1;
scale[0] = rotateX;
scale[1] = rotateY;
return scale;
}
这里只是,做了旋转,旋转之后做一个动画回到原来的位置
case MotionEvent.ACTION_UP:
stratAnim();
break;
private void stratAnim() {
final String cameraRotateXName = "cameraRotateX";
final String cameraRotateYName = "cameraRotateY";
PropertyValuesHolder rotateXHoler = PropertyValuesHolder.ofFloat(cameraRotateXName, rotX, 0);
PropertyValuesHolder rotateYHolder = PropertyValuesHolder.ofFloat(cameraRotateYName, rotY, 0);
valueAnimator = ValueAnimator.ofPropertyValuesHolder(rotateXHoler, rotateYHolder);
valueAnimator.setDuration(800);
valueAnimator.setInterpolator(new TimeInterpolator() {
@Override
public float getInterpolation(float input) {
float f = 0.571429f;
return (float) (Math.pow(2, -2 * input) * Math.sin((input - f / 4) * (2 * Math.PI) / f) + 1);
}
});
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
rotX = (float) animation.getAnimatedValue(cameraRotateXName);
rotY = (float) animation.getAnimatedValue(cameraRotateYName);
}
});
valueAnimator.start();
}