Android中如何在CalendarView指定日期显示红绿标记点的实现方法
Absolutely! You can totally add custom red and green markers to specific dates in CalendarView—the catch is that the default Android CalendarView doesn’t support this out of the box, so you’ll need a bit of customization. Here are two solid approaches to make this work with your existing simpleCalendarView.xml and CalendarFragment setup:
CalendarView (No Third-Party Libraries) This method involves creating a subclass of CalendarView and overriding its drawing logic to render markers on target dates. It uses reflection to access the internal date grid, so keep in mind there might be minor compatibility quirks across Android versions.
Step 1: Create the Custom CalendarView Class
public class MarkedCalendarView extends CalendarView { private List<Long> redMarkDates = new ArrayList<>(); private List<Long> greenMarkDates = new ArrayList<>(); private Paint redPaint, greenPaint; private int markerRadius = 10; // Adjust this to change marker size public MarkedCalendarView(Context context) { super(context); initPaints(); } public MarkedCalendarView(Context context, AttributeSet attrs) { super(context, attrs); initPaints(); } public MarkedCalendarView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initPaints(); } private void initPaints() { // Set up red marker paint redPaint = new Paint(); redPaint.setColor(Color.RED); redPaint.setStyle(Paint.Style.FILL); redPaint.setAntiAlias(true); // Set up green marker paint greenPaint = new Paint(); greenPaint.setColor(Color.GREEN); greenPaint.setStyle(Paint.Style.FILL); greenPaint.setAntiAlias(true); } // Call this to set dates that need red markers (pass millisecond timestamps) public void setRedMarkDates(List<Long> dates) { redMarkDates.clear(); if (dates != null) redMarkDates.addAll(dates); invalidate(); // Refresh the view to show new markers } // Call this to set dates that need green markers public void setGreenMarkDates(List<Long> dates) { greenMarkDates.clear(); if (dates != null) greenMarkDates.addAll(dates); invalidate(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); try { // Access the internal GridView that holds date cells via reflection Field gridField = CalendarView.class.getDeclaredField("mMonthGridView"); gridField.setAccessible(true); GridView dateGrid = (GridView) gridField.get(this); if (dateGrid == null) return; Adapter dateAdapter = dateGrid.getAdapter(); if (dateAdapter == null) return; // Loop through each date cell to check if it needs a marker for (int i = 0; i < dateAdapter.getCount(); i++) { View dateCell = dateAdapter.getView(i, null, dateGrid); if (dateCell == null) continue; long dateMillis = (long) dateAdapter.getItem(i); boolean needsRedMarker = redMarkDates.contains(dateMillis); boolean needsGreenMarker = greenMarkDates.contains(dateMillis); if (needsRedMarker || needsGreenMarker) { // Calculate marker position (centered below the date text) int[] cellLocation = new int[2]; dateCell.getLocationOnScreen(cellLocation); float centerX = cellLocation[0] + dateCell.getWidth() / 2f; float centerY = cellLocation[1] + dateCell.getHeight() - markerRadius - 8; // 8 = spacing from cell bottom // Draw the marker if (needsRedMarker) { canvas.drawCircle(centerX, centerY, markerRadius, redPaint); } if (needsGreenMarker) { canvas.drawCircle(centerX, centerY, markerRadius, greenPaint); } } } } catch (Exception e) { e.printStackTrace(); } } }
Step 2: Update Your Layout File
Replace the default CalendarView in simpleCalendarView.xml with your custom class (make sure to use your actual package name):
<com.your.package.name.MarkedCalendarView android:id="@+id/calendarView" android:layout_width="match_parent" android:layout_height="match_parent" android:dateTextAppearance="@style/YourCalendarDateStyle" android:weekDayTextAppearance="@style/YourCalendarWeekStyle" />
Step 3: Configure Markers in CalendarFragment
public class CalendarFragment extends Fragment { private MarkedCalendarView calendarView; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.simpleCalendarView, container, false); calendarView = view.findViewById(R.id.calendarView); // Prepare your target dates (example: add markers to specific days) List<Long> redMarkedDates = new ArrayList<>(); List<Long> greenMarkedDates = new ArrayList<>(); // Add a red marker for 2 days from today Calendar redDate = Calendar.getInstance(); redDate.add(Calendar.DAY_OF_MONTH, 2); redMarkedDates.add(redDate.getTimeInMillis()); // Add a green marker for 5 days from today Calendar greenDate = Calendar.getInstance(); greenDate.add(Calendar.DAY_OF_MONTH, 5); greenMarkedDates.add(greenDate.getTimeInMillis()); // Apply markers to the calendar calendarView.setRedMarkDates(redMarkedDates); calendarView.setGreenMarkDates(greenMarkedDates); // Keep your existing date click listener calendarView.setOnDateChangeListener((view1, year, month, dayOfMonth) -> { // Your existing click logic here Toast.makeText(getContext(), "Selected: " + year + "/" + (month+1) + "/" + dayOfMonth, Toast.LENGTH_SHORT).show(); }); return view; } }
If you want to avoid reflection and get access to more built-in features, use a library like MaterialCalendarView. It natively supports date markers via decorators.
Step 1: Add the Library Dependency
Add this to your app-level build.gradle file:
dependencies { implementation 'com.prolificinteractive:material-calendarview:1.4.3' }
Step 2: Update Your Layout
Replace the default CalendarView with MaterialCalendarView:
<com.prolificinteractive.materialcalendarview.MaterialCalendarView android:id="@+id/materialCalendarView" android:layout_width="match_parent" android:layout_height="match_parent" />
Step 3: Set Up Markers in CalendarFragment
public class CalendarFragment extends Fragment { private MaterialCalendarView calendarView; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.simpleCalendarView, container, false); calendarView = view.findViewById(R.id.materialCalendarView); // Add red and green markers using custom decorators calendarView.addDecorator(new DateMarkerDecorator(Color.RED, getRedMarkedDates())); calendarView.addDecorator(new DateMarkerDecorator(Color.GREEN, getGreenMarkedDates())); // Set up date click listener calendarView.setOnDateChangedListener((widget, date, selected) -> { // Your click logic here Toast.makeText(getContext(), "Selected: " + date.getYear() + "/" + (date.getMonth()+1) + "/" + date.getDay(), Toast.LENGTH_SHORT).show(); }); return view; } // Helper method to get dates for red markers private List<CalendarDay> getRedMarkedDates() { List<CalendarDay> dates = new ArrayList<>(); dates.add(CalendarDay.from(2024, Calendar.MAY, 20)); // Example date return dates; } // Helper method to get dates for green markers private List<CalendarDay> getGreenMarkedDates() { List<CalendarDay> dates = new ArrayList<>(); dates.add(CalendarDay.from(2024, Calendar.MAY, 25)); // Example date return dates; } // Custom decorator class to draw markers private static class DateMarkerDecorator implements DayViewDecorator { private final int markerColor; private final HashSet<CalendarDay> targetDates; public DateMarkerDecorator(int color, Collection<CalendarDay> dates) { this.markerColor = color; this.targetDates = new HashSet<>(dates); } @Override public boolean shouldDecorate(CalendarDay day) { return targetDates.contains(day); } @Override public void decorate(DayViewFacade view) { // Draw a dot below the date text (adjust radius as needed) view.addSpan(new DotSpan(10, markerColor)); } } }
Which Approach Should You Choose?
- Use Approach 1 if you want to stick to native components and avoid third-party dependencies.
- Use Approach 2 if you want a more stable, low-maintenance solution with extra features like range selection, custom date formatting, etc.
内容的提问来源于stack exchange,提问作者Sudha Kumari

