ps:简约圆盘指针时钟,颜色可调、自由搭配;支持阿拉伯数字、罗马数字刻度显示;
 
< com.nepalese.harinetest.player.VirgoCircleClockandroid: id= " @+id/circleclock" android: layout_width= " 300dp" android: layout_height= " 300dp" app: paddingFrame= " 10dp" app: strokeSize= " 5dp" app: offsetMark= " -1dp" app: offsetText= " -1dp" app: rSmall= " 5px" app: rBig= " 8px" app: needBg= " true" app: frameColor= " @color/colorBlack30" app: bgColor= " @color/colorEye" app: markColor1= " @color/black" app: markColor2= " @color/colorWhite" app: textColor= " @color/black" app: txtSize= " 18sp" app: displayType= " type_num" /> private  VirgoCircleClock  circleClock; 
circleClock =  findViewById ( R . id. circleclock) ; 
circleClock. startPlay ( ) ; 
if  ( circleClock !=  null )  { circleClock. releaseView ( ) ; 
} 
1. attrs.xml <?xml version="1.0" encoding="utf-8"?> 
< resources> < declare-styleablename = " VirgoCircleClock" > < attrname = " needBg" format = " boolean" /> < attrname = " bgColor" format = " color|reference" /> < attrname = " frameColor" format = " color|reference" /> < attrname = " markColor1" format = " color|reference" /> < attrname = " markColor2" format = " color|reference" /> < attrname = " textColor" format = " color|reference" /> < attrname = " secondColor" format = " color|reference" /> < attrname = " rBig" format = " dimension|reference" /> < attrname = " rSmall" format = " dimension|reference" /> < attrname = " offsetMark" format = " dimension|reference" /> < attrname = " offsetText" format = " dimension|reference" /> < attrname = " displayType" format = " integer" > < enumname = " type_num" value = " 1" /> < enumname = " type_roma" value = " 2" /> </ attr> < attrname = " paddingFrame" format = " dimension|reference" /> < attrname = " txtSize" format = " dimension|reference" /> < attrname = " strokeSize" format = " dimension|reference" /> </ declare-styleable> </ resources> 2. VirgoCircleClock.java package  com. nepalese. harinetest. player ; import  android. content.  Context ; 
import  android. content. res.  TypedArray ; 
import  android. graphics.  Canvas ; 
import  android. graphics.  Color ; 
import  android. graphics.  Paint ; 
import  android. graphics.  Rect ; 
import  android. os.  Handler ; 
import  android. os.  Message ; 
import  android. util.  AttributeSet ; 
import  android. util.  Log ; 
import  android. view.  View ; import  androidx. annotation.  Nullable ; import  com. nepalese. harinetest.  R ; import  java. util.  Calendar ; public  class  VirgoCircleClock  extends  View  { private  static  final  String  TAG =  "VirgoCircleClock" ; private  static  final  int  DEF_RADIUS_BIG =  3 ; private  static  final  int  DEF_RADIUS_SMALL =  2 ; private  static  final  int  DEF_OFF_MARK =  2 ; private  static  final  int  DEF_OFF_TEXT =  3 ; private  static  final  int  TYPE_NUM =  1 ; private  static  final  int  TYPE_ROMA =  2 ; private  static  final  int  DEF_PADDING =  15 ; private  static  final  float  DEF_SIZE_TEXT =  18f ; private  static  final  float  DEF_FRAME_STROKE =  5f ; private  static  final  float  RATE_HOUR =  0.5f ; private  static  final  float  RATE_HOUR_TAIL =  0.05f ; private  static  final  float  RATE_HOUR_WIDTH =  70f ; private  static  final  float  RATE_MINUTE =  0.6f ; private  static  final  float  RATE_MINUTE_TAIL =  0.08f ; private  static  final  float  RATE_MINUTE_WIDTH =  120f ; private  static  final  float  RATE_SECOND =  0.7f ; private  static  final  float  RATE_SECOND_TAIL =  0.1f ; private  static  final  float  RATE_SECOND_WIDTH =  240f ; private  Paint  paintMain; private  Paint  paintFrame; private  Calendar  calendar; private  boolean  needBg; private  int  bgColor; private  int  frameColor; private  int  markColor1; private  int  markColor2; private  int  textColor; private  int  secondColor; private  int  radiusBig,  radiusSmall; private  int  offMark,  offText; private  int  markY,  txtY; private  int  displayStyle; private  int  padding; private  int  radius; private  int  hours,  minutes,  seconds; private  int  centerX,  centerY; private  float  textSize; private  float  strokeSize; public  VirgoCircleClock ( Context  context)  { this ( context,  null ) ; } public  VirgoCircleClock ( Context  context,  @Nullable  AttributeSet  attrs)  { this ( context,  attrs,  0 ) ; } public  VirgoCircleClock ( Context  context,  @Nullable  AttributeSet  attrs,  int  defStyleAttr)  { super ( context,  attrs,  defStyleAttr) ; init ( context,  attrs) ; } private  void  init ( Context  context,  AttributeSet  attrs)  { TypedArray  typedArray =  context. obtainStyledAttributes ( attrs,  R . styleable. VirgoCircleClock) ; needBg =  typedArray. getBoolean ( R . styleable. VirgoCircleClock_needBg,  false ) ; bgColor =  typedArray. getColor ( R . styleable. VirgoCircleClock_bgColor,  Color . WHITE) ; frameColor =  typedArray. getColor ( R . styleable. VirgoCircleClock_frameColor,  Color . BLACK) ; markColor1 =  typedArray. getColor ( R . styleable. VirgoCircleClock_markColor1,  Color . BLACK) ; markColor2 =  typedArray. getColor ( R . styleable. VirgoCircleClock_markColor2,  Color . DKGRAY) ; textColor =  typedArray. getColor ( R . styleable. VirgoCircleClock_textColor,  Color . BLACK) ; secondColor =  typedArray. getColor ( R . styleable. VirgoCircleClock_secondColor,  Color . RED) ; radiusBig =  typedArray. getDimensionPixelSize ( R . styleable. VirgoCircleClock_rBig,  DEF_RADIUS_BIG) ; radiusSmall =  typedArray. getDimensionPixelSize ( R . styleable. VirgoCircleClock_rSmall,  DEF_RADIUS_SMALL) ; offMark =  typedArray. getDimensionPixelSize ( R . styleable. VirgoCircleClock_offsetMark,  DEF_OFF_MARK) ; offText =  typedArray. getDimensionPixelSize ( R . styleable. VirgoCircleClock_offsetText,  DEF_OFF_TEXT) ; displayStyle =  typedArray. getInteger ( R . styleable. VirgoCircleClock_displayType,  TYPE_NUM) ; textSize =  typedArray. getDimension ( R . styleable. VirgoCircleClock_txtSize,  DEF_SIZE_TEXT) ; strokeSize =  typedArray. getDimension ( R . styleable. VirgoCircleClock_strokeSize,  DEF_FRAME_STROKE) ; padding =  typedArray. getDimensionPixelSize ( R . styleable. VirgoCircleClock_paddingFrame,  DEF_PADDING) ; initData ( ) ; } public  void  initLayout ( int  width,  int  height)  { Log . d ( TAG,  "initLayout: "  +  width +  " - "  +  height) ; int  diameter =  Math . min ( width,  height) ; radius =  ( int )  ( ( diameter -  padding -  strokeSize)  /  2 ) ; centerX =  diameter /  2 ; centerY =  diameter /  2 ; markY =  ( int )  ( padding +  strokeSize +  offMark) ; txtY =  markY +  radiusBig *  2  +  offText; } private  void  initData ( )  { paintMain =  new  Paint ( ) ; paintMain. setAntiAlias ( true ) ; paintMain. setStyle ( Paint. Style . FILL) ; paintFrame =  new  Paint ( ) ; paintFrame. setAntiAlias ( true ) ; paintFrame. setStyle ( Paint. Style . STROKE) ; paintFrame. setStrokeWidth ( strokeSize) ; calendar =  Calendar . getInstance ( ) ; } @Override protected  void  onSizeChanged ( int  w,  int  h,  int  oldw,  int  oldh)  { super . onSizeChanged ( w,  h,  oldw,  oldh) ; if  ( w >  0  &&  h >  0 )  { initLayout ( w,  h) ; } } @Override protected  void  onDraw ( Canvas  canvas)  { super . onDraw ( canvas) ; if  ( radius <  1 )  { return ; } getTimes ( ) ; drawPlate ( canvas) ; drawPointHour ( canvas) ; drawPointMinutes ( canvas) ; drawPointSeconds ( canvas) ; } private  void  getTimes ( )  { calendar. setTimeInMillis ( System . currentTimeMillis ( ) ) ; hours =  calendar. get ( Calendar . HOUR_OF_DAY) ; minutes =  calendar. get ( Calendar . MINUTE) ; seconds =  calendar. get ( Calendar . SECOND) ; } private  void  drawPlate ( Canvas  canvas)  { if ( needBg) { paintMain. setColor ( bgColor) ; canvas. drawCircle ( centerX,  centerY,  radius,  paintMain) ; } paintFrame. setColor ( frameColor) ; canvas. drawCircle ( centerX,  centerY,  radius,  paintFrame) ; canvas. save ( ) ; for  ( int  i =  0 ;  i <  60 ;  i++ )  { if  ( i %  5  ==  0 )  { paintMain. setColor ( markColor1) ; canvas. drawCircle ( centerX -  radiusBig /  2f ,  markY,  radiusBig,  paintMain) ; }  else  { paintMain. setColor ( markColor2) ; canvas. drawCircle ( centerX -  radiusSmall /  2f ,  markY,  radiusSmall,  paintMain) ; } canvas. rotate ( 6 ,  centerX,  centerY) ; } canvas. restore ( ) ; paintMain. setColor ( textColor) ; paintMain. setTextSize ( textSize) ; for  ( int  i =  1 ;  i <=  12 ;  i++ )  { double  radians =  Math . toRadians ( 30  *  i) ;  String  hourText; if  ( displayStyle ==  TYPE_ROMA)  { hourText =  getHoursGreece ( i) ; }  else  { hourText =  String . valueOf ( i) ; } Rect  rect =  new  Rect ( ) ;  paintMain. getTextBounds ( hourText,  0 ,  hourText. length ( ) ,  rect) ; int  textWidth =  rect. width ( ) ; int  textHeight =  rect. height ( ) ; canvas. drawText ( hourText, ( float )  ( centerX +  ( radius -  txtY)  *  Math . sin ( radians)  -  textWidth /  2 ) , ( float )  ( centerY -  ( radius -  txtY)  *  Math . cos ( radians)  +  textHeight /  2 ) , paintMain) ;  } } private  void  drawPointHour ( Canvas  canvas)  { drawPoint ( canvas,  360  /  12  *  hours +  ( 30  *  minutes /  60 ) ,  RATE_HOUR,  RATE_HOUR_TAIL,  RATE_HOUR_WIDTH) ; } private  void  drawPointMinutes ( Canvas  canvas)  { drawPoint ( canvas,  360  /  60  *  minutes +  ( 6  *  seconds /  60 ) ,  RATE_MINUTE,  RATE_MINUTE_TAIL,  RATE_MINUTE_WIDTH) ; } private  void  drawPointSeconds ( Canvas  canvas)  { paintMain. setColor ( secondColor) ; drawPoint ( canvas,  360  /  60  *  seconds,  RATE_SECOND,  RATE_SECOND_TAIL,  RATE_SECOND_WIDTH) ; } private  void  drawPoint ( Canvas  canvas,  int  degree,  float  rateLen,  float  rateTail,  float  rateWidth)  { double  radians =  Math . toRadians ( degree) ; int  endX =  ( int )  ( centerX +  radius *  Math . cos ( radians)  *  rateLen) ;  int  endY =  ( int )  ( centerY +  radius *  Math . sin ( radians)  *  rateLen) ;  canvas. save ( ) ; paintMain. setStrokeWidth ( radius /  rateWidth) ; canvas. rotate ( ( - 90 ) ,  centerX,  centerY) ;  canvas. drawLine ( centerX,  centerY,  endX,  endY,  paintMain) ;  radians =  Math . toRadians ( degree -  180 ) ;  endX =  ( int )  ( centerX +  radius *  Math . cos ( radians)  *  rateTail) ; endY =  ( int )  ( centerY +  radius *  Math . sin ( radians)  *  rateTail) ; canvas. drawLine ( centerX,  centerY,  endX,  endY,  paintMain) ; canvas. restore ( ) ; } private  String  getHoursGreece ( int  i)  { switch  ( i)  { case  1 : return  "I" ; case  2 : return  "II" ; case  3 : return  "III" ; case  4 : return  "IV" ; case  5 : return  "V" ; case  6 : return  "VI" ; case  7 : return  "VII" ; case  8 : return  "VIII" ; case  9 : return  "IX" ; case  10 : return  "X" ; case  11 : return  "XI" ; case  12 : default : return  "XII" ; } } private  final  int  MSG_UPDATE_TIME =  1 ; private  final  Handler  handler =  new  Handler ( new  Handler. Callback ( )  { @Override public  boolean  handleMessage ( Message  msg)  { if  ( msg. what ==  MSG_UPDATE_TIME)  { invalidate ( ) ; startTask ( ) ; } return  false ; } } ) ; private  void  startTask ( )  { handler. removeMessages ( MSG_UPDATE_TIME) ; handler. sendEmptyMessageDelayed ( MSG_UPDATE_TIME,  1000L ) ; } private  void  pauseTask ( )  { removeMsg ( ) ; } private  void  stopTask ( )  { removeMsg ( ) ; } private  void  removeMsg ( )  { handler. removeMessages ( MSG_UPDATE_TIME) ; } public  void  startPlay ( )  { startTask ( ) ; } public  void  pausePlay ( )  { pauseTask ( ) ; } public  void  continuePlay ( )  { startTask ( ) ; } public  void  releaseView ( )  { stopTask ( ) ; } 
}