目錄
前言
之前項目有個需求是在桌面上增加一個桌面時鐘,當時是參考別人的博客,在其基礎上添加了屬性動畫然後實現的。因此今天特地重新寫了一遍這個自定義view 加深印象並分享出來
效果圖
用手機拍的視頻,然後轉爲GIF的,因爲是試用的,所以就是這個渣渣效果,還有水印,求推薦一個好用的格式轉換工具
項目實現
爲了編寫方便,這裏沒有用到自定義屬性,有需要的話,可以自行添加自定義屬性,同時在代碼中註釋很詳細,文章這裏只簡單介紹一下
用到的屬性
private Paint hPaint ,mPaint ,sPaint ,circlePaint ,cPointPaint,paintDegree,txtPaint,titlePaint;
private final int maxWidth = 400;
int width ,height;
int hSound = 0;
int mSound = 0;
int sSound = 0;
private int hcount = 0;//當前小時數
private int mcount = 0;//當前分鐘數
private int scount = 0;//當前秒鐘數
private final int hScale = 30;//每小時之間30度
private static final int mScale = 6;//每分鐘之間是6度
RectF hRect , mRect ,sRect ;//三個
boolean first = true;//執行動畫之後再轉動指針
重寫三個構造方法
public ClockView(Context context) {
super(context);
init();
}
public ClockView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ClockView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
初始化各個指針的畫筆,指針的矩形
這裏各種new操作不要放在Ondraw方法中,避免重複實現
private void init(){
//外圓盤畫筆
circlePaint = new Paint();
circlePaint.setAntiAlias(true);
circlePaint.setDither(true);
circlePaint.setStyle(Paint.Style.STROKE);
circlePaint.setColor(Color.BLACK);
circlePaint.setStrokeWidth(1);
//圓心畫筆
cPointPaint = new Paint();
cPointPaint.setAntiAlias(true);
cPointPaint.setDither(true);
cPointPaint.setStyle(Paint.Style.FILL_AND_STROKE);
cPointPaint.setColor(0xff4CAF50);//綠色
cPointPaint.setStrokeWidth(2);
//時針畫筆
hPaint = new Paint();
hPaint.setAntiAlias(true);
hPaint.setDither(true);
hPaint.setStyle(Paint.Style.FILL_AND_STROKE);
hPaint.setColor(0xff349CE2);//藍色
hPaint.setStrokeWidth(7);
//時針矩形
hRect = new RectF(-16,0,89,0);
//分針畫筆
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setColor(0xffDB2540);//紅色
mPaint.setStrokeWidth(6);
//分針矩形
mRect = new RectF(-25,0,140,0);
//秒針畫筆
sPaint = new Paint();
sPaint.setAntiAlias(true);
sPaint.setDither(true);
sPaint.setStyle(Paint.Style.FILL_AND_STROKE);
sPaint.setColor(0xff4CAF50);//綠色
sPaint.setStrokeWidth(5);
//秒鐘矩形
sRect = new RectF(-30,0,180,0);
//刻度畫筆
paintDegree = new Paint();
paintDegree.setColor(0xff000000);
paintDegree.setStyle(Paint.Style.STROKE);
paintDegree.setAntiAlias(true);
//文字畫筆
txtPaint = new Paint();
txtPaint.setColor(0xff000000);
txtPaint.setTextSize(20);
txtPaint.setAntiAlias(true);
//標題畫筆
titlePaint = new Paint();
titlePaint.setColor(0xff000000);
titlePaint.setTextSize(45);
titlePaint.setAntiAlias(true);
}
重寫 onMeasure方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (widthMode == MeasureSpec.AT_MOST && heightMode ==MeasureSpec.AT_MOST){
setMeasuredDimension(maxWidth,maxWidth);
}else if (widthMode == MeasureSpec.AT_MOST ){
setMeasuredDimension(maxWidth,width);
}else if (heightMode ==MeasureSpec.AT_MOST ){
setMeasuredDimension(height,maxWidth);
}
}
獲取系統的時間
private void getDatas() {
SimpleDateFormat format = new SimpleDateFormat("HH,mm,ss");
String time = format.format(new Date());
try {
String s[] = time.split(",");
hcount = Integer.parseInt(s[0]);
mcount = Integer.parseInt(s[1]);
scount = Integer.parseInt(s[2]);
} catch (Exception ex) {
ex.printStackTrace();
}
}
重寫ondraw方法
首先獲取並計算寬高,設置各指針長度以及外圓的半徑
super.onDraw(canvas);
width = getMeasuredWidth()-getPaddingStart()-getPaddingEnd();
height = getMeasuredHeight()-getPaddingBottom()-getPaddingTop();
width = height = Math.min(width,height);
//圓心點
int cpoints = width/2;
hSound = width/8;//時針長度
mSound = width/6;//分針長度
sSound = width/4;//秒針長度
int radius =cpoints*3/4;//外圓半徑
借用一下別人的圖
這裏判斷是否是第一次顯示時鐘,等於true爲第一次,然後繪製標題,位置的計算根據上圖
if(!first){
getDatas();
}
String titleText = "三原色時鐘";
//X座標等於圓心的X座標減去文字的一半的長度,Y座標等於圓心的Y座標減圓的半徑再減20
canvas.drawText(titleText,cpoints - titlePaint.measureText(titleText) / 2 ,cpoints-radius-20,titlePaint);
然後繪製刻度,圓盤
//畫出12個小時的刻度線及文字
for (int i = 0; i < 12; i++) {
String txtTime = Integer.toString(i);
//3,6,9,12比其他的略粗略長
if(i%3==0) {
if (i==0){
txtTime = "12";
}
paintDegree.setStrokeWidth(5);
canvas.drawLine(width / 2, height / 2 - radius, width / 2, height / 2 - radius + 20, paintDegree);
}else{
paintDegree.setStrokeWidth(4);
canvas.drawLine(width / 2, height / 2 - radius, width / 2, height / 2 - radius + 15, paintDegree);
}
canvas.drawText(txtTime,cpoints - txtPaint.measureText(txtTime) / 2,height / 2 - radius + 40,txtPaint);
canvas.rotate(hScale, width / 2, height / 2);
}
//畫出60個分鐘的刻度線
for (int x = 0; x < 60; x++) {
paintDegree.setStrokeWidth(3);
if (x % 5 != 0) {//當x % 5 == 0時即是時鐘刻度,因此不需要繪製,避免重複繪製
canvas.drawLine(width / 2, height / 2 - radius, width / 2, height / 2 - radius + 8, paintDegree);
}
canvas.rotate(mScale, width / 2, height / 2);
}
//畫外層圓
canvas.drawCircle(cpoints,cpoints,radius,circlePaint);
//畫內層圓
canvas.drawCircle(cpoints,cpoints,radius/6,circlePaint);
//平移至中心點
canvas.translate(cpoints,cpoints);
//保存畫布
canvas.save();
最後是繪製三個指針
//int hRotate = 270 + hScale * hcount;
int offset = 30 * mcount / 60;
offset -= offset % mScale;//時針相對分針數,有一個偏移量
int hRotate = 270 + hScale * hcount + offset;
canvas.rotate(hRotate);
// canvas.drawLine(0, -10, 0, hSound, hPaint);//畫時針
canvas.drawRoundRect(hRect,15,15,hPaint);//畫時針
canvas.restore();
canvas.save();
int mRotate = 270 + mScale * mcount ;
canvas.rotate(mRotate);
canvas.drawRoundRect(mRect,25,25,mPaint);//畫分針
//canvas.drawLine(0, -15, 0, mSound, mPaint);//畫分針
canvas.restore();
canvas.save();
//一圈360度,總共60秒,因此時間每多一秒,度數加6
int sRotate = 270 + mScale * scount ;
canvas.rotate(sRotate);
//canvas.drawLine(0, -25, 0, sSound, sPaint);//畫秒針
canvas.drawRoundRect(sRect,15,15,sPaint);//畫秒針
繪製指針上的圓心點
canvas.drawCircle(0,0,6,cPointPaint);//畫圓心
第一次出現時不轉動指針
if(!first){
postInvalidateDelayed(1000);
}
最後給時鐘添加上屬性動畫
在View中添加一個公共方法給外部調用
public void startAnim(){
getDatas();
final ValueAnimator animatorh = ValueAnimator.ofInt(0,hcount>12?hcount-12:hcount);//大於十二點時減去12 避免轉兩圈
final ValueAnimator animatorm=ValueAnimator.ofInt(0,mcount);
final ValueAnimator animators=ValueAnimator.ofInt(0,scount);
//設置動畫時長
animatorh.setDuration(1500);
animatorm.setDuration(1500);
animators.setDuration(1500);
animatorh.setInterpolator(new LinearInterpolator());
animatorh.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
hcount = (int)animation.getAnimatedValue();
}
});
animatorh.start();
animatorm.setInterpolator(new LinearInterpolator());
animatorm.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mcount = (int)animation.getAnimatedValue();
}
});
animatorm.start();
animators.setInterpolator(new LinearInterpolator());
animators.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
scount = (int)animation.getAnimatedValue();
postInvalidate();//添加之後動畫纔會執行,不然看不到效果
}
});
animators.start();
//添加動畫完成時的監聽,在動畫完成之後開始指針的轉動
animators.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
first = false ;
postInvalidate();
}
});
}
有時不需要執行動畫的話,可以再添加一個公共方法直接設置first 爲false
public void SetFirstInit(Boolean ttt) {
first =ttt;
}
//然後在佈局添加自定義view即可
在Activity中使用ClockView並根據需求調用對應的方法
到這裏文章就結束了,覺得還算可以的,歡迎點贊啊