Android
主页 > 软件编程 > Android >

Android自定义View实现时钟功能

2022-09-14 | 佚名 | 点击:

实现效果

View分析

时钟主要分为五个部分:

1、中心点: 圆心位置
2、圆盘: 以中心点为圆心,drawCircle画个圆
3、刻度:

paint有个aip, setPathEffect可以根据path画特效, 那么刻度就可以根据圆的path画一个矩形path的特效, 并且这个api只会画特效, 不会画出圆.

1

2

3

4

5

6

7

8

/**

* shape: 特效的path, 这里传一个矩形

* advance: 两个特效path之间的间距, 即两个矩形的left间距

* phase: 特效起始位置的偏移

* style: 原始path拐弯的时候特效path的转换方式,这里用ROTATE跟着旋转即可

*/

PathDashPathEffect(Path shape, float advance, float phase,

                              Style style)

刻度又分两种, 粗一点刻度: 3、6、9、12, 和细一点的刻度. 两种特效又可以用SumPathEffect合起来画

1

SumPathEffect(PathEffect first, PathEffect second) 

4、时分秒指针

时分秒指针都是一个圆角矩形, 先把他们的位置计算出来, 然后旋转圆心去绘制不同角度的指针.

5、动画效果

TimerTask每隔一秒计算时间, 根据时间去换算当前时分秒指针的角度, 动态变量只有三个角度.

实现源码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

//

// Created by skylar on 2022/4/19.

//

class ClockView : View {

    private var mTimer: Timer? = null

    private val mCirclePaint: Paint = Paint()

    private val mPointerPaint: Paint = Paint()

    private val mTextPaint: Paint = Paint()

 

    private val mCirclePath: Path = Path()

    private val mHourPath: Path = Path()

    private val mMinutePath: Path = Path()

    private val mSecondPath: Path = Path()

 

    private lateinit var mPathMeasure: PathMeasure

    private lateinit var mSumPathEffect: SumPathEffect

 

    private var mViewWidth = 0

    private var mViewHeight = 0

    private var mCircleWidth = 6f

    private var mRadius = 0f

    private var mRectRadius = 20f

    private var mHoursDegree = 0f

    private var mMinutesDegree = 0f

    private var mSecondsDegree = 0f

    private var mCurrentTimeInSecond = 0L

 

    constructor(context: Context) : super(context)

 

    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)

 

    constructor(

        context: Context, attrs: AttributeSet?,

        defStyleAttr: Int

    ) : super(context, attrs, defStyleAttr)

 

    init {

        mCirclePaint.color = Color.BLACK

        mCirclePaint.isAntiAlias = true

        mCirclePaint.style = Paint.Style.STROKE

        mCirclePaint.strokeWidth = mCircleWidth

 

        mPointerPaint.color = Color.BLACK

        mPointerPaint.isAntiAlias = true

        mPointerPaint.style = Paint.Style.FILL

 

        mTextPaint.color = Color.BLACK

        mTextPaint.isAntiAlias = true

        mTextPaint.style = Paint.Style.FILL

        mTextPaint.textSize = 40f

    }

 

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {

        super.onSizeChanged(w, h, oldw, oldh)

        mViewWidth = measuredWidth - paddingLeft - paddingRight

        mViewHeight = measuredHeight - paddingTop - paddingBottom

 

        mRadius = mViewWidth / 2 - mCircleWidth

        mCirclePath.addCircle(0f, 0f, mRadius, Path.Direction.CW)

 

        mPathMeasure = PathMeasure(mCirclePath, false)

        val minutesShapePath = Path()

        val quarterShapePath = Path()

        minutesShapePath.addRect(0f, 0f, mRadius * 0.01f, mRadius * 0.06f, Path.Direction.CW)

        quarterShapePath.addRect(0f, 0f, mRadius * 0.02f, mRadius * 0.06f, Path.Direction.CW)

        val minutesDashPathEffect = PathDashPathEffect(

            minutesShapePath,

            mPathMeasure.length / 60,

            0f,

            PathDashPathEffect.Style.ROTATE

        )

        val quarterDashPathEffect = PathDashPathEffect(

            quarterShapePath,

            mPathMeasure.length / 12,

            0f,

            PathDashPathEffect.Style.ROTATE

        )

        mSumPathEffect = SumPathEffect(minutesDashPathEffect, quarterDashPathEffect)

 

        val hourPointerHeight = mRadius * 0.5f

        val hourPointerWidth = mRadius * 0.07f

        val hourRect = RectF(

            -hourPointerWidth / 2,

            -hourPointerHeight * 0.7f,

            hourPointerWidth / 2,

            hourPointerHeight * 0.3f

        )

        mHourPath.addRoundRect(hourRect, mRectRadius, mRectRadius, Path.Direction.CW)

 

        val minutePointerHeight = mRadius * 0.7f

        val minutePointerWidth = mRadius * 0.05f

        val minuteRect = RectF(

            -minutePointerWidth / 2,

            -minutePointerHeight * 0.8f,

            minutePointerWidth / 2,

            minutePointerHeight * 0.2f

        )

        mMinutePath.addRoundRect(minuteRect, mRectRadius, mRectRadius, Path.Direction.CW)

 

        val secondPointerHeight = mRadius * 0.9f

        val secondPointerWidth = mRadius * 0.03f

        val secondRect = RectF(

            -secondPointerWidth / 2,

            -secondPointerHeight * 0.8f,

            secondPointerWidth / 2,

            secondPointerHeight * 0.2f

        )

        mSecondPath.addRoundRect(secondRect, mRectRadius, mRectRadius, Path.Direction.CW)

 

        startAnimator()

    }

 

    override fun onDraw(canvas: Canvas?) {

        super.onDraw(canvas)

        if (canvas == null) {

            return

        }

        canvas.translate((mViewWidth / 2).toFloat(), (mViewHeight / 2).toFloat())

 

        //画圆盘

        mCirclePaint.pathEffect = null

        canvas.drawPath(mCirclePath, mCirclePaint)

 

        //画刻度

        mCirclePaint.pathEffect = mSumPathEffect

        canvas.drawPath(mCirclePath, mCirclePaint)

 

        //时分秒指针

        mPointerPaint.color = Color.BLACK

 

        canvas.save()

        canvas.rotate(mHoursDegree)

        canvas.drawPath(mHourPath, mPointerPaint)

        canvas.restore()

 

        canvas.save()

        canvas.rotate(mMinutesDegree)

        canvas.drawPath(mMinutePath, mPointerPaint)

        canvas.restore()

 

        canvas.save()

        canvas.rotate(mSecondsDegree)

        canvas.drawPath(mSecondPath, mPointerPaint)

        canvas.restore()

 

        //画中心点

        mPointerPaint.color = Color.WHITE

        canvas.drawCircle(0f, 0f, mRadius * 0.02f, mPointerPaint)

    }

 

 

    private fun startAnimator() {

        val cal = Calendar.getInstance()

        val hour = cal.get(Calendar.HOUR)  //小时

        val minute = cal.get(Calendar.MINUTE)  //分

        val second = cal.get(Calendar.SECOND)  //秒

        mCurrentTimeInSecond = (hour * 60 * 60 + minute * 60 + second).toLong()

 

        if (mTimer == null) {

            mTimer = Timer()

        } else {

            mTimer?.cancel()

            mTimerTask.cancel()

        }

        mTimer?.schedule(mTimerTask, 0, 1000)

    }

 

    private var mTimerTask: TimerTask = object : TimerTask() {

        override fun run() {

            mCurrentTimeInSecond++

            computeDegree()

            invalidate()

        }

    }

 

    //12小时 00:00:00 ~ 12:00:00

    private fun computeDegree() {

        val secondsInOneRoll = 12 * 60 * 60

        val currentSeconds = mCurrentTimeInSecond % secondsInOneRoll

 

        var leftSeconds = currentSeconds

        val hours = currentSeconds / 60 / 60

        leftSeconds = currentSeconds - hours * 60 * 60

        val minutes = leftSeconds / 60

        leftSeconds -= minutes * 60

        val seconds = leftSeconds % 60

 

        mHoursDegree = hours * 30f

        mMinutesDegree = minutes * 6f

        mSecondsDegree = seconds * 6f

 

    }

}

原文链接:https://blog.csdn.net/qq_29125669/article/details/124452399
相关文章
最新更新