├── gradle.properties ├── settings.gradle ├── ic_launcher.png ├── .gitignore ├── library ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ ├── res │ │ ├── drawable-hdpi │ │ │ ├── ic_action_back.png │ │ │ └── ic_action_forward.png │ │ ├── drawable-mdpi │ │ │ ├── ic_action_back.png │ │ │ └── ic_action_forward.png │ │ ├── drawable-xhdpi │ │ │ ├── ic_action_back.png │ │ │ └── ic_action_forward.png │ │ ├── drawable-xxhdpi │ │ │ ├── ic_action_back.png │ │ │ └── ic_action_forward.png │ │ ├── drawable-xxxhdpi │ │ │ ├── ic_action_back.png │ │ │ └── ic_action_forward.png │ │ ├── values │ │ │ ├── strings.xml │ │ │ ├── colours.xml │ │ │ ├── dimens.xml │ │ │ ├── attrs.xml │ │ │ └── styles.xml │ │ ├── drawable │ │ │ ├── selected.xml │ │ │ ├── ic_background.xml │ │ │ ├── ic_down.xml │ │ │ └── ic_up.xml │ │ ├── values-v21 │ │ │ └── styles.xml │ │ └── layout │ │ │ ├── calendar.xml │ │ │ ├── dialog.xml │ │ │ ├── month.xml │ │ │ ├── week.xml │ │ │ ├── week_1.xml │ │ │ ├── week_2.xml │ │ │ ├── week_3.xml │ │ │ ├── week_4.xml │ │ │ ├── week_5.xml │ │ │ └── week_6.xml │ │ └── java │ │ └── org │ │ └── billthefarmer │ │ └── view │ │ ├── DayDecorator.java │ │ ├── CalendarListener.java │ │ ├── DayView.java │ │ ├── CalendarUtils.java │ │ ├── CustomCalendarDialog.java │ │ └── CustomCalendarView.java └── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── app ├── src │ └── main │ │ ├── res │ │ ├── drawable-hdpi │ │ │ └── ic_launcher.png │ │ ├── drawable-mdpi │ │ │ └── ic_launcher.png │ │ ├── drawable-xhdpi │ │ │ └── ic_launcher.png │ │ ├── drawable-xxhdpi │ │ │ └── ic_launcher.png │ │ ├── drawable-xxxhdpi │ │ │ └── ic_launcher.png │ │ ├── values │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ ├── values-v21 │ │ │ └── styles.xml │ │ └── layout │ │ │ └── main.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── org │ │ └── billthefarmer │ │ └── test │ │ └── Main.java └── build.gradle ├── .github └── workflows │ ├── build.yml │ └── release.yml ├── gradlew.bat ├── gradlew ├── README.md └── Apache-2.0.txt /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':library' 2 | -------------------------------------------------------------------------------- /ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billthefarmer/CustomCalendarView/HEAD/ic_launcher.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle/ 2 | build/ 3 | local.properties 4 | *.iml 5 | .idea/ 6 | *~ 7 | *.orig 8 | *.aar 9 | -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billthefarmer/CustomCalendarView/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billthefarmer/CustomCalendarView/HEAD/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billthefarmer/CustomCalendarView/HEAD/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billthefarmer/CustomCalendarView/HEAD/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billthefarmer/CustomCalendarView/HEAD/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billthefarmer/CustomCalendarView/HEAD/app/src/main/res/drawable-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | CalendarView Sample 4 | 5 | -------------------------------------------------------------------------------- /library/src/main/res/drawable-hdpi/ic_action_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billthefarmer/CustomCalendarView/HEAD/library/src/main/res/drawable-hdpi/ic_action_back.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-mdpi/ic_action_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billthefarmer/CustomCalendarView/HEAD/library/src/main/res/drawable-mdpi/ic_action_back.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/ic_action_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billthefarmer/CustomCalendarView/HEAD/library/src/main/res/drawable-xhdpi/ic_action_back.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/ic_action_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billthefarmer/CustomCalendarView/HEAD/library/src/main/res/drawable-xxhdpi/ic_action_back.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-hdpi/ic_action_forward.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billthefarmer/CustomCalendarView/HEAD/library/src/main/res/drawable-hdpi/ic_action_forward.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-mdpi/ic_action_forward.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billthefarmer/CustomCalendarView/HEAD/library/src/main/res/drawable-mdpi/ic_action_forward.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xhdpi/ic_action_forward.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billthefarmer/CustomCalendarView/HEAD/library/src/main/res/drawable-xhdpi/ic_action_forward.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxxhdpi/ic_action_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billthefarmer/CustomCalendarView/HEAD/library/src/main/res/drawable-xxxhdpi/ic_action_back.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxhdpi/ic_action_forward.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billthefarmer/CustomCalendarView/HEAD/library/src/main/res/drawable-xxhdpi/ic_action_forward.png -------------------------------------------------------------------------------- /library/src/main/res/drawable-xxxhdpi/ic_action_forward.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billthefarmer/CustomCalendarView/HEAD/library/src/main/res/drawable-xxxhdpi/ic_action_forward.png -------------------------------------------------------------------------------- /library/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | %s %d 4 | 5 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/selected.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/ic_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/ic_down.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /library/src/main/res/drawable/ic_up.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /library/src/main/res/values/colours.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @android:color/background_light 5 | @android:color/background_dark 6 | #F1F5F8 7 | #DEDEDE 8 | @android:color/holo_blue_light 9 | 10 | 11 | -------------------------------------------------------------------------------- /library/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /library/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8dp 5 | 8dp 6 | 8dp 7 | 8dp 8 | 12 | 32dp 13 | 32dp 14 | 15 | 16 | -------------------------------------------------------------------------------- /library/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.github.dcendents.android-maven' 3 | 4 | group = 'com.github.billthefarmer' 5 | 6 | android { 7 | compileSdkVersion 28 8 | buildToolsVersion '29.0.3' 9 | 10 | defaultConfig { 11 | minSdkVersion 14 12 | targetSdkVersion 28 13 | } 14 | 15 | compileOptions { 16 | sourceCompatibility JavaVersion.VERSION_1_8 17 | targetCompatibility JavaVersion.VERSION_1_8 18 | } 19 | 20 | lintOptions { 21 | disable 'OldTargetApi', 'IconMissingDensityFolder', 'IconDensities', 22 | 'InflateParams', 'ExpiredTargetSdkVersion' 23 | // abortOnError false 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | buildToolsVersion '29.0.3' 6 | 7 | defaultConfig { 8 | applicationId "org.billthefarmer.test" 9 | minSdkVersion 14 10 | targetSdkVersion 28 11 | versionName "1.0" 12 | versionCode 1 13 | } 14 | 15 | compileOptions { 16 | sourceCompatibility JavaVersion.VERSION_1_8 17 | targetCompatibility JavaVersion.VERSION_1_8 18 | } 19 | 20 | lintOptions { 21 | disable 'IconDensities', 'ExpiredTargetSdkVersion', 'OldTargetApi', 22 | 'AllowBackup' 23 | // abortOnError false 24 | } 25 | } 26 | 27 | dependencies { 28 | implementation project(':library') 29 | } 30 | -------------------------------------------------------------------------------- /library/src/main/java/org/billthefarmer/view/DayDecorator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Stacktips {link: http://stacktips.com}. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.billthefarmer.view; 18 | 19 | // DayDecorator 20 | public interface DayDecorator 21 | { 22 | void decorate(DayView cell); 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 16 | 17 | 18 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /library/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # Build on push and PR events 2 | on: 3 | push: 4 | branches: 5 | - master 6 | tags-ignore: 7 | - '*' 8 | pull_request: 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: "actions/checkout@v2" 15 | 16 | - name: Build with Gradle 17 | run: ./gradlew build 18 | 19 | - name: Show checksums 20 | run: | 21 | sha256sum library/build/outputs/aar/library-release.aar 22 | sha256sum app/build/outputs/apk/debug/app-debug.apk 23 | - name: Upload library 24 | uses: actions/upload-artifact@v2 25 | with: 26 | name: library-release.aar 27 | path: library/build/outputs/aar/library-release.aar 28 | 29 | - name: Upload APK 30 | uses: actions/upload-artifact@v2 31 | with: 32 | name: app-debug.apk 33 | path: app/build/outputs/apk/debug/app-debug.apk 34 | -------------------------------------------------------------------------------- /library/src/main/java/org/billthefarmer/view/CalendarListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Stacktips {link: http://stacktips.com}. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.billthefarmer.view; 18 | 19 | import java.util.Calendar; 20 | 21 | // CalendarListener 22 | public interface CalendarListener 23 | { 24 | void onDateSelected(Calendar date); 25 | 26 | void onMonthChanged(Calendar date); 27 | } 28 | -------------------------------------------------------------------------------- /library/src/main/res/layout/calendar.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | 16 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /library/src/main/res/layout/dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /library/src/main/res/layout/month.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 21 | 22 | 30 | 31 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/java/org/billthefarmer/test/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Bill Farmer. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.billthefarmer.test; 18 | 19 | import android.app.Activity; 20 | import android.os.Bundle; 21 | 22 | import org.billthefarmer.view.CustomCalendarView; 23 | 24 | import java.util.Calendar; 25 | import java.util.Locale; 26 | 27 | public class Main extends Activity { 28 | public final static String TAG = "Main"; 29 | 30 | // onCreate 31 | @Override 32 | protected void onCreate(Bundle savedInstanceState) { 33 | super.onCreate(savedInstanceState); 34 | setContentView(R.layout.main); 35 | 36 | // Initialize CustomCalendarView from layout 37 | CustomCalendarView calendarView = findViewById(R.id.calendar); 38 | 39 | // Show Monday as first date of week 40 | calendarView.setFirstDayOfWeek(Calendar.MONDAY); 41 | 42 | // Show/hide overflow days of a month 43 | // calendarView.setShowOverflowDate(false); 44 | 45 | // Initialize calendar with date 46 | Calendar currentCalendar = Calendar.getInstance(Locale.getDefault()); 47 | 48 | // call refreshCalendar to update calendar the view 49 | calendarView.refreshCalendar(currentCalendar); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # Release on new tags 2 | on: 3 | push: 4 | tags: 5 | - 'v*' 6 | 7 | jobs: 8 | release: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: "actions/checkout@v2" 12 | 13 | - name: Build with Gradle 14 | run: ./gradlew build 15 | 16 | - name: List files 17 | run: find 18 | 19 | - name: Get the tag version 20 | id: version 21 | run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/v} 22 | 23 | - name: Show checksums 24 | run: | 25 | sha256sum library/build/outputs/aar/library-release.aar 26 | sha256sum app/build/outputs/apk/debug/app-debug.apk 27 | - name: Create release 28 | id: create_release 29 | uses: actions/create-release@v1 30 | env: 31 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 32 | with: 33 | tag_name: ${{ github.ref }} 34 | release_name: "Version ${{ steps.version.outputs.VERSION }}" 35 | 36 | - name: Upload library 37 | uses: actions/upload-release-asset@v1 38 | env: 39 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 40 | with: 41 | upload_url: ${{ steps.create_release.outputs.upload_url }} 42 | asset_path: library/build/outputs/aar/library-release.aar 43 | asset_name: "CustomCalendarView-${{ steps.version.outputs.VERSION }}.aar" 44 | asset_content_type: application/java-archive 45 | 46 | - name: Upload APK 47 | uses: actions/upload-release-asset@v1 48 | env: 49 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 50 | with: 51 | upload_url: ${{ steps.create_release.outputs.upload_url }} 52 | asset_path: app/build/outputs/apk/debug/app-debug.apk 53 | asset_name: "CustomCalendarViewTest-${{ steps.version.outputs.VERSION }}.apk" 54 | asset_content_type: application/vnd.android.package-archive 55 | -------------------------------------------------------------------------------- /library/src/main/java/org/billthefarmer/view/DayView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Stacktips {link: http://stacktips.com}. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.billthefarmer.view; 18 | 19 | import android.content.Context; 20 | import android.util.AttributeSet; 21 | import android.widget.TextView; 22 | 23 | import java.text.DateFormat; 24 | import java.text.SimpleDateFormat; 25 | import java.util.Calendar; 26 | import java.util.List; 27 | import java.util.Locale; 28 | 29 | // DayView 30 | public class DayView extends TextView 31 | { 32 | private Calendar date; 33 | private List decorators; 34 | 35 | // DayView 36 | public DayView(Context context, AttributeSet attrs) 37 | { 38 | super(context, attrs); 39 | } 40 | 41 | // bind 42 | public void bind(Calendar date, List decorators) 43 | { 44 | this.date = date; 45 | this.decorators = decorators; 46 | 47 | final DateFormat dateFormat = 48 | new SimpleDateFormat("d", Locale.getDefault()); 49 | setText(dateFormat.format(date.getTime())); 50 | } 51 | 52 | // decorate 53 | public void decorate() 54 | { 55 | // Set custom decorators 56 | if (decorators != null) 57 | { 58 | for (DayDecorator decorator : decorators) 59 | { 60 | decorator.decorate(this); 61 | } 62 | } 63 | } 64 | 65 | // getDate 66 | public Calendar getDate() 67 | { 68 | return date; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /library/src/main/res/layout/week.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 17 | 23 | 24 | 30 | 31 | 37 | 38 | 44 | 45 | 51 | 52 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /library/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 16 | 17 | 18 | 24 | 25 | 34 | 35 | 43 | 44 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /library/src/main/java/org/billthefarmer/view/CalendarUtils.java: -------------------------------------------------------------------------------- 1 | 2 | package org.billthefarmer.view; 3 | 4 | import java.util.Calendar; 5 | import java.util.Date; 6 | 7 | /** 8 | * Created by Nilanchala Panigrahy on 8/24/16. 9 | */ 10 | 11 | public class CalendarUtils 12 | { 13 | public static boolean isSameMonth(Calendar c1, Calendar c2) 14 | { 15 | if (c1 == null || c2 == null) 16 | return false; 17 | return (c1.get(Calendar.ERA) == c2.get(Calendar.ERA) 18 | && c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR) 19 | && c1.get(Calendar.MONTH) == c2.get(Calendar.MONTH)); 20 | } 21 | 22 | /** 23 | *

