Reputation: 1
Click here to see screenshot ROTA chart
As shown in attached screenshot I am setting bars based on time line and managing their top using below calculation here I am facing one issue which is As you can see in Red color bar My time difference comes 33 minutes then it should be drawn from slight below from center line which is between 11:00 and 12:00, Same as for Blue color bar The time difference is 43 minutes so it should be drawn from below of Center line which is between 15:00 and 16:00 but it has drawn from 16:00 which is wrong so can anyone provide me solution for the same?, Below is code for whole adapter please let me know my mistake.
NOTE : Whole code is for what i implemented in whole adapter just facing issue of getting margin based on time diffrence, I want to give margin top and bottom based on time difference For example My appointment time is 11:00 But I have started appointment at 11:30 then my Bars should apply top and bottom margin based on difference of 33 mins all code for calculating difference works well but i am not getting accurate margin, Also for information
class RotaChartAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
var shiftTimeList = arrayListOf<RotaTimeListResponse>()
private val mChartDataList: ArrayList<RotaHolderTimeData> = CommonDataProvider.getDaysHours24()
private var mainAdapterPosition: Int = -1
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return ViewHolder(
RotaRowItemChartBinding.inflate(
LayoutInflater.from(parent.context), parent, false
)
)
}
override fun getItemCount(): Int {
return 24
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
(holder as ViewHolder).apply {
binding()
manageRedBlock()
manageGreenBlock()
}
}
private fun String?.extractHourOnly(): Int {
return this?.getLocalTime()?.substring(0, 2)?.toIntOrNull() ?: 0
}
private fun getBetween(startTime: String, endTime: String): Long {
val timeFormatter = DateTimeFormatter.ofPattern("HH:mm")
val start = LocalTime.parse(startTime, timeFormatter)
val end = LocalTime.parse(endTime, timeFormatter)
return abs(Duration.between(start, end).toMinutes())
}
private fun String?.getLocalTime(): String? {
return this?.getUTCToLocalLong()?.formatDate("HH:mm")
}
private fun String?.getAccurateEnd(): Int {
var endHour = this?.getLocalTime()?.substring(0, 2)?.toIntOrNull() ?: 0
val endMinutes = this?.getLocalTime()?.substring(3)?.toIntOrNull() ?: 0
if (endMinutes > 0) {
endHour += 1
}
return endHour
}
inner class ViewHolder(private val binding: RotaRowItemChartBinding) :
RecyclerView.ViewHolder(binding.root) {
fun binding() = with(binding) {
setHeightDynamic(root, blockBlue, blockGreen, blockRed)
if (mainAdapterPosition == 0) {
viewGrayLine.setStartMargin(R.dimen.dp_12)
}
viewBlueBottom.isVisible = adapterPosition == (itemCount - 1)
manageView()
ivProfile.gone()
}
private fun manageView() = with(binding) {
blockBlue.isInvisible = true
blockRed.isInvisible = true
blockGreen.isInvisible = true
}
fun manageRedBlock() = with(binding) {
shiftTimeList.forEach terminateLoop@{
val data = mChartDataList[adapterPosition]
if (!it.startTime.isNullOrEmpty() && !it.endTime.isNullOrEmpty()) {
if (it.appointmentStatus == StatusEnum.MISSED.value) {
if (areTimesWithinRange(
startTime = data.fixedStartTime,
endTime = data.fixedEndTime,
betweenStartTime = it.startTime.getLocalTime().toString(),
betweenEndTime = it.endTime.getLocalTime().toString(),
position = adapterPosition
)
) {
blockRed.post {
blockRed.isVisible = true
handleStartTime(
startTime = it.startTime,
fixedStartTime = data.fixedStartTime,
block = blockRed
)
handleEndTime(
endTime = it.endTime,
fixedEndTime = data.fixedEndTime,
block = blockRed
)
}
return@terminateLoop
}
}
}
}
}
fun manageGreenBlock() = with(binding) {
shiftTimeList.forEach terminateLoop@{
val data = mChartDataList[adapterPosition]
if (!it.shiftStartTime.isNullOrEmpty() && !it.shiftEndTime.isNullOrEmpty()) {
if (it.appointmentStatus == StatusEnum.COMPLETED.value) {
if (areTimesWithinRange(
startTime = data.fixedStartTime,
endTime = data.fixedEndTime,
betweenStartTime = it.startTime.getLocalTime().toString(),
betweenEndTime = it.endTime.getLocalTime().toString(),
position = adapterPosition
)
) {
blockGreen.post {
blockGreen.isVisible = true
val cornerRadius =
itemView.resources.getDimensionPixelSize(R.dimen.dp_8).toFloat()
if (
handleStartTime(
startTime = it.startTime,
fixedStartTime = data.fixedStartTime,
block = blockGreen
)
) {
ivProfile.isVisible = true
setTopRadius(cornerRadius, blockGreen)
} else {
ivProfile.isVisible = false
setTopRadius(0f, blockGreen)
}
if (
handleEndTime(
endTime = it.endTime,
fixedEndTime = data.fixedEndTime,
block = blockGreen
)
) {
setBottomRadius(cornerRadius, blockGreen)
} else {
setBottomRadius(0f, blockGreen)
}
}
}
if (areTimesWithinRange(
startTime = data.fixedStartTime,
endTime = data.fixedEndTime,
betweenStartTime = it.shiftStartTime.getLocalTime().toString(),
betweenEndTime = it.shiftEndTime.getLocalTime().toString(),
position = adapterPosition
)
) {
blockBlue.post {
blockBlue.isVisible = true
handleStartTime(
startTime = it.shiftStartTime,
fixedStartTime = data.fixedStartTime,
block = blockBlue
)
handleEndTime(
endTime = it.shiftEndTime,
fixedEndTime = data.fixedEndTime,
block = blockBlue
)
}
}
}
}
}
}
}
private fun setTopRadius(radius: Float, vararg views: View) {
views.forEach {
it.setBackgroundRadiusTopPx(radius)
}
}
private fun setBottomRadius(radius: Float, vararg views: View) {
views.forEach {
it.setBackgroundRadiusBottomPx(radius)
}
}
private fun setRadius(radius: Float, vararg views: View) {
views.forEach {
it.setBackgroundRadiusPx(radius)
}
}
private fun handleStartTime(
startTime: String?,
fixedStartTime: String,
block: View
): Boolean {
return if (startTime?.extractHourOnly() == fixedStartTime.substring(0, 2).toInt()) {
val difference = getBetween(
startTime = fixedStartTime,
endTime = startTime.getLocalTime().toString()
)
val topMargin = calculateTopMargin(
view = block,
context = block.context,
differenceInMinutes = difference,
isTop = true
)
eLog("============================================================================")
eLog("Difference ====> $difference Start Time: ${startTime.getLocalTime()}")
block.setTopMargin(topMargin)
true
} else {
false
}
}
private fun areTimesWithinRange(
startTime: String,
endTime: String,
betweenStartTime: String,
betweenEndTime: String,
position: Int
): Boolean {
val format = SimpleDateFormat("HH:mm", Locale.getDefault())
val start = format.parse(startTime)
val end = format.parse(endTime)
val betweenStart = format.parse(betweenStartTime)
val betweenEnd = format.parse(betweenEndTime)
val isBetween =
if (start != null && end != null && betweenStart != null && betweenEnd != null) {
if (betweenStart <= betweenEnd) {
// Check for overlap in the normal case
!(end <= betweenStart || start >= betweenEnd)
} else {
// Check for overlap when the range crosses midnight
!(end <= betweenStart && start >= betweenEnd)
}
} else {
false
}
return isBetween
}
private fun handleEndTime(
endTime: String?,
fixedEndTime: String,
block: View
): Boolean {
return if (endTime?.getAccurateEnd() == fixedEndTime.substring(0, 2).toInt()) {
val difference = getBetween(
startTime = fixedEndTime,
endTime = endTime.getLocalTime().toString()
)
val bottomMargin = calculateTopMargin(
view = block,
context = block.context,
differenceInMinutes = difference,
isTop = false
)
block.setBottomMargin(bottomMargin)
true
} else {
false
}
}
fun setList(time: ArrayList<RotaTimeListResponse>, mainAdapterPosition: Int) {
this.mainAdapterPosition = mainAdapterPosition
shiftTimeList = time
notifyChangeAll()
}
private fun setHeightDynamic(vararg views: View) {
// Get the screen density from any view (all views share the same context density)
val density = views.firstOrNull()?.context?.resources?.displayMetrics?.density ?: return
// Calculate the height dynamically based on density
val dynamicHeight =
(100 * density).toInt() // Adjust '100' to your desired base height logic
// Loop through each view and set its height
views.forEach { view ->
val layoutParams = view.layoutParams
layoutParams.height = dynamicHeight
view.layoutParams = layoutParams
}
}
private fun calculateTopMargin(
context: Context,
differenceInMinutes: Long,
view: View,
isTop: Boolean
): Int {
try {
val totalHeightOfView = view.height.toFloat()
val heightPerMinute = totalHeightOfView / 60L
val topMargin = differenceInMinutes * heightPerMinute
eLog("TOP totalHeightOfView =====> $$totalHeightOfView")
eLog("TOP totalHeightOfView IN DP =====> $${totalHeightOfView.toInt().toDp(context)}")
eLog("TOP heightPerMinute =====> $$heightPerMinute")
eLog("TOP topMargin =====> $$topMargin")
eLog("TOP topMargin IN INTEGER =====> $${topMargin.toInt()}")
eLog("TOP topMargin IN DP =====> $${topMargin.toInt().toDp(context)}")
eLog("TOP topMargin coerceAtMost =====> $${topMargin.coerceAtMost(300f).toInt()}")
// Convert pixels to dp
return topMargin.toInt().toDp(context)
} catch (e: Exception) {
e.printStackTrace()
return 0
}
}
}
Upvotes: 0
Views: 45