prolificinteractive / material-calendarview

A Material design back port of Android's CalendarView
https://prolificinteractive.github.io/material-calendarview/
MIT License
5.92k stars 1.32k forks source link

How to remove decorator for a selected day? #902

Open orifn opened 5 years ago

orifn commented 5 years ago

First of all I want to mention issues #190 and #679 which are related to this topic, but discussion/comments on those issues didn't helped me solve the problem. The samples mentioned in repository also do not cover this topic. So, the topic is about removing event decorator for a selected date which was added previously. Is there any possibility to achieve that?

Here is what I have so far:

public class RemoveDecorator implements DayViewDecorator {
    private final HashSet<CalendarDay> dates;

    public RemoveDecorator(Collection<CalendarDay> dates) {
        this.dates = new HashSet<>(dates);
    }

    @Override
    public boolean shouldDecorate(CalendarDay day) {
        return dates.contains(day);
        // also tried with just
        // return false;
    }

    @Override
    public void decorate(DayViewFacade view) {
        // TODO: what to do?
    }
}

which is used in delete method like this:

private MaterialCalendarView widget;
private List<CalendarDay> calendarDays = new ArrayList<CalendarDay>();

private void removeEvent(CalendarDay day) {
    calendarDays.remove(day);
    widget.removeDecorator(new RemoveDecorator(calendarDays));
    widget.invalidateDecorators();
}

Tested on: Android: API15 and API27 with material-calendarview: 1.6.1

heitorcolangelo commented 5 years ago

@orif-jr What do you want to achieve? In my case, I have a decorator for the current day and the selected text appearance is defined in the XML. If the user selects the current day, the appearance should be the one defined for selected dates and not the decorator one. Is that what you want to achieve?

In the below gif, the described situation happens when the user clicks on '28'.

ezgif com-crop

orifn commented 5 years ago

@heitorcolangelo In my case its about adding/deleting a dot decoration under the selected date. To achieve this I used a decorator sample. So, I want to delete previously added dot decoration for the selected date, i.e. in my example there are two buttons on top of the calendar: one is for adding a "dot" and another button is for deleting that "dot".

The adding dot procedure works fine, i.e. after calling the decorator the view refreshes automatically and one can see the dot. But deleting procedure doesn't works well, i.e. after calling the remove decorator one have to change the view (go to another android activity) and then comeback to calendar view in order to see that decorator is removed.

Either I use widget.invalidateDecorators(); in a wrong place or I miss something that results such behaviour in calendar which doesn't refreshes the view properly after the deletion of decorator. šŸ¤·šŸ»ā€ā™‚ļø

p.s. I added a link to my decorator in the question to make it more clear.

ezgif com-resize

heitorcolangelo commented 5 years ago

@orif-jr I think you must keep a reference to that RemoveDecorator instance, and then pass it inside the removeDecorator method, like this:

private MaterialCalendarView widget;
private RemoveDecorator decoratorReference;
private List<CalendarDay> calendarDays = new ArrayList<CalendarDay>();

private void removeEvent(CalendarDay day) {
    calendarDays.remove(day);
    widget.removeDecorator(decoratorReference);
    widget.invalidateDecorators();
}

Don't forget to initialize the decorator before removing it. I really hope that this works šŸ™ šŸ˜†

sergii-frost commented 5 years ago

@heitorcolangelo it seems your gif is quite close to what I want to achieve:

What was your approach in achieving your behaviour for "today"?

heitorcolangelo commented 5 years ago

It's not straightforward, but it works. Create those two styles in your styles.xml

<style name="Typography.Calendar.Day">
    <item name="android:textColor">@color/sl_calendar_day_text_color</item>
</style>
<style name="Typography.Calendar.Today">
    <item name="android:textColor">@color/sl_calendar_today_text_color</item>
</style>

Those are used to style the text color only. After that, add those two selectors in your res/color folder. sl_calendar_day_text_color

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:color="@color/white"
        android:state_checked="true"/>
    <item
        android:color="@color/white"
        android:state_pressed="true"/>
    <item
        android:color="@color/disabled_grey"
        android:state_enabled="false"/>
    <item android:color="@color/colorTextPrimary"/>
</selector>

sl_calendar_today_text_color

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:color="@color/white"
        android:state_checked="true"/>
    <item
        android:color="@color/white"
        android:state_pressed="true"/>
    <item
        android:color="@color/disabled_grey"
        android:state_enabled="false"/>
    <item android:color="@color/colorPrimary"/> //This color is different
</selector>

Then in your MaterialCalendarView

