关于GAStudio哥玄酷的华为时钟界面代码学习
2018-04-02 14:58
405 查看
package gastudio.clock.ui;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
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.Path;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Xfermode;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import java.util.Calendar;
import gastudio.clock.R;
import gastudio.clock.utils.Constants;
import gastudio.clock.utils.UiUtils;
public class ClockViewByPath extends View {
//03.08:
// 1.Canvas是一个很虚幻的概念,相当于一个透明图层(用过PS的同学应该都知道),每次Canvas画图时(即调用Draw系列函数),
// 都会产生一个透明图层,然后在这个图层上画图,画完之后覆盖在屏幕上显示
// 2.在画源图像时,会把之前画布上所有的内容都做为目标图像
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Save layer
//mPaint.setColor(Color.RED);
//canvas.drawRect(mClockViewRectF.left+10,mClockViewRectF.top+10,mClockViewRectF.right+10,mClockViewRectF.bottom+10,mPaint);
int layerOne = canvas.saveLayer(mClockViewRectF, mPaint, Canvas.ALL_SAVE_FLAG);
//03.08saveLayer会创建一个全新透明的bitmap,大小与指定保存的区域一致,其后的绘图操作都放在这个bitmap上进行。
// 在绘制结束后,会直接盖在上一层的Bitmap上显示
// Draw clock scale lines
mPaint.setColor(mClockScaleLineColor);
float clockScaleLineStartY = mAdjustClockScaleLineStartX + mClockViewRectF.top;
float clockScaleLineEndY = clockScaleLineStartY + mClockScaleLineMaxHeight;
for (int i = 0; i < 120; i++) {//120
Log.i(“zyl log 0309”,”clockScaleLineStartY = ” + clockScaleLineStartY);
Log.i(“zyl log 0309”,”clockScaleLineEndY = ” + clockScaleLineEndY);
canvas.drawLine(mClockViewCenterX, clockScaleLineStartY,
mClockViewCenterX, clockScaleLineEndY, mPaint);
canvas.rotate(ANGLE_PER_SCALE, mClockViewCenterX, mClockViewCenterY);//3度;其实是画布所在的坐标系的旋转
}
//2017.03.03 canvas.rotate方法会将坐标轴旋转!!!!
mPaint.setXfermode(mXfermode);
canvas.rotate(mNowClockAngle - mClockMaskAdjustAngle, mClockViewCenterX, mClockViewCenterY);
// Generate a mask by path
int layerTwo = canvas.saveLayer(mClockViewRectF, mPaint, Canvas.ALL_SAVE_FLAG);
Log.i(“zyl log 02281”,”mClockViewRectF = ” + mClockViewRectF);//(0,0,520,520)
Xfermode zXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC); //add by zyl 2017.03.09
//mPaint.setXfermode(null);
mPaint.setXfermode(zXfermode);
//为什么需要重置一下呢?
//1.因为在调用saveLayer时,会生成了一个全新的bitmap,这个bitmap的大小就是我们指定的保存区域的大小,
// 新生成的bitmap是全透明的,注意是透明的在调用saveLayer后所有的绘图操作都是在这个bitmap上进行的
//2.在layerOne时,Paint的Xfermode为取下层绘制非交集部分,那么在drawOval和drawPath之后效果跟新建一张透明图层效果是一样的
canvas.drawOval(mClockViewRectF, mPaint);//外层大圆
//mPaint.setXfermode(mXfermode);//取下层绘制非交集部分
zXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT);//03.09 ??? 为什么缺省一块?
mPaint.setXfermode(zXfermode);
//mPaint.setColor(mClockPointColor);
canvas.drawPath(mClockMaskPath, mPaint);//mClockMaskPath==眼球
//03.09:关于mClockMaskPath路径的计算???
canvas.restoreToCount(layerTwo);
}
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
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.Path;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Xfermode;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import java.util.Calendar;
import gastudio.clock.R;
import gastudio.clock.utils.Constants;
import gastudio.clock.utils.UiUtils;
public class ClockViewByPath extends View {
private static final int DEFAULT_CLOCK_ANIMATION_DURATION = Constants.MINUTE; private static final int FULL_ANGLE = 360; private static final int SECOND_PER_MINUTE = 60; private static final int DEFAULT_TOTAL_CLOCK_SCALE_LINE_NUM = 120; private static final int DEFAULT_CLOCK_VIEW_WIDTH = 260; private static final int ANGLE_PER_SECOND = FULL_ANGLE / SECOND_PER_MINUTE; private static final int ANGLE_PER_SCALE = FULL_ANGLE / DEFAULT_TOTAL_CLOCK_SCALE_LINE_NUM; private static final int DEFAULT_CLOCK_SCALE_LINE_COLOR = Color.WHITE; private static final int DEFAULT_CLOCK_SCALE_LINE_WIDTH = 2; private static final int DEFAULT_CLOCK_SCALE_LINE_HEIGHT = 14; private static final int ADJUST_CLOCK_SCALE_LINE_START_X = 1; private static final int DEFAULT_DIGITAL_TIME_TEXT_COLOR = Color.WHITE; private static final int DEFAULT_DIGITAL_TIME_TEXT_SIZE = 60; private static final String DEFAULT_DEFAULT_DIGITAL_TIME_TEXT = "00:00"; private static final int DEFAULT_CLOCK_POINT_COLOR = Color.RED; private static final int DEFAULT_CLOCK_POINT_RADIUS = 6; private static final float[] CLOCK_SCALE_LINE_BASE_LEN_ARRAY = new float[]{ 1F, 1.1F, 1.21F, 1.32F, 1.452F, 1.551F, 1.6827F, 1.75F, 1.6827F, 1.551F, 1.452F, 1.32F, 1.21F, 1.1F, 1F}; // This is max in CLOCK_SCALE_LINE_BASE_LEN_ARRAY private static final float RATIO_OF_MAX_HEIGTH_TO_NORMAL_HEIGHT = 1.75F; private int mClockScaleLineWidth; private int mClockScaleLineHeight; private int mClockScaleLineMaxHeight; private int mClockScaleLineColor; private int mAdjustClockScaleLineStartX; private int mClockViewCenterX; private int mClockViewCenterY; private int mClockMaskRadius; private RectF mClockViewRectF; private Path mClockMaskPath; private float mClockMaskAdjustAngle; private int mClockPointRadius; private int mClockPointColor; private int mClockPointCenterX; private int mClockPointCenterY; private int mDigitalTimeTextStartX; private int mDigitalTimeTextStartY; private int mDigitalTimeTextSize; private int mDigitalTimeTextColor; private Rect mDigitalTimeTextRect; private String mLastDigitalTimeStr; private Paint mPaint; private Xfermode mXfermode; private ValueAnimator mClockAnimator; private float mNowClockAngle; private float mInitClockAngle; private long mLastTimeMillis; private Calendar mCalendar; public ClockViewByPath(Context context) { this(context, null); } public ClockViewByPath(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ClockViewByPath(Context context, AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } public ClockViewByPath(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.ClockView); mDigitalTimeTextSize = a.getDimensionPixelSize(R.styleable.ClockView_timeTextSize, UiUtils.dipToPx(context, DEFAULT_DIGITAL_TIME_TEXT_SIZE)); mDigitalTimeTextColor = a.getColor(R.styleable.ClockView_timeTextSize, DEFAULT_DIGITAL_TIME_TEXT_COLOR); mClockPointRadius = a.getDimensionPixelSize(R.styleable.ClockView_pointRadius, UiUtils.dipToPx(context, DEFAULT_CLOCK_POINT_RADIUS)); mClockPointColor = a.getColor(R.styleable.ClockView_pointColor, DEFAULT_CLOCK_POINT_COLOR); mClockScaleLineWidth = a.getDimensionPixelSize(R.styleable.ClockView_timeScaleWidth, UiUtils.dipToPx(context, DEFAULT_CLOCK_SCALE_LINE_WIDTH)); mClockScaleLineHeight = a.getDimensionPixelSize(R.styleable.ClockView_timeScaleHeight, UiUtils.dipToPx(context, DEFAULT_CLOCK_SCALE_LINE_HEIGHT)); mClockScaleLineMaxHeight = (int) (RATIO_OF_MAX_HEIGTH_TO_NORMAL_HEIGHT * mClockScaleLineHeight); mClockScaleLineColor = a.getDimensionPixelSize(R.styleable.ClockView_timeScaleColor, UiUtils.dipToPx(context, DEFAULT_CLOCK_SCALE_LINE_COLOR)); a.recycle(); init(); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, 4000 oldh); int len = w > h ? h : w; mClockViewRectF.set(0, 0, len, len); mClockViewRectF.offset((w - len) / 2, (h - len) / 2); mClockViewCenterX = (int) mClockViewRectF.centerX(); mClockViewCenterY = (int) mClockViewRectF.centerY(); mDigitalTimeTextStartX = mClockViewCenterX - mDigitalTimeTextRect.left - mDigitalTimeTextRect.width() / 2; mDigitalTimeTextStartY = mClockViewCenterY - mDigitalTimeTextRect.top - mDigitalTimeTextRect.height() / 2; mClockPointCenterX = mClockViewCenterX; mClockPointCenterY = (int) (mClockViewRectF.top + mAdjustClockScaleLineStartX + mClockScaleLineMaxHeight + mClockPointRadius * 2); mClockMaskRadius = (int) (mClockViewRectF.width() / 2 - mClockScaleLineMaxHeight); generateMaskPath(); mClockMaskAdjustAngle = (float) (CLOCK_SCALE_LINE_BASE_LEN_ARRAY.length + 1) / 2 * ANGLE_PER_SCALE; } private void generateMaskPath() { Log.i("zyl log 0223","generateMaskPath"); Log.i("zyl log 0223","mClockViewCenterX = " + mClockViewCenterX);//260 Log.i("zyl log 0223","mClockViewCenterY = " + mClockViewCenterY);//260 Log.i("zyl log 0223","mClockViewCenterY - mClockMaskRadius - mClockScaleLineHeight = " + (mClockViewCenterY - mClockMaskRadius - mClockScaleLineHeight)); Point point = new Point(mClockViewCenterX, mClockViewCenterY - mClockMaskRadius - mClockScaleLineHeight);//21 mClockMaskPath.moveTo(point.x, point.y); // Generate contour of the special clock scale lines int arrayLen = CLOCK_SCALE_LINE_BASE_LEN_ARRAY.length;//15 for (int index = 0; index < arrayLen; index++) { calculateNextPoint(point, CLOCK_SCALE_LINE_BASE_LEN_ARRAY[index], (float) Math.toRadians(ANGLE_PER_SCALE * (index + 1))); mClockMaskPath.lineTo(point.x, point.y); } // Generate contour of the normal clock scale lines int insertLen = mClockScaleLineMaxHeight - mClockScaleLineHeight; RectF cycleRectF = new RectF(mClockViewRectF); cycleRectF.inset(insertLen, insertLen);//03.10 ??? //If dx is positive, then the sides are moved inwards, making the rectangle narrower mClockMaskPath.arcTo(cycleRectF, arrayLen * ANGLE_PER_SCALE - 90,//-45(对于圆心找-45度方位,与矩形交点即为划线起始点) (DEFAULT_TOTAL_CLOCK_SCALE_LINE_NUM - arrayLen) * ANGLE_PER_SCALE);//315,弧线划过的角度(相对于圆心) } private void calculateNextPoint(Point point, float scale, float angle) { Log.i("zyl log 0228","mClockMaskRadius = " + mClockMaskRadius);//211 Log.i("zyl log 0228","mClockScaleLineHeight = " + mClockScaleLineHeight);//28 float originLen = mClockMaskRadius + mClockScaleLineHeight; float adjustLen = mClockMaskRadius + mClockScaleLineHeight * scale; Log.i("zyl log 0228","mClockViewCenterX + (int) (adjustLen * Math.sin(angle) = " + (mClockViewCenterX + (int) (adjustLen * Math.sin(angle) ) ) ) ; Log.i("zyl log 0228","mClockViewCenterY - mClockMaskRadius - mClockScaleLineHeight\n" + " + (int) (-adjustLen * Math.cos(angle) + originLen) = " + (mClockViewCenterY - mClockMaskRadius - mClockScaleLineHeight + (int) (-adjustLen * Math.cos(angle) + originLen))); point.set(mClockViewCenterX + (int) (adjustLen * Math.sin(angle)), mClockViewCenterY - mClockMaskRadius - mClockScaleLineHeight + (int) (-adjustLen * Math.cos(angle) + originLen)); } private void init() { mClockViewRectF = new RectF(); mDigitalTimeTextRect = new Rect(); mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setTextSize(mDigitalTimeTextSize); mPaint.getTextBounds(DEFAULT_DEFAULT_DIGITAL_TIME_TEXT, 0, DEFAULT_DEFAULT_DIGITAL_TIME_TEXT.length(), mDigitalTimeTextRect); mPaint.setStrokeWidth(mClockScaleLineWidth); mPaint.setStrokeCap(Paint.Cap.ROUND); mXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);//取下层绘制非交集部分(大小正方形中套圆) mAdjustClockScaleLineStartX = UiUtils.dipToPx(getContext(), ADJUST_CLOCK_SCALE_LINE_START_X); mClockMaskPath = new Path(); mCalendar = Calendar.getInstance(); mLastDigitalTimeStr = String.format("%02d:%02d", mCalendar.get(Calendar.HOUR), mCalendar.get(Calendar.MINUTE)); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); if (widthSpecMode != MeasureSpec.EXACTLY) { widthMeasureSpec = MeasureSpec.makeMeasureSpec( UiUtils.dipToPx(getContext(), DEFAULT_CLOCK_VIEW_WIDTH), MeasureSpec.EXACTLY); } if (heightSpecMode != MeasureSpec.EXACTLY) { heightMeasureSpec = MeasureSpec.makeMeasureSpec( UiUtils.dipToPx(getContext(), DEFAULT_CLOCK_VIEW_WIDTH), MeasureSpec.EXACTLY); } setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); }
//03.08:
// 1.Canvas是一个很虚幻的概念,相当于一个透明图层(用过PS的同学应该都知道),每次Canvas画图时(即调用Draw系列函数),
// 都会产生一个透明图层,然后在这个图层上画图,画完之后覆盖在屏幕上显示
// 2.在画源图像时,会把之前画布上所有的内容都做为目标图像
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Save layer
//mPaint.setColor(Color.RED);
//canvas.drawRect(mClockViewRectF.left+10,mClockViewRectF.top+10,mClockViewRectF.right+10,mClockViewRectF.bottom+10,mPaint);
int layerOne = canvas.saveLayer(mClockViewRectF, mPaint, Canvas.ALL_SAVE_FLAG);
//03.08saveLayer会创建一个全新透明的bitmap,大小与指定保存的区域一致,其后的绘图操作都放在这个bitmap上进行。
// 在绘制结束后,会直接盖在上一层的Bitmap上显示
// Draw clock scale lines
mPaint.setColor(mClockScaleLineColor);
float clockScaleLineStartY = mAdjustClockScaleLineStartX + mClockViewRectF.top;
float clockScaleLineEndY = clockScaleLineStartY + mClockScaleLineMaxHeight;
for (int i = 0; i < 120; i++) {//120
Log.i(“zyl log 0309”,”clockScaleLineStartY = ” + clockScaleLineStartY);
Log.i(“zyl log 0309”,”clockScaleLineEndY = ” + clockScaleLineEndY);
canvas.drawLine(mClockViewCenterX, clockScaleLineStartY,
mClockViewCenterX, clockScaleLineEndY, mPaint);
canvas.rotate(ANGLE_PER_SCALE, mClockViewCenterX, mClockViewCenterY);//3度;其实是画布所在的坐标系的旋转
}
//2017.03.03 canvas.rotate方法会将坐标轴旋转!!!!
mPaint.setXfermode(mXfermode);
canvas.rotate(mNowClockAngle - mClockMaskAdjustAngle, mClockViewCenterX, mClockViewCenterY);
// Generate a mask by path
int layerTwo = canvas.saveLayer(mClockViewRectF, mPaint, Canvas.ALL_SAVE_FLAG);
Log.i(“zyl log 02281”,”mClockViewRectF = ” + mClockViewRectF);//(0,0,520,520)
Xfermode zXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC); //add by zyl 2017.03.09
//mPaint.setXfermode(null);
mPaint.setXfermode(zXfermode);
//为什么需要重置一下呢?
//1.因为在调用saveLayer时,会生成了一个全新的bitmap,这个bitmap的大小就是我们指定的保存区域的大小,
// 新生成的bitmap是全透明的,注意是透明的在调用saveLayer后所有的绘图操作都是在这个bitmap上进行的
//2.在layerOne时,Paint的Xfermode为取下层绘制非交集部分,那么在drawOval和drawPath之后效果跟新建一张透明图层效果是一样的
canvas.drawOval(mClockViewRectF, mPaint);//外层大圆
//mPaint.setXfermode(mXfermode);//取下层绘制非交集部分
zXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT);//03.09 ??? 为什么缺省一块?
mPaint.setXfermode(zXfermode);
//mPaint.setColor(mClockPointColor);
canvas.drawPath(mClockMaskPath, mPaint);//mClockMaskPath==眼球
//03.09:关于mClockMaskPath路径的计算???
canvas.restoreToCount(layerTwo);
mPaint.setXfermode(null); // Draw clock point mPaint.setColor(mClockPointColor); canvas.rotate(mClockMaskAdjustAngle, mClockViewCenterX, mClockViewCenterY); canvas.drawCircle(mClockPointCenterX, mClockPointCenterY, mClockPointRadius, mPaint); canvas.restoreToCount(layerOne); updateTimeText(canvas); } private void updateTimeText(Canvas canvas) { long currentTimeMillis = System.currentTimeMillis(); if (currentTimeMillis - mLastTimeMillis >= Constants.MINUTE) { mLastTimeMillis = currentTimeMillis; mCalendar.setTimeInMillis(currentTimeMillis); mLastDigitalTimeStr = String.format("%02d:%02d", mCalendar.get(Calendar.HOUR), mCalendar.get(Calendar.MINUTE)); } mPaint.setColor(mDigitalTimeTextColor); canvas.drawText(mLastDigitalTimeStr, mDigitalTimeTextStartX, mDigitalTimeTextStartY, mPaint); } public void performAnimation() { cancelAnimation(); mClockAnimator = ValueAnimator.ofFloat(0, FULL_ANGLE); mClockAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mNowClockAngle = (float) animation.getAnimatedValue(); mNowClockAngle += mInitClockAngle; invalidate(); } }); mClockAnimator.setDuration(DEFAULT_CLOCK_ANIMATION_DURATION); mClockAnimator.setInterpolator(new LinearInterpolator()); mClockAnimator.setRepeatCount(Animation.INFINITE); mClockAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { super.onAnimationStart(animation); long currentTimeMillis = System.currentTimeMillis(); mCalendar.setTimeInMillis(currentTimeMillis); mLastDigitalTimeStr = String.format("%02d:%02d", mCalendar.get(Calendar.HOUR), mCalendar.get(Calendar.MINUTE)); mInitClockAngle = (mCalendar.get(Calendar.SECOND) + (float) mCalendar.get(Calendar.MILLISECOND) / Constants.SECOND) * ANGLE_PER_SECOND; mLastTimeMillis = currentTimeMillis - mCalendar.get(Calendar.SECOND) * Constants.SECOND - mCalendar.get(Calendar.MILLISECOND); } }); mClockAnimator.start(); } public void cancelAnimation() { if (mClockAnimator != null) { mClockAnimator.removeAllUpdateListeners(); mClockAnimator.removeAllListeners(); mClockAnimator.cancel(); mClockAnimator = null; } }
}
相关文章推荐
- 关于GAStudio哥玄酷的下载动画中代码一些个人的学习
- 关于设置Visaul Studio 2010 代码编辑界面背景的方法
- 一次强烈的对比_差距(关于电话界面的学习)在此一并表示给出此代码的那位朋友的感谢
- 今日学习笔记 关于S3C2440时钟设置的理解
- 关于更改Zend Studio/Eclipse代码风格主题的介绍
- 华为软件编程规范学习(十一)--代码测试、维护
- iOS学习 用代码实现界面
- 华为软件编程规范学习(十)--代码编辑、编译、审查
- 关于matlab深度学习工具箱中setup代码的注释
- OpenCv关于灰度积分图的SSE代码学习和改进。
- tiny6410裸机之代码重定位学习笔记(包含串口,时钟初始化)
- android studio 系统学习笔记(一)android studio界面介绍
- SSE图像算法优化系列六:OpenCv关于灰度积分图的SSE代码学习和改进。
- IOS开发使用纯代码开发界面_学习笔记
- 关于Python数据分析基础教程 Numpy学习指南 第二版 第二章中代码所呈现的问题
- 编写代码不能看!好好学习华为的编码规则!----项目经理
- 华为软件编程规范学习(十一)--代码测试、维护
- iPhone开发学习笔记001——Xib界面上的控件与代码的相互关联方法
- Unity学习笔记 之 关于 Unity UI 的 Slider 的代码记录
- 关于进入windows界面时,提示了:一个问题阻止windows正确检查此机器许可证,错误代码为: