Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 153 additions & 1 deletion library/src/main/java/com/alamkanak/weekview/WeekView.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import android.graphics.*;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import androidx.core.graphics.drawable.DrawableCompat;
import android.os.Build;

import androidx.annotation.NonNull;
Expand Down Expand Up @@ -79,6 +80,39 @@ private enum AutoScrollDirection {
private Paint mTodayHeaderTextPaint;
private Paint mEventBackgroundPaint;
private Paint mNewEventBackgroundPaint;
private TextPaint mTagTextPaint;
private Paint mTagBackgroundPaint;
private Xfermode mXfermode;
private int mTagIconSize = 96;
private int mTagIconSpacing = 12;
private int mTagCornerRadius = 12;

private static final Map<String, Integer> TAG_ICON_MAP = new HashMap<String, Integer>() {{
put("meeting", R.drawable.tag_meeting);
put("birthday", R.drawable.tag_birthday);
put("deadline", R.drawable.tag_deadline);
}};

public void setTagIconSize(int sizePx) {
mTagIconSize = sizePx;
}
public void setTagIconSpacing(int spacingPx) {
mTagIconSpacing = spacingPx;
}
public int getTagIconSize() {
return mTagIconSize;
}
public int getTagIconSpacing() {
return mTagIconSpacing;
}
public int getTagCornerRadius() {
return mTagCornerRadius;
}
public void setTagCornerRadius(int tagCornerRadius) {
mTagCornerRadius = tagCornerRadius;
}


private float mHeaderColumnWidth;
private List<EventRect> mEventRects;
private List<WeekViewEvent> mEvents;
Expand Down Expand Up @@ -561,6 +595,21 @@ private void init() {
// Set default empty event color.
mNewEventColor = Color.parseColor("#3c93d9");

// Initialize paint for tags
mTagBackgroundPaint = new Paint();
mTagBackgroundPaint.setColor(Color.WHITE);
mTagBackgroundPaint.setStyle(Paint.Style.FILL);

mTagTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
mTagTextPaint.setColor(Color.BLACK); // This color will be used to "punch out" the background
mTagTextPaint.setTextSize(mEventTextSize);
mTagTextPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
// Copy other relevant properties from mEventTextPaint if needed
// mTagTextPaint.setTypeface(mEventTextPaint.getTypeface());

// This Xfermode will create the "punch-out" effect
mXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);

mScaleDetector = new ScaleGestureDetector(mContext, new WeekViewGestureListener());
}

Expand Down Expand Up @@ -1153,7 +1202,14 @@ private void drawEventTitle(WeekViewEvent event, RectF rect, Canvas canvas, floa
bob.append(event.getLocation());
}

int availableHeight = (int) (rect.bottom - originalTop - mEventPadding * 2);
// Reserve space for tag icons
int iconRowHeight = 0;
List<String> tags = event.getTags();
if (tags != null && !tags.isEmpty()) {
iconRowHeight = mTagIconSize + mTagIconSpacing;
}

int availableHeight = (int) (rect.bottom - originalTop - mEventPadding * 2 - iconRowHeight);
int availableWidth = (int) (rect.right - originalLeft - mEventPadding * 2);

// Get text color if necessary
Expand Down Expand Up @@ -1186,6 +1242,102 @@ private void drawEventTitle(WeekViewEvent event, RectF rect, Canvas canvas, floa
canvas.restore();
}
}

// Draw tag icons row
if (tags != null && !tags.isEmpty()) {
drawTags(tags, rect, canvas, originalLeft, rect.bottom - mTagIconSize - mTagIconSpacing);
}
}

// Helper to get drawable for a tag
private Drawable getTagIconDrawable(String tag) {
Integer resId = TAG_ICON_MAP.get(tag);
if (resId == null) return null;
try {
return getResources().getDrawable(resId);
} catch (Exception e) {
return null;
}
}

// Draw tags
private void drawTags(List<String> tags, RectF rect, Canvas canvas, float left, float bottomY) {
canvas.save();
canvas.clipRect(rect);
float startX = left + mTagIconSpacing;
for (int i = 0; i < tags.size() - 1; i = i + 2) {
String tag = tags.get(i);
String color = tags.get(i + 1);
Drawable icon = getTagIconDrawable(tag);
if (icon != null) {
DrawableCompat.setTint(icon, Color.parseColor(color));
icon.setBounds((int) (startX), (int) bottomY, (int) (startX + mTagIconSize), (int) (bottomY + mTagIconSize));
icon.draw(canvas);
startX += mTagIconSize + mTagIconSpacing;
} else if (!TextUtils.isEmpty(tag)) {
// Check if the string is only emojis
boolean isOnlyEmoji = tag.matches("^[\\p{IsEmoji_Presentation}\\p{IsEmoji_Modifier_Base}\\p{IsEmoji_Component}\\u200d\\uFE0F]+$") && !tag.matches(".*\\d.*");

// Measure the text
float textWidth = mTagTextPaint.measureText(tag);
float tagPadding = mEventPadding / 2;

if (isOnlyEmoji) {
// --- EMOJI ONLY PATH ---
// Just draw the emoji in its original colors, no background
mTagTextPaint.setXfermode(null); // Ensure no Xfermode is active

StaticLayout textLayout = StaticLayout.Builder.obtain(tag, 0, tag.length(), mTagTextPaint, (int) Math.ceil(textWidth))
.setAlignment(Layout.Alignment.ALIGN_NORMAL)
.setIncludePad(false)
.build();

canvas.save();
// Align emoji vertically with where the icon/background would be
float textY = bottomY + (mTagIconSize - textLayout.getHeight()) / 2;
canvas.translate(startX, textY);
textLayout.draw(canvas);
canvas.restore();

startX += textWidth + mTagIconSpacing;

} else {
// --- STANDARD TEXT PATH (PUNCH-OUT EFFECT) ---
float backgroundHeight = mTagIconSize;
float backgroundWidth = textWidth + tagPadding * 2;
RectF backgroundRect = new RectF(startX, bottomY, startX + backgroundWidth, bottomY + backgroundHeight);

// Save layer for Xfermode composition
int saveCount = canvas.saveLayer(backgroundRect, null);

// 1. Draw the solid background
mTagBackgroundPaint.setColor(Color.parseColor(color));
canvas.drawRoundRect(backgroundRect, mTagCornerRadius, mTagCornerRadius, mTagBackgroundPaint);

// 2. Set Xfermode to DST_OUT to punch out the text
mTagTextPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));

StaticLayout textLayout = StaticLayout.Builder.obtain(tag, 0, tag.length(), mTagTextPaint, (int) Math.ceil(textWidth))
.setAlignment(Layout.Alignment.ALIGN_NORMAL)
.setIncludePad(false)
.build();

canvas.save();
float textY = backgroundRect.top + (backgroundHeight - textLayout.getHeight()) / 2;
float textX = backgroundRect.left + tagPadding;
canvas.translate(textX, textY);
textLayout.draw(canvas);
canvas.restore();

// 3. Cleanup
mTagTextPaint.setXfermode(null);
canvas.restoreToCount(saveCount);

startX += backgroundWidth + mTagIconSpacing;
}
}
}
canvas.restore();
}

/**
Expand Down
11 changes: 11 additions & 0 deletions library/src/main/java/com/alamkanak/weekview/WeekViewEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ public class WeekViewEvent {
int mColor;
private boolean mAllDay;
private Shader mShader;
private List<String> mTags = new ArrayList<>();

public WeekViewEvent() {

}


/**
* Initializes the event for week view.
*
Expand Down Expand Up @@ -220,6 +222,15 @@ public void setEndTime(Calendar endTime) {
this.mEndTime = endTime;
}

public List<String> getTags() {
return mTags;
}

public void setTags(List<String> tags) {
this.mTags = tags != null ? tags : new ArrayList<>();
}


public String getName() {
return mName;
}
Expand Down
9 changes: 9 additions & 0 deletions library/src/main/res/drawable/tag_birthday.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
android:viewportWidth="15"
android:viewportHeight="15"
android:width="15dp"
android:height="15dp">
<path
android:pathData="M12.2783 0C12.8529 0.0000287846 13.4042 0.228462 13.8105 0.634766C14.2169 1.04107 14.4453 1.5924 14.4453 2.16699V12.2783C14.4453 12.8529 14.2169 13.4042 13.8105 13.8105C13.4042 14.2169 12.8529 14.4453 12.2783 14.4453H2.16699C1.5924 14.4453 1.04107 14.2169 0.634766 13.8105C0.228462 13.4042 0.0000287846 12.8529 0 12.2783V2.16699C0.0000287214 1.5924 0.228462 1.04107 0.634766 0.634766C1.04107 0.228462 1.5924 0.0000287214 2.16699 0H12.2783ZM4.93066 2.63965L4.8623 2.6416C4.63169 2.65895 4.41604 2.76304 4.25879 2.93262C4.10165 3.10211 4.01465 3.32454 4.01465 3.55566V5.38965L4.0166 5.55371C4.03589 6.14378 4.16376 6.62592 4.44043 7.24805L4.58301 7.56836L4.64844 7.72461C4.83488 8.19654 4.93063 8.70889 4.93066 9.22754V10.8896L4.93359 10.958C4.95094 11.1886 5.05412 11.4043 5.22363 11.5615C5.39321 11.7188 5.61639 11.8057 5.84766 11.8057H8.59766L8.66602 11.8037C8.89663 11.7864 9.11228 11.6823 9.26953 11.5127C9.42668 11.3432 9.51463 11.1208 9.51465 10.8896V9.22754L9.51758 9.05469C9.53793 8.54229 9.65525 8.03848 9.8623 7.56934L10.0039 7.24805L10.0762 7.08105C10.3258 6.48276 10.4306 6.00122 10.4307 5.38965V3.55566L10.4287 3.4873C10.4114 3.25669 10.3073 3.04104 10.1377 2.88379C9.96824 2.72667 9.74572 2.6397 9.51465 2.63965H4.93066ZM9.51465 3.55566V4.93066H4.93066V3.55566H9.51465Z"
android:fillColor="#ffffff" />
</vector>
9 changes: 9 additions & 0 deletions library/src/main/res/drawable/tag_deadline.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
android:viewportWidth="15"
android:viewportHeight="15"
android:width="15dp"
android:height="15dp">
<path
android:pathData="M12.2783 0C12.8529 0.0000287846 13.4042 0.228462 13.8105 0.634766C14.2169 1.04107 14.4453 1.5924 14.4453 2.16699V12.2783C14.4453 12.8529 14.2169 13.4042 13.8105 13.8105C13.4042 14.2169 12.8529 14.4453 12.2783 14.4453H2.16699C1.5924 14.4453 1.04107 14.2169 0.634766 13.8105C0.228462 13.4042 0.0000287846 12.8529 0 12.2783V2.16699C0.0000287214 1.5924 0.228462 1.04107 0.634766 0.634766C1.04107 0.228462 1.5924 0.0000287214 2.16699 0H12.2783ZM4.93066 2.63965L4.8623 2.6416C4.63169 2.65895 4.41604 2.76304 4.25879 2.93262C4.10165 3.10211 4.01465 3.32454 4.01465 3.55566V5.38965L4.0166 5.55371C4.03589 6.14378 4.16376 6.62592 4.44043 7.24805L4.58301 7.56836L4.64844 7.72461C4.83488 8.19654 4.93063 8.70889 4.93066 9.22754V10.8896L4.93359 10.958C4.95094 11.1886 5.05412 11.4043 5.22363 11.5615C5.39321 11.7188 5.61639 11.8057 5.84766 11.8057H8.59766L8.66602 11.8037C8.89663 11.7864 9.11228 11.6823 9.26953 11.5127C9.42668 11.3432 9.51463 11.1208 9.51465 10.8896V9.22754L9.51758 9.05469C9.53793 8.54229 9.65525 8.03848 9.8623 7.56934L10.0039 7.24805L10.0762 7.08105C10.3258 6.48276 10.4306 6.00122 10.4307 5.38965V3.55566L10.4287 3.4873C10.4114 3.25669 10.3073 3.04104 10.1377 2.88379C9.96824 2.72667 9.74572 2.6397 9.51465 2.63965H4.93066ZM9.51465 3.55566V4.93066H4.93066V3.55566H9.51465Z"
android:fillColor="#ffffff" />
</vector>
9 changes: 9 additions & 0 deletions library/src/main/res/drawable/tag_meeting.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
android:viewportWidth="15"
android:viewportHeight="15"
android:width="15dp"
android:height="15dp">
<path
android:pathData="M12.2783 0C12.8529 0.0000287846 13.4042 0.228462 13.8105 0.634766C14.2169 1.04107 14.4453 1.5924 14.4453 2.16699V12.2783C14.4453 12.8529 14.2169 13.4042 13.8105 13.8105C13.4042 14.2169 12.8529 14.4453 12.2783 14.4453H2.16699C1.5924 14.4453 1.04107 14.2169 0.634766 13.8105C0.228462 13.4042 0.0000287846 12.8529 0 12.2783V2.16699C0.0000287214 1.5924 0.228462 1.04107 0.634766 0.634766C1.04107 0.228462 1.5924 0.0000287214 2.16699 0H12.2783ZM4.93066 2.63965L4.8623 2.6416C4.63169 2.65895 4.41604 2.76304 4.25879 2.93262C4.10165 3.10211 4.01465 3.32454 4.01465 3.55566V5.38965L4.0166 5.55371C4.03589 6.14378 4.16376 6.62592 4.44043 7.24805L4.58301 7.56836L4.64844 7.72461C4.83488 8.19654 4.93063 8.70889 4.93066 9.22754V10.8896L4.93359 10.958C4.95094 11.1886 5.05412 11.4043 5.22363 11.5615C5.39321 11.7188 5.61639 11.8057 5.84766 11.8057H8.59766L8.66602 11.8037C8.89663 11.7864 9.11228 11.6823 9.26953 11.5127C9.42668 11.3432 9.51463 11.1208 9.51465 10.8896V9.22754L9.51758 9.05469C9.53793 8.54229 9.65525 8.03848 9.8623 7.56934L10.0039 7.24805L10.0762 7.08105C10.3258 6.48276 10.4306 6.00122 10.4307 5.38965V3.55566L10.4287 3.4873C10.4114 3.25669 10.3073 3.04104 10.1377 2.88379C9.96824 2.72667 9.74572 2.6397 9.51465 2.63965H4.93066ZM9.51465 3.55566V4.93066H4.93066V3.55566H9.51465Z"
android:fillColor="#ffffff" />
</vector>