<com.prolificinteractive.materialcalendarview.MaterialCalendarView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/calendar_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:mcv_dateTextAppearance="@style/Typography.Calendar.Day"
    app:mcv_selectionColor="@color/colorPrimary"
    app:mcv_showOtherDates="all"
    app:mcv_tileHeight="@dimen/calendar_tile_height"
    app:mcv_tileWidth="@dimen/calendar_tile_width"/>

The other style we apply programatically

private void setTodayDecorator(MaterialCalendarView calendar) {
        Drawable background = getResources().getDrawable(R.drawable.bg_calendar_today, getContext().getTheme());
        TextAppearanceSpan textAppearance = new TextAppearanceSpan(getContext(), R.style.Typography_Calendar_Today);
        TodayDecorator todayDecorator = new TodayDecorator(CalendarDay.today(), textAppearance, background);
        calendar.addDecorator(todayDecorator);
    }

Add this drawable to customize your background bg_calendar_today

<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="oval">
    <solid
        android:color="@color/sl_calendar_today_background_color"/>
</shape>

And this one to your res/color folder sl_calendar_today_background_color

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:color="@color/colorPrimary"
        android:state_checked="true"/>
    <item
        android:color="@color/colorPrimary"
        android:state_pressed="true"/>
    <item
        android:color="@color/disabled_grey"
        android:state_enabled="false"/>
    <item android:color="@color/blueLight03"/>
</selector>

@sergii-frost let me know if that works, I might have missed something.

sergii-frost commented 5 years ago

@heitorcolangelo amazing! The last part with custom selector used as color did the trick for me šŸ‘

Thanks for the help! šŸš€

orifn commented 5 years ago

@heitorcolangelo unfortunately it didn't worked :( here is what I did:

private MaterialCalendarView widget;
private RemoveDecorator decoratorReference;
private List<CalendarDay> calendarDays = new ArrayList<CalendarDay>();

private void removeEvent(CalendarDay day) {
    calendarDays.remove(day);

    decoratorReference = new RemoveDecorator(calendarDays);

    widget.removeDecorator(decoratorReference);
    widget.invalidateDecorators();
}

p.s. I updated my first answer for your question with a gif file

heitorcolangelo commented 5 years ago

@orif-jr I think it's because you are creating a new RemoveDecorator instance before removing it. If you do that, you'll never remove the correct decorator instance from calendar. What I mean by keep a reference is something like this:

private MaterialCalendarView widget;
private RemoveDecorator decoratorReference;
private List<CalendarDay> calendarDays = new ArrayList<CalendarDay>();

private void addEvent(CalendarDay day) {
    // here you initialize the decorator
    decoratorReference = new RemoveDecorator(calendarDays);
    widget.addDecorator(decoratorReference);
}

private void removeEvent(CalendarDay day) {
    calendarDays.remove(day);

    widget.removeDecorator(decoratorReference);
    widget.invalidateDecorators();
}
orifn commented 5 years ago

@heitorcolangelo

Ifyou do that, you'll never remove the correct decorator instance from calendar.

But I already able to remove the decorator (have a look to attached GIF-demo where I try to remove decorator from 5th of December). The only thing is it doesn't refreshes the calendar view after the remove method call. To do that I need to change view (go to another menu and come back to calendar view)...

Anyway I tried your suggestion, i.e. creation of the reference for RemoveDecorator inside of addEvent(), but still it doesn't solve the problem. The calendar view still doesn't refreshes the view automatically after removeEvent method call. Btw, the usage of AddEvent decorator (see LOC:100) doesn't requires to keep a reference for a decorator and author of the library also uses it directly in his demos which works fine and calendar view refreshes properly each time after adding red dots. But the vice versa concept (RemoveEvent) with the same decorator concept (DayViewDecorator) not working for me. šŸ˜•

heitorcolangelo commented 5 years ago

@orif-jr I'm sorry, now I got it what were you talking about. I haven't used those dots decorator yet, but I'll have to implement it in a couple of days. I'll see how we are going to handle that case and then I'll come back here ok? It's quite rush here at the end of the year so I don't think I'll have spare time enough to check that šŸ˜ž In any case, if you find the solution, please keep us up-to-date! šŸ™

alishanidrees commented 4 years ago

@orif-jr What do you want to achieve? In my case, I have a decorator for the current day and the selected text appearance is defined in the XML. If the user selects the current day, the appearance should be the one defined for selected dates and not the decorator one. Is that what you want to achieve?

In the below gif, the described situation happens when the user clicks on '28'.

ezgif com-crop

can you share code of this program i want it

heitorcolangelo commented 4 years ago

Hey, sorry I cannot share this code as I don't have access to that codebase anymore. But there's plenty of the code in one of my comments https://github.com/prolificinteractive/material-calendarview/issues/902#issuecomment-444575359