28
28
import java .util .ArrayList ;
29
29
import java .util .Calendar ;
30
30
import java .util .Collections ;
31
+ import java .util .Comparator ;
31
32
import java .util .List ;
32
33
33
34
/**
@@ -448,18 +449,19 @@ private void drawEvents(Calendar date, float startFromPixel, Canvas canvas) {
448
449
if (isSameDay (mEventRects .get (i ).event .getStartTime (), date )) {
449
450
450
451
// Calculate top.
451
- float top = mEventRects .get (i ).event .getStartTime ().get (Calendar .HOUR_OF_DAY ) * 60 + mEventRects .get (i ).event .getStartTime ().get (Calendar .MINUTE );
452
- top = mHourHeight * 24 * top / 1440 + mCurrentOrigin .y + mHeaderTextHeight + mHeaderRowPadding * 2 + mHeaderMarginBottom + mTimeTextHeight /2 ;
452
+ float top = mHourHeight * 24 * mEventRects .get (i ).top / 1440 + mCurrentOrigin .y + mHeaderTextHeight + mHeaderRowPadding * 2 + mHeaderMarginBottom + mTimeTextHeight /2 ;
453
453
float originalTop = top ;
454
- if (top < mHeaderTextHeight + mHeaderRowPadding * 2 + mHeaderMarginBottom + mTimeTextHeight /2 ) top = mHeaderTextHeight + mHeaderRowPadding * 2 + mHeaderMarginBottom + mTimeTextHeight /2 ;
454
+ if (top < mHeaderTextHeight + mHeaderRowPadding * 2 + mHeaderMarginBottom + mTimeTextHeight /2 )
455
+ top = mHeaderTextHeight + mHeaderRowPadding * 2 + mHeaderMarginBottom + mTimeTextHeight /2 ;
455
456
456
457
// Calculate bottom.
457
- float bottom = mEventRects .get (i ).event . getEndTime (). get ( Calendar . HOUR_OF_DAY ) * 60 + mEventRects . get ( i ). event . getEndTime (). get ( Calendar . MINUTE ) ;
458
+ float bottom = mEventRects .get (i ).bottom ;
458
459
bottom = mHourHeight * 24 * bottom / 1440 + mCurrentOrigin .y + mHeaderTextHeight + mHeaderRowPadding * 2 + mHeaderMarginBottom + mTimeTextHeight /2 ;
459
460
460
461
// Calculate left and right.
461
- float left = startFromPixel ;
462
- float right = startFromPixel + mWidthPerDay ;
462
+ float left = startFromPixel + mEventRects .get (i ).left * mWidthPerDay ;
463
+ float originalLeft = left ;
464
+ float right = left + mEventRects .get (i ).width * mWidthPerDay ;
463
465
if (left < mHeaderColumnWidth ) left = mHeaderColumnWidth ;
464
466
465
467
// Draw the event and the event name on top of it.
@@ -474,7 +476,7 @@ eventRectF.top < getHeight() &&
474
476
mEventRects .get (i ).rectF = eventRectF ;
475
477
mEventBackgroundPaint .setColor (mEventRects .get (i ).event .getColor () == 0 ? mDefaultEventColor : mEventRects .get (i ).event .getColor ());
476
478
canvas .drawRect (mEventRects .get (i ).rectF , mEventBackgroundPaint );
477
- drawText (mEventRects .get (i ).event .getName (), mEventRects .get (i ).rectF , canvas , originalTop , startFromPixel );
479
+ drawText (mEventRects .get (i ).event .getName (), mEventRects .get (i ).rectF , canvas , originalTop , originalLeft );
478
480
}
479
481
else
480
482
mEventRects .get (i ).rectF = null ;
@@ -483,6 +485,7 @@ eventRectF.top < getHeight() &&
483
485
}
484
486
}
485
487
488
+
486
489
/**
487
490
* Draw the name of the event on top of the event rectangle.
488
491
* @param text The text to draw.
@@ -516,7 +519,6 @@ else if (lineHeight >= availableHeight) {
516
519
canvas .translate (originalLeft + mEventPadding , originalTop + mEventPadding );
517
520
mTextLayout .draw (canvas );
518
521
canvas .restore ();
519
-
520
522
}
521
523
522
524
@@ -526,6 +528,10 @@ else if (lineHeight >= availableHeight) {
526
528
private class EventRect {
527
529
public WeekViewEvent event ;
528
530
public RectF rectF ;
531
+ public float left ;
532
+ public float width ;
533
+ public float top ;
534
+ public float bottom ;
529
535
530
536
public EventRect (WeekViewEvent event , RectF rectF ) {
531
537
this .event = event ;
@@ -564,6 +570,7 @@ private void getMoreEvents(Calendar day) {
564
570
if (mFetchedMonths [0 ] < 1 || mFetchedMonths [0 ] != previousMonth || mRefreshEvents ) {
565
571
if (!containsValue (lastFetchedMonth , previousMonth ) && !isInEditMode ()){
566
572
List <WeekViewEvent > events = mMonthChangeListener .onMonthChange ((previousMonth ==12 )?day .get (Calendar .YEAR )-1 :day .get (Calendar .YEAR ), previousMonth );
573
+ sortEvents (events );
567
574
for (WeekViewEvent event : events ) {
568
575
mEventRects .add (new EventRect (event , null ));
569
576
}
@@ -575,6 +582,7 @@ private void getMoreEvents(Calendar day) {
575
582
if (mFetchedMonths [1 ] < 1 || mFetchedMonths [1 ] != day .get (Calendar .MONTH )+1 || mRefreshEvents ) {
576
583
if (!containsValue (lastFetchedMonth , day .get (Calendar .MONTH )+1 ) && !isInEditMode ()) {
577
584
List <WeekViewEvent > events = mMonthChangeListener .onMonthChange (day .get (Calendar .YEAR ), day .get (Calendar .MONTH ) + 1 );
585
+ sortEvents (events );
578
586
for (WeekViewEvent event : events ) {
579
587
mEventRects .add (new EventRect (event , null ));
580
588
}
@@ -586,15 +594,165 @@ private void getMoreEvents(Calendar day) {
586
594
if (mFetchedMonths [2 ] < 1 || mFetchedMonths [2 ] != nextMonth || mRefreshEvents ) {
587
595
if (!containsValue (lastFetchedMonth , nextMonth ) && !isInEditMode ()) {
588
596
List <WeekViewEvent > events = mMonthChangeListener .onMonthChange (nextMonth == 1 ? day .get (Calendar .YEAR ) + 1 : day .get (Calendar .YEAR ), nextMonth );
597
+ sortEvents (events );
589
598
for (WeekViewEvent event : events ) {
590
599
mEventRects .add (new EventRect (event , null ));
591
600
}
592
601
}
593
602
mFetchedMonths [2 ] = nextMonth ;
594
603
}
604
+
605
+ // Prepare to calculate positions of each events.
606
+ ArrayList <EventRect > tempEvents = new ArrayList <EventRect >(mEventRects );
607
+ mEventRects = new ArrayList <EventRect >();
608
+ Calendar dayCounter = (Calendar ) day .clone ();
609
+ dayCounter .add (Calendar .MONTH , -1 );
610
+ dayCounter .set (Calendar .DAY_OF_MONTH , 1 );
611
+ Calendar maxDay = (Calendar ) day .clone ();
612
+ maxDay .add (Calendar .MONTH , 1 );
613
+ maxDay .set (Calendar .DAY_OF_MONTH , maxDay .getActualMaximum (Calendar .DAY_OF_MONTH ));
614
+
615
+ // Iterate through each day to calculate the position of the events.
616
+ while (dayCounter .getTimeInMillis () <= maxDay .getTimeInMillis ()) {
617
+ ArrayList <EventRect > eventRects = new ArrayList <EventRect >();
618
+ for (EventRect eventRect : tempEvents ) {
619
+ if (isSameDay (eventRect .event .getStartTime (), dayCounter ))
620
+ eventRects .add (eventRect );
621
+ }
622
+ computePositionOfEvents (eventRects );
623
+ dayCounter .add (Calendar .DATE , 1 );
624
+ }
625
+ }
626
+
627
+ /**
628
+ * Sorts the events in ascending order.
629
+ * @param events The events to be sorted.
630
+ */
631
+ private void sortEvents (List <WeekViewEvent > events ) {
632
+ Collections .sort (events , new Comparator <WeekViewEvent >() {
633
+ @ Override
634
+ public int compare (WeekViewEvent event1 , WeekViewEvent event2 ) {
635
+ long start1 = event1 .getStartTime ().getTimeInMillis ();
636
+ long start2 = event2 .getStartTime ().getTimeInMillis ();
637
+ int comparator = start1 > start2 ? 1 : (start1 < start2 ? -1 : 0 );
638
+ if (comparator == 0 ) {
639
+ long end1 = event1 .getEndTime ().getTimeInMillis ();
640
+ long end2 = event2 .getEndTime ().getTimeInMillis ();
641
+ comparator = end1 > end2 ? 1 : (end1 < end2 ? -1 : 0 );
642
+ }
643
+ return comparator ;
644
+ }
645
+ });
646
+ }
647
+
648
+ /**
649
+ * Calculates the left and right positions of each events. This comes handy specially if events
650
+ * are overlapping.
651
+ * @param eventRects The events along with their wrapper class.
652
+ */
653
+ private void computePositionOfEvents (List <EventRect > eventRects ) {
654
+ // Make "collision groups" for all events that collide with others.
655
+ List <List <EventRect >> collisionGroups = new ArrayList <List <EventRect >>();
656
+ for (EventRect eventRect : eventRects ) {
657
+ boolean isPlaced = false ;
658
+ outerLoop :
659
+ for (List <EventRect > collisionGroup : collisionGroups ) {
660
+ for (EventRect groupEvent : collisionGroup ) {
661
+ if (isEventsCollide (groupEvent .event , eventRect .event )) {
662
+ collisionGroup .add (eventRect );
663
+ isPlaced = true ;
664
+ break outerLoop ;
665
+ }
666
+ }
667
+ }
668
+ if (!isPlaced ) {
669
+ List <EventRect > newGroup = new ArrayList <EventRect >();
670
+ newGroup .add (eventRect );
671
+ collisionGroups .add (newGroup );
672
+ }
673
+ }
674
+
675
+ for (List <EventRect > collisionGroup : collisionGroups ) {
676
+ expandEventsToMaxWidth (collisionGroup );
677
+ }
678
+
679
+ }
680
+
681
+ /**
682
+ * Expands all the events to maximum possible width. The events will try to occupy maximum
683
+ * space available horizontally.
684
+ * @param collisionGroup The group of events which overlap with each other.
685
+ */
686
+ private void expandEventsToMaxWidth (List <EventRect > collisionGroup ) {
687
+ // Expand the events to maximum possible width.
688
+ List <List <EventRect >> columns = new ArrayList <List <EventRect >>();
689
+ columns .add (new ArrayList <EventRect >());
690
+ for (EventRect eventRect : collisionGroup ) {
691
+ boolean isPlaced = false ;
692
+ for (List <EventRect > column : columns ) {
693
+ if (column .size () == 0 ) {
694
+ column .add (eventRect );
695
+ isPlaced = true ;
696
+ }
697
+ else if (!isEventsCollide (eventRect .event , column .get (column .size ()-1 ).event )) {
698
+ column .add (eventRect );
699
+ isPlaced = true ;
700
+ break ;
701
+ }
702
+ }
703
+ if (!isPlaced ) {
704
+ List <EventRect > newColumn = new ArrayList <EventRect >();
705
+ newColumn .add (eventRect );
706
+ columns .add (newColumn );
707
+ }
708
+ }
709
+
710
+
711
+ // Calculate left and right position for all the events.
712
+ int maxRowCount = columns .get (0 ).size ();
713
+ for (int i = 0 ; i < maxRowCount ; i ++) {
714
+ // Set the left and right values of the event.
715
+ float j = 0 ;
716
+ for (List <EventRect > column : columns ) {
717
+ if (column .size () >= i +1 ) {
718
+ EventRect eventRect = column .get (i );
719
+ eventRect .width = 1f / columns .size ();
720
+ eventRect .left = j / columns .size ();
721
+ eventRect .top = eventRect .event .getStartTime ().get (Calendar .HOUR_OF_DAY ) * 60 + eventRect .event .getStartTime ().get (Calendar .MINUTE );
722
+ eventRect .bottom = eventRect .event .getEndTime ().get (Calendar .HOUR_OF_DAY ) * 60 + eventRect .event .getEndTime ().get (Calendar .MINUTE );;
723
+ mEventRects .add (eventRect );
724
+ }
725
+ j ++;
726
+ }
727
+ }
595
728
}
596
729
597
730
731
+ /**
732
+ * Checks if two events overlap.
733
+ * @param event1 The first event.
734
+ * @param event2 The second event.
735
+ * @return true if the events overlap.
736
+ */
737
+ private boolean isEventsCollide (WeekViewEvent event1 , WeekViewEvent event2 ) {
738
+ long start1 = event1 .getStartTime ().getTimeInMillis ();
739
+ long end1 = event1 .getEndTime ().getTimeInMillis ();
740
+ long start2 = event2 .getStartTime ().getTimeInMillis ();
741
+ long end2 = event2 .getEndTime ().getTimeInMillis ();
742
+ return (start1 > start2 && start1 < end2 ) || (end1 > start2 && end1 < end2 );
743
+ }
744
+
745
+
746
+ /**
747
+ * Checks if time1 occurs after (or at the same time) time2.
748
+ * @param time1 The time to check.
749
+ * @param time2 The time to check against.
750
+ * @return true if time1 and time2 are equal or if time1 is after time2. Otherwise false.
751
+ */
752
+ private boolean isTimeAfterOrEquals (Calendar time1 , Calendar time2 ) {
753
+ return !(time1 == null || time2 == null ) && time1 .getTimeInMillis () >= time2 .getTimeInMillis ();
754
+ }
755
+
598
756
/**
599
757
* Deletes the events of the months that are too far away from the current month.
600
758
* @param currentDay The current day.
0 commit comments