Checks if a calendar is today.

24 | * 25 | * @param calendar the calendar, not altered, not null. 26 | * @return true if the calendar is today. 27 | * @throws IllegalArgumentException if the calendar is null 28 | */ 29 | public static boolean isToday(Calendar calendar) 30 | { 31 | return isSameDay(calendar, Calendar.getInstance()); 32 | } 33 | 34 | public static boolean isSameDay(Calendar cal1, Calendar cal2) 35 | { 36 | if (cal1 == null || cal2 == null) 37 | throw new IllegalArgumentException("The dates must not be null"); 38 | return (cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && 39 | cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && 40 | cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR)); 41 | } 42 | 43 | public static int getTotalWeeks(Calendar calendar) 44 | { 45 | if (null == calendar) return 0; 46 | return calendar.getActualMaximum(Calendar.WEEK_OF_MONTH); 47 | 48 | } 49 | 50 | public static int getTotalWeeks(Date date) 51 | { 52 | Calendar cal = Calendar.getInstance(); 53 | cal.setTime(date); 54 | return getTotalWeeks(cal); 55 | } 56 | 57 | public static boolean isPastDay(Date date) 58 | { 59 | Calendar calendar = Calendar.getInstance(); 60 | calendar.set(Calendar.HOUR_OF_DAY, 0); 61 | calendar.set(Calendar.MINUTE, 0); 62 | calendar.set(Calendar.SECOND, 0); 63 | calendar.set(Calendar.MILLISECOND, 0); 64 | return date.before(calendar.getTime()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /library/src/main/res/layout/week_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 26 | 27 | 35 | 36 | 44 | 45 | 53 | 54 | 62 | 63 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /library/src/main/res/layout/week_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 26 | 27 | 35 | 36 | 44 | 45 | 53 | 54 | 62 | 63 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /library/src/main/res/layout/week_3.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 26 | 27 | 35 | 36 | 44 | 45 | 53 | 54 | 62 | 63 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /library/src/main/res/layout/week_4.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 26 | 27 | 35 | 36 | 44 | 45 | 53 | 54 | 62 | 63 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /library/src/main/res/layout/week_5.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 26 | 27 | 35 | 36 | 44 | 45 | 53 | 54 | 62 | 63 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /library/src/main/res/layout/week_6.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 26 | 27 | 35 | 36 | 44 | 45 | 53 | 54 | 62 | 63 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto init 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto init 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :init 68 | @rem Get command-line arguments, handling Windows variants 69 | 70 | if not "%OS%" == "Windows_NT" goto win9xME_args 71 | 72 | :win9xME_args 73 | @rem Slurp the command line arguments. 74 | set CMD_LINE_ARGS= 75 | set _SKIP=2 76 | 77 | :win9xME_args_slurp 78 | if "x%~1" == "x" goto execute 79 | 80 | set CMD_LINE_ARGS=%* 81 | 82 | :execute 83 | @rem Setup the command line 84 | 85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 86 | 87 | 88 | @rem Execute Gradle 89 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 90 | 91 | :end 92 | @rem End local scope for the variables with windows NT shell 93 | if "%ERRORLEVEL%"=="0" goto mainEnd 94 | 95 | :fail 96 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 97 | rem the _cmd.exe /c_ return code! 98 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 99 | exit /b 1 100 | 101 | :mainEnd 102 | if "%OS%"=="Windows_NT" endlocal 103 | 104 | :omega 105 | -------------------------------------------------------------------------------- /library/src/main/java/org/billthefarmer/view/CustomCalendarDialog.java: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // CustomCalendarDialog - Custom calendar dialog for Android 4 | // 5 | // Copyright © 2017 Bill Farmer 6 | // 7 | // This program is free software: you can redistribute it and/or modify 8 | // it under the terms of the GNU General Public License as published by 9 | // the Free Software Foundation, either version 3 of the License, or 10 | // (at your option) any later version. 11 | // 12 | // This program is distributed in the hope that it will be useful, 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | // GNU General Public License for more details. 16 | // 17 | // You should have received a copy of the GNU General Public License 18 | // along with this program. If not, see . 19 | // 20 | //////////////////////////////////////////////////////////////////////////////// 21 | 22 | package org.billthefarmer.view; 23 | 24 | import android.app.AlertDialog; 25 | import android.content.Context; 26 | import android.content.DialogInterface; 27 | import android.view.LayoutInflater; 28 | import android.view.View; 29 | 30 | import java.text.DateFormat; 31 | import java.util.Calendar; 32 | 33 | // CustomCalendarDialog 34 | public class CustomCalendarDialog extends AlertDialog 35 | implements CalendarListener, DialogInterface.OnClickListener 36 | { 37 | private final static String TAG = "CustomCalendarDialog"; 38 | 39 | private OnDateSetListener listener; 40 | private CustomCalendarView calendarView; 41 | private Calendar date; 42 | 43 | // CustomCalendarDialog 44 | public CustomCalendarDialog(Context context, 45 | OnDateSetListener listener, 46 | int year, int month, int dayOfMonth) 47 | { 48 | this(context, 0, listener, year, month, dayOfMonth); 49 | } 50 | 51 | // CustomCalendarDialog 52 | public CustomCalendarDialog(Context context, int themeResId, 53 | OnDateSetListener listener, 54 | int year, int month, int dayOfMonth) 55 | { 56 | super(context, themeResId); 57 | 58 | this.listener = listener; 59 | 60 | date = Calendar.getInstance(); 61 | date.set(year, month, dayOfMonth); 62 | 63 | setTitle(DateFormat.getDateInstance(DateFormat.FULL) 64 | .format(date.getTime())); 65 | 66 | String ok = 67 | getContext().getResources().getString(android.R.string.ok); 68 | setButton(DialogInterface.BUTTON_POSITIVE, ok, this); 69 | String cancel = 70 | getContext().getResources().getString(android.R.string.cancel); 71 | setButton(DialogInterface.BUTTON_NEGATIVE, cancel, this); 72 | 73 | View view = 74 | LayoutInflater.from(getContext()).inflate(R.layout.dialog, null); 75 | setView(view); 76 | 77 | calendarView = view.findViewById(R.id.calendar); 78 | calendarView.setCalendarListener(this); 79 | 80 | // Show Monday as first date of week 81 | calendarView.setFirstDayOfWeek(Calendar.MONDAY); 82 | 83 | // Show/hide overflow days of a month 84 | // calendarView.setShowOverflowDate(false); 85 | 86 | // Call refreshCalendar to update calendar 87 | calendarView.refreshCalendar(date); 88 | } 89 | 90 | // onDateSelected 91 | @Override 92 | public void onDateSelected(Calendar date) 93 | { 94 | this.date = date; 95 | setTitle(DateFormat.getDateInstance(DateFormat.FULL) 96 | .format(date.getTime())); 97 | } 98 | 99 | // onMonthChanged 100 | @Override 101 | public void onMonthChanged(Calendar date) 102 | { 103 | setTitle(DateFormat.getDateInstance(DateFormat.FULL) 104 | .format(date.getTime())); 105 | } 106 | 107 | // onClick 108 | @Override 109 | public void onClick(DialogInterface dialog, int which) 110 | { 111 | switch (which) 112 | { 113 | case DialogInterface.BUTTON_POSITIVE: 114 | if (listener != null) 115 | listener.onDateSet(calendarView, 116 | date.get(Calendar.YEAR), 117 | date.get(Calendar.MONTH), 118 | date.get(Calendar.DATE)); 119 | break; 120 | } 121 | } 122 | 123 | // getCalendarView 124 | public CustomCalendarView getCalendarView() 125 | { 126 | return calendarView; 127 | } 128 | 129 | // OnDateSetListener 130 | public interface OnDateSetListener 131 | { 132 | void onDateSet(CustomCalendarView view, 133 | int year, int month, int date); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![Logo](app/src/main/res/drawable-mdpi/ic_launcher.png) CustomCalendarView [![.github/workflows/build.yml](https://github.com/billthefarmer/CustomCalendarView/workflows/.github/workflows/build.yml/badge.svg)](https://github.com/billthefarmer/CustomCalendarView/workflows/.github/actions) [![JitPack](https://jitpack.io/v/billthefarmer/CustomCalendarView.svg)](https://jitpack.io/#billthefarmer/CustomCalendarView) 2 | Android custom calendar view - not quite forked from https://github.com/npanigrahy/Custom-Calendar-View 3 | 4 | ![Calendar](https://github.com/billthefarmer/billthefarmer.github.io/raw/master/images/Calendar.png) 5 | 6 | The [npanigrahy/Custom-Calendar-View](https://github.com/npanigrahy/Custom-Calendar-View) library, 7 | although the best I could find, has several issues 8 | * It is slow starting, probably due to finding views using keys rather 9 | than ids 10 | * If used in a dialog it can be very wide, especially in landscape, 11 | possibly due to using `android:layout_width="match_parent"` rather 12 | than `wrap_content` 13 | * Uses a colour rather than a drawable for the selected entry 14 | background, so you can't use a rounded rectangle, for example, 15 | without providing a DayDecorator 16 | 17 | This is an attempt to fix some of the above without breaking it. It 18 | all works as it should, but the calendar is about the same size as 19 | [CalendarView](https://developer.android.com/reference/android/widget/CalendarView.html). 20 | 21 | ## Use in project 22 | Add the driver to your build.gradle with 23 | ```gradle 24 | allprojects { 25 | repositories { 26 | jcenter() 27 | google() 28 | maven { url "https://jitpack.io" } 29 | } 30 | } 31 | ``` 32 | and: 33 | ```gradle 34 | dependencies { 35 | implementation 'com.github.billthefarmer:CustomCalendarView:v1.05' 36 | } 37 | ``` 38 | 39 | ## Using CustomCalendarDialog 40 | ### Constructors 41 | ```java 42 | public CustomCalendarDialog(Context context, 43 | OnDateSetListener listener, 44 | int year, int month, int date) 45 | 46 | public CustomCalendarDialog(Context context, int themeResId, 47 | OnDateSetListener listener, 48 | int year, int month, int date) 49 | ``` 50 | ### Listener 51 | ```java 52 | public interface OnDateSetListener 53 | { 54 | public abstract void onDateSet (CustomCalendarView view, 55 | int year, int month, int date); 56 | } 57 | ``` 58 | ### Using 59 | ```java 60 | // showCustomCalendar 61 | public void showCustomCalendarDialog(Calendar date) 62 | { 63 | CustomCalendarDialog dialog = new 64 | CustomCalendarDialog(this, this, 65 | date.get(Calendar.YEAR), 66 | date.get(Calendar.MONTH), 67 | date.get(Calendar.DATE)); 68 | 69 | List decorators = new ArrayList(); 70 | decorators.add(new EntryDecorator()); 71 | CustomCalendarView calendarView = dialog.getCalendarView(); 72 | calendarView.setDecorators(decorators); 73 | dialog.show(); 74 | } 75 | ``` 76 | 77 | ## Using CustomCalendarView Library 78 | The GitHub project source includes a simple test application. Once the 79 | library is added to your project, you can include the 80 | CustomCalendarView into your activity/fragment layout using the 81 | following code snippets. 82 | ```xml 83 | 87 | ``` 88 | The above code snippet will show the simple Calendar View with the 89 | default design. You can use the following attributes to customize the 90 | appearance of the calendar. 91 | ```xml 92 | 107 | ``` 108 | Because the android build system merges library resources when it 109 | builds an app, you may replace the drawables in the library with your 110 | own by using the same names. 111 | ``` 112 | ic_action_back.png 113 | ic_action_forward.png 114 | ic_up.xml 115 | ic_down.xml 116 | ic_background.xml 117 | selected.xml 118 | ``` 119 | Initialize the calendar view to control the behavior of the calendar 120 | using the following methods. 121 | ```java 122 | //Initialize CustomCalendarView from layout 123 | calendarView = (CustomCalendarView) findViewById(R.id.calendar_view); 124 | 125 | //Initialize calendar with date 126 | Calendar currentCalendar = Calendar.getInstance(Locale.getDefault()); 127 | 128 | //Show Monday as first date of week 129 | calendarView.setFirstDayOfWeek(Calendar.MONDAY); 130 | 131 | //Show/hide overflow days of a month 132 | calendarView.setShowOverflowDate(false); 133 | 134 | //call refreshCalendar to update calendar the view 135 | calendarView.refreshCalendar(currentCalendar); 136 | 137 | //Handling custom calendar events 138 | calendarView.setCalendarListener(new CalendarListener() 139 | { 140 | @Override 141 | public void onDateSelected(Date date) 142 | { 143 | SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy"); 144 | Toast.makeText(MainActivity.this, df.format(date), Toast.LENGTH_SHORT).show(); 145 | } 146 | 147 | @Override 148 | public void onMonthChanged(Date date) 149 | { 150 | SimpleDateFormat df = new SimpleDateFormat("MM-yyyy"); 151 | Toast.makeText(MainActivity.this, df.format(date), Toast.LENGTH_SHORT).show(); 152 | } 153 | }); 154 | ``` 155 | 156 | ## Using Custom TypeFace 157 | ```java 158 | //Setting custom font 159 | final Typeface typeface = Typeface.createFromAsset(getAssets(), "fonts/Arch_Rival_Bold.ttf"); 160 | if (typeface != null) 161 | { 162 | calendarView.setCustomTypeface(typeface); 163 | calendarView.refreshCalendar(currentCalendar); 164 | } 165 | ``` 166 | 167 | ## Using Day Decorators 168 | ```java 169 | //adding calendar day decorators 170 | List decorators = new ArrayList<>(); 171 | decorators.add(new ColorDecorator()); 172 | calendarView.setDecorators(decorators); 173 | calendarView.refreshCalendar(currentCalendar); 174 | ``` 175 | -------------------------------------------------------------------------------- /Apache-2.0.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /library/src/main/java/org/billthefarmer/view/CustomCalendarView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Stacktips {link: http://stacktips.com}. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.billthefarmer.view; 18 | 19 | import android.content.Context; 20 | import android.content.res.Resources; 21 | import android.content.res.TypedArray; 22 | import android.graphics.Typeface; 23 | import android.util.AttributeSet; 24 | import android.view.GestureDetector; 25 | import android.view.MotionEvent; 26 | import android.view.View; 27 | import android.view.ViewGroup; 28 | import android.widget.ImageButton; 29 | import android.widget.LinearLayout; 30 | import android.widget.TextView; 31 | 32 | import java.text.DateFormatSymbols; 33 | import java.util.Calendar; 34 | import java.util.Date; 35 | import java.util.List; 36 | import java.util.Locale; 37 | 38 | // CustomCalendarView 39 | public class CustomCalendarView extends LinearLayout 40 | { 41 | private static final String TAG = "CustomCalendarView"; 42 | private static final int CALENDAR_DAYS = 42; 43 | 44 | // Days of week 45 | private static final int weekdays[] = 46 | { 47 | R.id.weekday_1, R.id.weekday_2, R.id.weekday_3, 48 | R.id.weekday_4, R.id.weekday_5, R.id.weekday_6, 49 | R.id.weekday_7 50 | }; 51 | 52 | // Weeks of month 53 | private static final int weeks[] = 54 | { 55 | R.id.week_1, R.id.week_2, R.id.week_3, 56 | R.id.week_4, R.id.week_5, R.id.week_6 57 | }; 58 | 59 | // Days of month 60 | private static final int days[] = 61 | { 62 | R.id.day_1, R.id.day_2, R.id.day_3, R.id.day_4, 63 | R.id.day_5, R.id.day_6, R.id.day_7, R.id.day_8, 64 | R.id.day_9, R.id.day_10, R.id.day_11, R.id.day_12, 65 | R.id.day_13, R.id.day_14, R.id.day_15, R.id.day_16, 66 | R.id.day_17, R.id.day_18, R.id.day_19, R.id.day_20, 67 | R.id.day_21, R.id.day_22, R.id.day_23, R.id.day_24, 68 | R.id.day_25, R.id.day_26, R.id.day_27, R.id.day_28, 69 | R.id.day_29, R.id.day_30, R.id.day_31, R.id.day_32, 70 | R.id.day_33, R.id.day_34, R.id.day_35, R.id.day_36, 71 | R.id.day_37, R.id.day_38, R.id.day_39, R.id.day_40, 72 | R.id.day_41, R.id.day_42 73 | }; 74 | 75 | private Context context; 76 | 77 | private GestureDetector gestureDetector; 78 | 79 | private int firstDayOfWeek = Calendar.MONDAY; 80 | private List decorators = null; 81 | private CalendarListener calendarListener; 82 | private Calendar currentCalendar; 83 | private Date lastSelectedDay; 84 | private Typeface customTypeface; 85 | 86 | private int disabledDayBackgroundColor; 87 | private int disabledDayTextColor; 88 | private int calendarBackgroundColor; 89 | private int weekLayoutBackgroundColor; 90 | private int calendarTitleBackgroundColor; 91 | private int selectedDayBackground; 92 | private int selectedDayTextColor; 93 | private int calendarTitleTextColor; 94 | private int dayOfWeekTextColor; 95 | private int dayOfMonthTextColor; 96 | private int currentDayOfMonth; 97 | 98 | private boolean isOverflowDateVisible = true; 99 | // onDayOfMonthClickListener 100 | private OnClickListener onDayOfMonthClickListener = new OnClickListener() 101 | { 102 | @Override 103 | public void onClick(View view) 104 | { 105 | // Extract day selected 106 | final TextView dayOfMonthText = (TextView) view; 107 | 108 | // Fire event 109 | final Calendar calendar = Calendar.getInstance(); 110 | calendar.setFirstDayOfWeek(getFirstDayOfWeek()); 111 | calendar.setTime(currentCalendar.getTime()); 112 | calendar.set(Calendar.DAY_OF_MONTH, 113 | Integer.valueOf(dayOfMonthText.getText().toString())); 114 | markDayAsSelectedDay(calendar.getTime()); 115 | 116 | //Set the current day color 117 | markDayAsCurrentDay(currentCalendar); 118 | 119 | if (calendarListener != null) 120 | calendarListener.onDateSelected(calendar); 121 | } 122 | }; 123 | 124 | // CustomCalendarView 125 | public CustomCalendarView(Context context) 126 | { 127 | super(context); 128 | 129 | this.context = context; 130 | 131 | gestureDetector = 132 | new GestureDetector(context, new GestureListener()); 133 | 134 | setAttributes(); 135 | initializeCalendar(); 136 | } 137 | 138 | // CustomCalendarView 139 | public CustomCalendarView(Context context, AttributeSet attrs) 140 | { 141 | super(context, attrs); 142 | 143 | this.context = context; 144 | 145 | gestureDetector = 146 | new GestureDetector(context, new GestureListener()); 147 | 148 | getAttributes(attrs); 149 | initializeCalendar(); 150 | } 151 | 152 | // setAttributes 153 | @SuppressWarnings("deprecation") 154 | private void setAttributes() 155 | { 156 | Resources resources = getResources(); 157 | 158 | calendarBackgroundColor = resources.getColor(R.color.white); 159 | calendarTitleBackgroundColor = resources.getColor(R.color.white); 160 | calendarTitleTextColor = resources.getColor(R.color.black); 161 | weekLayoutBackgroundColor = resources.getColor(R.color.white); 162 | dayOfWeekTextColor = resources.getColor(R.color.black); 163 | dayOfMonthTextColor = resources.getColor(R.color.black); 164 | disabledDayBackgroundColor = 165 | resources.getColor(R.color.day_disabled_background_color); 166 | disabledDayTextColor = 167 | resources.getColor(R.color.day_disabled_text_color); 168 | selectedDayBackground = R.drawable.selected; 169 | selectedDayTextColor = resources.getColor(R.color.white); 170 | currentDayOfMonth = 171 | resources.getColor(R.color.current_day_of_month); 172 | } 173 | 174 | // getAttributes 175 | @SuppressWarnings("deprecation") 176 | private void getAttributes(AttributeSet attrs) 177 | { 178 | Resources resources = getResources(); 179 | 180 | final TypedArray typedArray = 181 | context.obtainStyledAttributes(attrs, R.styleable 182 | .CustomCalendarView, 0, 0); 183 | calendarBackgroundColor = 184 | typedArray.getColor(R.styleable 185 | .CustomCalendarView_calendarBackgroundColor, 186 | resources.getColor(R.color.white)); 187 | calendarTitleBackgroundColor = 188 | typedArray.getColor(R.styleable 189 | .CustomCalendarView_titleLayoutBackgroundColor, 190 | resources.getColor(R.color.white)); 191 | calendarTitleTextColor = 192 | typedArray.getColor(R.styleable 193 | .CustomCalendarView_calendarTitleTextColor, 194 | resources.getColor(R.color.black)); 195 | weekLayoutBackgroundColor = 196 | typedArray.getColor(R.styleable 197 | .CustomCalendarView_weekLayoutBackgroundColor, 198 | resources.getColor(R.color.white)); 199 | dayOfWeekTextColor = 200 | typedArray.getColor(R.styleable 201 | .CustomCalendarView_dayOfWeekTextColor, 202 | resources.getColor(R.color.black)); 203 | dayOfMonthTextColor = 204 | typedArray.getColor(R.styleable 205 | .CustomCalendarView_dayOfMonthTextColor, 206 | resources.getColor(R.color.black)); 207 | disabledDayBackgroundColor = 208 | typedArray.getColor(R.styleable 209 | .CustomCalendarView_disabledDayBackgroundColor, 210 | resources 211 | .getColor(R.color 212 | .day_disabled_background_color)); 213 | disabledDayTextColor = 214 | typedArray.getColor(R.styleable 215 | .CustomCalendarView_disabledDayTextColor, 216 | resources 217 | .getColor(R.color.day_disabled_text_color)); 218 | selectedDayBackground = 219 | typedArray.getInteger(R.styleable 220 | .CustomCalendarView_selectedDayBackground, 221 | R.drawable.selected); 222 | selectedDayTextColor = 223 | typedArray.getColor(R.styleable 224 | .CustomCalendarView_selectedDayTextColor, 225 | resources.getColor(R.color.white)); 226 | currentDayOfMonth = 227 | typedArray.getColor(R.styleable 228 | .CustomCalendarView_currentDayOfMonthColor, 229 | resources 230 | .getColor(R.color.current_day_of_month)); 231 | typedArray.recycle(); 232 | } 233 | 234 | // initializeCalendar 235 | private void initializeCalendar() 236 | { 237 | View content = View.inflate(context, R.layout.calendar, this); 238 | 239 | View calendar = findViewById(R.id.calendar); 240 | if (calendar != null) 241 | calendar.setBackgroundColor(calendarBackgroundColor); 242 | 243 | ImageButton previous = findViewById(R.id.previous); 244 | if (previous != null) 245 | previous.setOnClickListener(v -> onSwipeRight()); 246 | 247 | ImageButton next = findViewById(R.id.next); 248 | if (next != null) 249 | next.setOnClickListener(v -> onSwipeLeft()); 250 | 251 | // Initialize calendar for current month 252 | Calendar currentCalendar = Calendar.getInstance(Locale.getDefault()); 253 | 254 | setFirstDayOfWeek(Calendar.MONDAY); 255 | refreshCalendar(currentCalendar); 256 | } 257 | 258 | // dispatchTouchEvent 259 | @Override 260 | public boolean dispatchTouchEvent(MotionEvent event) 261 | { 262 | gestureDetector.onTouchEvent(event); 263 | return super.dispatchTouchEvent(event); 264 | } 265 | 266 | // onSwipeLeft 267 | private void onSwipeLeft() 268 | { 269 | currentCalendar.add(Calendar.MONTH, 1); 270 | refreshCalendar(currentCalendar); 271 | 272 | if (calendarListener != null) 273 | calendarListener.onMonthChanged(currentCalendar); 274 | } 275 | 276 | // onSwipeRight 277 | private void onSwipeRight() 278 | { 279 | currentCalendar.add(Calendar.MONTH, -1); 280 | refreshCalendar(currentCalendar); 281 | 282 | if (calendarListener != null) 283 | calendarListener.onMonthChanged(currentCalendar); 284 | } 285 | 286 | // onSwipeDown 287 | private void onSwipeDown() 288 | { 289 | currentCalendar.add(Calendar.YEAR, 1); 290 | refreshCalendar(currentCalendar); 291 | 292 | if (calendarListener != null) 293 | calendarListener.onMonthChanged(currentCalendar); 294 | } 295 | 296 | // onSwipeUp 297 | private void onSwipeUp() 298 | { 299 | currentCalendar.add(Calendar.YEAR, -1); 300 | refreshCalendar(currentCalendar); 301 | 302 | if (calendarListener != null) 303 | calendarListener.onMonthChanged(currentCalendar); 304 | } 305 | 306 | // initializeTitleLayout 307 | private void initializeTitleLayout() 308 | { 309 | View titleLayout = findViewById(R.id.title); 310 | if (titleLayout != null) 311 | titleLayout.setBackgroundColor(calendarTitleBackgroundColor); 312 | 313 | final String monthTextArray[] = 314 | DateFormatSymbols.getInstance().getShortMonths(); 315 | String monthText = 316 | monthTextArray[currentCalendar.get(Calendar.MONTH)]; 317 | String titleFormat = 318 | getResources().getString(R.string.title_format); 319 | String titleText = String.format(titleFormat, monthText, 320 | currentCalendar.get(Calendar.YEAR)); 321 | TextView dateTitle = findViewById(R.id.month); 322 | if (dateTitle != null) 323 | { 324 | dateTitle.setTextColor(calendarTitleTextColor); 325 | dateTitle.setText(titleText); 326 | 327 | if (getCustomTypeface() != null) 328 | dateTitle.setTypeface(getCustomTypeface(), Typeface.BOLD); 329 | } 330 | } 331 | 332 | // initializeWeekLayout 333 | private void initializeWeekLayout() 334 | { 335 | TextView dayOfWeek; 336 | String dayOfTheWeekString; 337 | 338 | final String[] weekDaysArray = 339 | DateFormatSymbols.getInstance().getShortWeekdays(); 340 | for (int i = 1; i < weekDaysArray.length; i++) 341 | { 342 | dayOfTheWeekString = weekDaysArray[i]; 343 | 344 | dayOfWeek = findViewById(weekdays[getWeekIndex(i, currentCalendar) 345 | - 1]); 346 | if (dayOfWeek != null) 347 | dayOfWeek.setText(dayOfTheWeekString); 348 | } 349 | } 350 | 351 | // setDaysInCalendar 352 | private void setDaysInCalendar() 353 | { 354 | Calendar calendar = Calendar.getInstance(Locale.getDefault()); 355 | calendar.setTime(currentCalendar.getTime()); 356 | calendar.set(Calendar.DAY_OF_MONTH, 1); 357 | calendar.setFirstDayOfWeek(getFirstDayOfWeek()); 358 | int firstDayOfMonth = calendar.get(Calendar.DAY_OF_WEEK); 359 | 360 | // Calculate dayOfMonthIndex 361 | int dayOfMonthIndex = getWeekIndex(firstDayOfMonth, calendar); 362 | int actualMaximum = calendar.getActualMaximum(Calendar.DAY_OF_MONTH); 363 | 364 | final Calendar startCalendar = (Calendar) calendar.clone(); 365 | //Add required number of days 366 | startCalendar.add(Calendar.DATE, -(dayOfMonthIndex - 1)); 367 | int monthEndIndex = 368 | CALENDAR_DAYS - (actualMaximum + dayOfMonthIndex - 1); 369 | 370 | DayView dayView; 371 | for (int i = 1; i <= CALENDAR_DAYS; i++) 372 | { 373 | dayView = findViewById(days[i - 1]); 374 | if (dayView == null) 375 | continue; 376 | 377 | //Apply the default styles 378 | dayView.setOnClickListener(null); 379 | dayView.bind(startCalendar, getDecorators()); 380 | dayView.setVisibility(View.VISIBLE); 381 | 382 | if (CalendarUtils.isSameMonth(calendar, startCalendar)) 383 | { 384 | dayView.setOnClickListener(onDayOfMonthClickListener); 385 | dayView.setBackgroundColor(calendarBackgroundColor); 386 | dayView.setTextColor(dayOfWeekTextColor); 387 | //Set the current day color 388 | markDayAsCurrentDay(startCalendar); 389 | } 390 | else 391 | { 392 | dayView.setBackgroundColor(disabledDayBackgroundColor); 393 | dayView.setTextColor(disabledDayTextColor); 394 | 395 | if (!isOverflowDateVisible()) 396 | dayView.setVisibility(View.GONE); 397 | 398 | else if (i >= 36 && ((float) monthEndIndex / 7.0f) >= 1) 399 | { 400 | dayView.setVisibility(View.GONE); 401 | } 402 | } 403 | 404 | dayView.decorate(); 405 | 406 | 407 | startCalendar.add(Calendar.DATE, 1); 408 | dayOfMonthIndex++; 409 | } 410 | 411 | // If the last week row has no visible days, hide it or show 412 | // it in case 413 | ViewGroup weekRow = findViewById(weeks[5]); 414 | if (weekRow != null) 415 | { 416 | dayView = findViewById(days[35]); 417 | if (dayView != null) 418 | { 419 | if (dayView.getVisibility() != VISIBLE) 420 | weekRow.setVisibility(GONE); 421 | 422 | else 423 | weekRow.setVisibility(VISIBLE); 424 | } 425 | } 426 | } 427 | 428 | // clearDayOfTheMonthStyle 429 | private void clearDayOfTheMonthStyle(Date currentDate) 430 | { 431 | if (currentDate != null) 432 | { 433 | final Calendar calendar = getTodaysCalendar(); 434 | calendar.setFirstDayOfWeek(getFirstDayOfWeek()); 435 | calendar.setTime(currentDate); 436 | 437 | final DayView dayView = getDayOfMonthText(calendar); 438 | dayView.setBackgroundColor(calendarBackgroundColor); 439 | dayView.setTextColor(dayOfWeekTextColor); 440 | dayView.decorate(); 441 | } 442 | } 443 | 444 | // getDayOfMonthText 445 | private DayView getDayOfMonthText(Calendar currentCalendar) 446 | { 447 | return (DayView) getView(currentCalendar); 448 | } 449 | 450 | // getDayIndexByDate 451 | private int getDayIndexByDate(Calendar currentCalendar) 452 | { 453 | int monthOffset = getMonthOffset(currentCalendar); 454 | int currentDay = currentCalendar.get(Calendar.DAY_OF_MONTH); 455 | return currentDay + monthOffset; 456 | } 457 | 458 | // getMonthOffset 459 | private int getMonthOffset(Calendar currentCalendar) 460 | { 461 | final Calendar calendar = Calendar.getInstance(); 462 | calendar.setFirstDayOfWeek(getFirstDayOfWeek()); 463 | calendar.setTime(currentCalendar.getTime()); 464 | calendar.set(Calendar.DAY_OF_MONTH, 1); 465 | 466 | int firstDayWeekPosition = calendar.getFirstDayOfWeek(); 467 | int dayPosition = calendar.get(Calendar.DAY_OF_WEEK); 468 | 469 | if (firstDayWeekPosition == 1) 470 | return dayPosition - 1; 471 | 472 | else 473 | { 474 | if (dayPosition == 1) 475 | return 6; 476 | 477 | else 478 | return dayPosition - 2; 479 | } 480 | } 481 | 482 | // getWeekIndex 483 | private int getWeekIndex(int weekIndex, Calendar currentCalendar) 484 | { 485 | int firstDayWeekPosition = currentCalendar.getFirstDayOfWeek(); 486 | if (firstDayWeekPosition == 1) 487 | return weekIndex; 488 | 489 | else 490 | { 491 | 492 | if (weekIndex == 1) 493 | return 7; 494 | 495 | else 496 | return weekIndex - 1; 497 | } 498 | } 499 | 500 | // getView 501 | private View getView(Calendar currentCalendar) 502 | { 503 | int index = getDayIndexByDate(currentCalendar); 504 | return findViewById(days[index - 1]); 505 | } 506 | 507 | // getTodaysCalendar 508 | private Calendar getTodaysCalendar() 509 | { 510 | Calendar currentCalendar = Calendar.getInstance(Locale.getDefault()); 511 | currentCalendar.setFirstDayOfWeek(getFirstDayOfWeek()); 512 | return currentCalendar; 513 | } 514 | 515 | // refreshCalendar 516 | public void refreshCalendar(Calendar currentCalendar) 517 | { 518 | this.currentCalendar = currentCalendar; 519 | this.currentCalendar.setFirstDayOfWeek(getFirstDayOfWeek()); 520 | 521 | // Set date title 522 | initializeTitleLayout(); 523 | 524 | // Set weeks days titles 525 | initializeWeekLayout(); 526 | 527 | // Initialize and set days in calendar 528 | setDaysInCalendar(); 529 | } 530 | 531 | // getFirstDayOfWeek 532 | public int getFirstDayOfWeek() 533 | { 534 | return firstDayOfWeek; 535 | } 536 | 537 | // setFirstDayOfWeek 538 | public void setFirstDayOfWeek(int firstDayOfWeek) 539 | { 540 | this.firstDayOfWeek = firstDayOfWeek; 541 | } 542 | 543 | // markDayAsCurrentDay 544 | public void markDayAsCurrentDay(Calendar calendar) 545 | { 546 | if (calendar != null && CalendarUtils.isToday(calendar)) 547 | { 548 | DayView dayOfMonth = getDayOfMonthText(calendar); 549 | dayOfMonth.setTextColor(currentDayOfMonth); 550 | } 551 | } 552 | 553 | // markDayAsSelectedDay 554 | public void markDayAsSelectedDay(Date currentDate) 555 | { 556 | final Calendar currentCalendar = getTodaysCalendar(); 557 | currentCalendar.setFirstDayOfWeek(getFirstDayOfWeek()); 558 | currentCalendar.setTime(currentDate); 559 | 560 | // Clear previous marks 561 | clearDayOfTheMonthStyle(lastSelectedDay); 562 | 563 | // Store current values as last values 564 | storeLastValues(currentDate); 565 | 566 | // Mark current day as selected 567 | DayView view = getDayOfMonthText(currentCalendar); 568 | view.setBackgroundResource(selectedDayBackground); 569 | 570 | view.setTextColor(selectedDayTextColor); 571 | } 572 | 573 | // storeLastValues 574 | private void storeLastValues(Date currentDate) 575 | { 576 | lastSelectedDay = currentDate; 577 | } 578 | 579 | // setCalendarListener 580 | public void setCalendarListener(CalendarListener calendarListener) 581 | { 582 | this.calendarListener = calendarListener; 583 | } 584 | 585 | // getDecorators 586 | public List getDecorators() 587 | { 588 | return decorators; 589 | } 590 | 591 | // setDecorators 592 | public void setDecorators(List decorators) 593 | { 594 | this.decorators = decorators; 595 | } 596 | 597 | // isOverflowDateVisible 598 | public boolean isOverflowDateVisible() 599 | { 600 | return isOverflowDateVisible; 601 | } 602 | 603 | // setShowOverflowDate 604 | public void setShowOverflowDate(boolean isOverFlowEnabled) 605 | { 606 | isOverflowDateVisible = isOverFlowEnabled; 607 | } 608 | 609 | // getCustomTypeface 610 | public Typeface getCustomTypeface() 611 | { 612 | return customTypeface; 613 | } 614 | 615 | // setCustomTypeface 616 | public void setCustomTypeface(Typeface customTypeface) 617 | { 618 | this.customTypeface = customTypeface; 619 | } 620 | 621 | // getCurrentCalendar 622 | public Calendar getCurrentCalendar() 623 | { 624 | return currentCalendar; 625 | } 626 | 627 | // GestureListener 628 | private class GestureListener 629 | extends GestureDetector.SimpleOnGestureListener 630 | { 631 | private static final int SWIPE_THRESHOLD = 100; 632 | private static final int SWIPE_VELOCITY_THRESHOLD = 100; 633 | 634 | // onDown 635 | @Override 636 | public boolean onDown(MotionEvent e) 637 | { 638 | return true; 639 | } 640 | 641 | // onFling 642 | @Override 643 | public boolean onFling(MotionEvent e1, MotionEvent e2, 644 | float velocityX, float velocityY) 645 | { 646 | boolean result = false; 647 | 648 | try 649 | { 650 | float diffX = e2.getX() - e1.getX(); 651 | float diffY = e2.getY() - e1.getY(); 652 | if (Math.abs(diffX) > Math.abs(diffY)) 653 | { 654 | if (Math.abs(diffX) > SWIPE_THRESHOLD && 655 | Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) 656 | { 657 | if (diffX > 0) 658 | { 659 | onSwipeRight(); 660 | } 661 | else 662 | { 663 | onSwipeLeft(); 664 | } 665 | 666 | result = true; 667 | } 668 | } 669 | else 670 | { 671 | if (Math.abs(diffY) > SWIPE_THRESHOLD && 672 | Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) 673 | { 674 | if (diffY > 0) 675 | { 676 | onSwipeDown(); 677 | } 678 | else 679 | { 680 | onSwipeUp(); 681 | } 682 | 683 | result = true; 684 | } 685 | } 686 | } 687 | catch (Exception e) 688 | { 689 | } 690 | 691 | return result; 692 | } 693 | } 694 | } 695 | --------------------------------------------------------------------------------