TimePickerView.kt 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. package com.develop.common.widget
  2. import android.annotation.SuppressLint
  3. import android.content.Context
  4. import android.graphics.Color
  5. import android.util.AttributeSet
  6. import android.util.Log
  7. import android.view.View
  8. import android.view.animation.AlphaAnimation
  9. import android.view.animation.Animation
  10. import android.widget.TextView
  11. import androidx.constraintlayout.widget.ConstraintLayout
  12. import androidx.recyclerview.widget.LinearSnapHelper
  13. import com.develop.base.ext.isBrand062
  14. import com.develop.base.ext.updateText
  15. import com.develop.common.R
  16. import com.develop.common.databinding.TimePickerViewBinding
  17. import com.drake.brv.utils.models
  18. import com.drake.brv.utils.setup
  19. class TimePickerView : ConstraintLayout {
  20. private var binding: TimePickerViewBinding
  21. private var timeColor = Color.WHITE
  22. private val firstData = mutableListOf<String>()
  23. private val secondData = mutableListOf<String>()
  24. private var firstTimePos = 0
  25. private var secondTimePos = 0
  26. private var currentTotalSecond = 0
  27. private var maxTime = 8 * 60 * 60
  28. private var minTime = 0
  29. private var firstUnit: String? = null
  30. private var secondUnit: String? = null
  31. private var is062 = false
  32. var onTimePickerCallback: OnTimePickerCallback? = null
  33. private var firstManager: ScrollLinearManager
  34. private var secondManager: ScrollLinearManager
  35. private val animation: Animation = AlphaAnimation(1f, 0.2f)
  36. private var isCanChange = true
  37. constructor(context: Context) : super(context) {}
  38. constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
  39. initAttrs(context, attrs)
  40. }
  41. constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
  42. context,
  43. attrs,
  44. defStyleAttr
  45. ) {
  46. initAttrs(context, attrs)
  47. }
  48. init {
  49. val root = View.inflate(context, R.layout.time_picker_view, this)
  50. binding = TimePickerViewBinding.bind(root)
  51. is062 = isBrand062()
  52. repeat(60) { idx ->
  53. if (idx < 10) {
  54. firstData.add("0$idx")
  55. secondData.add("0$idx")
  56. } else {
  57. firstData.add(idx.toString())
  58. secondData.add(idx.toString())
  59. }
  60. }
  61. repeat(99) { idx ->
  62. if (idx == 0) {
  63. return@repeat
  64. }
  65. if (idx < 10) {
  66. firstData.add("0$idx")
  67. } else {
  68. firstData.add(idx.toString())
  69. }
  70. }
  71. firstManager = ScrollLinearManager(context)
  72. secondManager = ScrollLinearManager(context)
  73. }
  74. @SuppressLint("ClickableViewAccessibility")
  75. private fun initAttrs(context: Context, attrs: AttributeSet?) {
  76. context.obtainStyledAttributes(attrs, R.styleable.TimePickerView).apply {
  77. timeColor = getColor(R.styleable.TimePickerView_time_color, Color.WHITE)
  78. }.recycle()
  79. if (is062) timeColor = Color.WHITE
  80. binding.tvDotTime.setTextColor(timeColor)
  81. binding.tvFirstBottom.setTextColor(timeColor)
  82. binding.tvSecondBottom.setTextColor(timeColor)
  83. binding.tvSetTimeFirst.apply {
  84. layoutManager = firstManager
  85. setup {
  86. addType<String>(R.layout.item_cook_time)
  87. onBind {
  88. val tvTime = findView<TextView>(R.id.tv_time)
  89. tvTime.updateText(getModel())
  90. tvTime.setTextColor(timeColor)
  91. }
  92. }
  93. }.models = firstData
  94. binding.tvSetTimeSecond.apply {
  95. layoutManager = secondManager
  96. setup {
  97. addType<String>(R.layout.item_cook_time)
  98. onBind {
  99. val tvTime = findView<TextView>(R.id.tv_time)
  100. tvTime.updateText(getModel())
  101. tvTime.setTextColor(timeColor)
  102. }
  103. }
  104. }.models = secondData
  105. binding.tvSetTimeFirst.addOnScrollListener(LinearPositionObserver { pos ->
  106. firstTimePos = pos
  107. checkChangeTimeScale(pos)
  108. updateDisplayTime()
  109. })
  110. binding.tvSetTimeFirst.setOnTouchListener { view, motionEvent ->
  111. if (isCanChange){
  112. stopAlphaAnim()
  113. alphaAnim(binding.tvSetTimeFirst)
  114. onTimePickerCallback?.onTimePickerTouchFirst(1)
  115. }
  116. false
  117. }
  118. binding.tvSetTimeSecond.addOnScrollListener(LinearPositionObserver { pos ->
  119. secondTimePos = pos
  120. updateDisplayTime()
  121. })
  122. binding.tvSetTimeSecond.setOnTouchListener { view, motionEvent ->
  123. if (isCanChange){
  124. stopAlphaAnim()
  125. alphaAnim(binding.tvSetTimeSecond)
  126. onTimePickerCallback?.onTimePickerTouchFirst(2)
  127. }
  128. false
  129. }
  130. LinearSnapHelper().attachToRecyclerView(binding.tvSetTimeFirst)
  131. LinearSnapHelper().attachToRecyclerView(binding.tvSetTimeSecond)
  132. }
  133. private fun alphaAnim(view: View) {
  134. animation.duration = 600
  135. animation.repeatCount = -1
  136. view.startAnimation(animation)
  137. }
  138. fun stopAlphaAnim() {
  139. binding.tvSetTimeFirst.clearAnimation()
  140. binding.tvSetTimeSecond.clearAnimation()
  141. }
  142. private fun checkChangeTimeScale(firstPos: Int) {
  143. val firstUnit: String
  144. val secondUnit: String
  145. if (firstPos >= 60) {
  146. firstUnit = resources.getString(R.string.mode_hour)
  147. secondUnit = resources.getString(R.string.mode_min)
  148. } else {
  149. firstUnit = resources.getString(R.string.mode_min)
  150. secondUnit = resources.getString(R.string.mode_sec)
  151. }
  152. if (firstUnit != binding.tvFirstBottom.text) {
  153. binding.tvFirstBottom.text = firstUnit
  154. }
  155. if (secondUnit != binding.tvSecondBottom.text) {
  156. binding.tvSecondBottom.text = secondUnit
  157. }
  158. this.firstUnit = firstUnit;
  159. this.secondUnit = secondUnit;
  160. }
  161. private fun updateDisplayTime() {
  162. checkLimitTime(true)
  163. if (firstTimePos >= 60) {
  164. //处理时间 的 小时超出自动换最大值
  165. var maxhoursTime = maxTime / 3600
  166. var hoursTime = 0
  167. var secondTime = (maxTime - (maxhoursTime * 3600)) / 60
  168. if ((firstTimePos - 60 + 1) > maxhoursTime) {
  169. hoursTime = maxhoursTime
  170. } else {
  171. hoursTime = firstTimePos - 60 + 1
  172. if (secondTimePos < secondTime) {
  173. secondTime = secondTimePos
  174. }
  175. }
  176. setTimeInternal(
  177. hoursTime,
  178. secondTime,
  179. 0,
  180. true,
  181. setByUser = true
  182. )
  183. } else {
  184. setTimeInternal(
  185. 0,
  186. firstTimePos,
  187. secondTimePos,
  188. true,
  189. setByUser = true
  190. )
  191. }
  192. }
  193. @SuppressLint("SetTextI18n")
  194. fun setTimeInternal(
  195. hours: Int,
  196. minute: Int,
  197. second: Int,
  198. changeSetting: Boolean,
  199. setByUser: Boolean = false
  200. ) {
  201. val firstTime: Int
  202. val secondTime: Int
  203. if (hours > 0 || minute >= 60) {
  204. checkChangeTimeScale(80)
  205. firstTime = (hours + minute / 60)
  206. secondTime = (minute % 60)
  207. } else {
  208. checkChangeTimeScale(30)
  209. firstTime = minute
  210. secondTime = second
  211. }
  212. binding.let {
  213. if (changeSetting) {
  214. firstTimePos = if (hours > 0) {
  215. 59 + firstTime
  216. } else {
  217. firstTime
  218. }
  219. secondTimePos = secondTime
  220. it.tvSetTimeFirst.scrollToPosition(firstTimePos)
  221. it.tvSetTimeSecond.scrollToPosition(secondTimePos)
  222. }
  223. val firstStr = if (firstTime < 10) {
  224. "0$firstTime"
  225. } else {
  226. firstTime.toString()
  227. }
  228. val secondStr = if (secondTime < 10) {
  229. "0$secondTime"
  230. } else {
  231. secondTime.toString()
  232. }
  233. onTimePickerCallback?.onTimePicker(
  234. hours,
  235. minute,
  236. second,
  237. "${firstStr}:${secondStr}",
  238. setByUser
  239. )
  240. }
  241. }
  242. /**
  243. * 设置最大最小时间
  244. * 单位秒
  245. */
  246. fun setTimeRange(minTime: Int, maxTime: Int) {
  247. this.minTime = minTime
  248. this.maxTime = maxTime
  249. checkLimitTime()
  250. }
  251. private fun checkLimitTime(setByUser: Boolean = false) {
  252. val minMinutes = (minTime / 60 % 60)
  253. val minHours = (minTime / 3600)
  254. val minSeconds = minTime % 60
  255. val maxMinutes = (maxTime / 60 % 60)
  256. val maxHours = (maxTime / 3600)
  257. val maxSeconds = maxTime % 60
  258. val currentSeconds: Int = if (firstUnit === "hour") {
  259. val indexRe = firstData[firstTimePos]
  260. val hours = if (indexRe.startsWith("0")) {
  261. indexRe.substring(1, 2).toInt()
  262. } else {
  263. indexRe.toInt()
  264. }
  265. hours * 60 * 60 + secondTimePos * 60
  266. } else {
  267. firstTimePos * 60 + secondTimePos
  268. }
  269. if (currentSeconds < minTime) {
  270. setTimeInternal(
  271. minHours,
  272. minMinutes,
  273. minSeconds,
  274. changeSetting = true,
  275. setByUser = setByUser
  276. )
  277. } else if (currentSeconds > maxTime) {
  278. setTimeInternal(
  279. maxHours,
  280. maxMinutes,
  281. maxSeconds,
  282. changeSetting = true,
  283. setByUser = setByUser
  284. )
  285. }
  286. }
  287. fun setTargetTime(time: String) {
  288. binding.tvTargetTime.visibility = View.VISIBLE
  289. binding.tvTargetTime.text = time
  290. }
  291. fun hideTargetTime() {
  292. binding.tvTargetTime.visibility = View.INVISIBLE
  293. }
  294. fun isTimeCanChange(isCanChange: Boolean) {
  295. this.isCanChange = isCanChange
  296. binding.tvSetTimeFirst.isEnabled = !isCanChange
  297. binding.tvSetTimeSecond.isEnabled = !isCanChange
  298. firstManager.setCanScrollVertically(isCanChange)
  299. secondManager.setCanScrollVertically(isCanChange)
  300. }
  301. interface OnTimePickerCallback {
  302. fun onTimePicker(hours: Int, minute: Int, second: Int, time: String, setByUser: Boolean)
  303. fun onTimePickerTouchFirst(where: Int)
  304. }
  305. }