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
167 changes: 166 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,52 @@ private enum AutoScrollDirection {
private Paint mTodayHeaderTextPaint;
private Paint mEventBackgroundPaint;
private Paint mNewEventBackgroundPaint;
private TextPaint mTagTextPaint;
private Paint mTagBackgroundPaint;
private Xfermode mXfermode;
private int mTagSize = 96;
private int mTagSpacing = 12;
private int mTagCornerRadius = 12;
private int mTagTextSize = 12;

private static final Map<String, Integer> TAG_ICON_MAP = new HashMap<String, Integer>() {{
put("EXCLAMATION", R.drawable.tag_exclamation);
put("BEER", R.drawable.tag_beer);
put("X", R.drawable.tag_x);
put("BED", R.drawable.tag_bed);
put("CHECK", R.drawable.tag_check);
}};

public void setTagSize(int sizePx) {
mTagSize = sizePx;
invalidate();
}
public void setTagSpacing(int spacingPx) {
mTagSpacing = spacingPx;
invalidate();
}
public int getTagSize() {
return mTagSize;
}
public int getTagpacing() {
return mTagSpacing;
}
public int getTagCornerRadius() {
return mTagCornerRadius;
}
public int getTagTextSize() {
return mTagTextSize;
}
public void setTagTextSize(int tagTextSize) {
mTagTextSize = tagTextSize;
mTagTextPaint.setTextSize(mTagTextSize);
invalidate();
}
public void setTagCornerRadius(int tagCornerRadius) {
mTagCornerRadius = tagCornerRadius;
invalidate();
}

private float mHeaderColumnWidth;
private List<EventRect> mEventRects;
private List<WeekViewEvent> mEvents;
Expand Down Expand Up @@ -561,6 +608,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(mTagTextSize);
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 +1215,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 = mTagSize + mTagSpacing;
}

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 +1255,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 - mTagSize - mTagSpacing);
}
}

// 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 + mTagSpacing;
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 + mTagSize), (int) (bottomY + mTagSize));
icon.draw(canvas);
startX += mTagSize + mTagSpacing;
} 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 + (mTagSize - textLayout.getHeight()) / 2;
canvas.translate(startX, textY);
textLayout.draw(canvas);
canvas.restore();

startX += textWidth + mTagSpacing;

} else {
// --- STANDARD TEXT PATH (PUNCH-OUT EFFECT) ---
float backgroundHeight = mTagSize;
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 + mTagSpacing;
}
}
}
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
10 changes: 10 additions & 0 deletions library/src/main/res/drawable/tag_bed.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="15dp"
android:height="15dp"
android:viewportWidth="14.45"
android:viewportHeight="14.45">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M12.28 0c0.57 0 1.12 0.23 1.53 0.63 0.4 0.41 0.64 0.96 0.64 1.54v10.1c0 0.58-0.23 1.13-0.64 1.54-0.4 0.4-0.96 0.64-1.53 0.64H2.17c-0.58 0-1.13-0.23-1.54-0.64C0.23 13.41 0 12.85 0 12.28V2.17c0-0.58 0.23-1.13 0.63-1.54C1.04 0.23 1.6 0 2.17 0h10.1ZM3 5.56c-0.28 0-0.5 0.22-0.5 0.5V9.8c0 0.27 0.22 0.5 0.5 0.5s0.5-0.23 0.5-0.5V9.06h7.33V9.8c0 0.27 0.23 0.5 0.5 0.5 0.28 0 0.5-0.23 0.5-0.5V7.72c0-0.46-0.18-0.9-0.51-1.23-0.33-0.33-0.77-0.52-1.24-0.52H7.17c-0.28 0-0.5 0.23-0.5 0.5v1.59H3.5v-2c0-0.28-0.22-0.5-0.5-0.5Zm7.08 1.41c0.2 0 0.4 0.08 0.53 0.22 0.14 0.14 0.22 0.33 0.22 0.53v0.34H7.67V6.97h2.41Zm-5-1.83c-0.35 0-0.69 0.14-0.94 0.39S3.75 6.12 3.75 6.47c0 0.36 0.14 0.7 0.4 0.95C4.38 7.67 4.72 7.8 5.07 7.8c0.36 0 0.7-0.14 0.95-0.4 0.25-0.24 0.39-0.58 0.39-0.94 0-0.35-0.14-0.69-0.4-0.94-0.24-0.25-0.58-0.39-0.94-0.39Zm0 1c0.1 0 0.18 0.03 0.24 0.1 0.06 0.06 0.1 0.14 0.1 0.23 0 0.1-0.04 0.18-0.1 0.24-0.06 0.06-0.15 0.1-0.24 0.1S4.91 6.77 4.85 6.7c-0.06-0.06-0.1-0.15-0.1-0.24s0.04-0.17 0.1-0.23c0.06-0.07 0.14-0.1 0.23-0.1Z"/>
</vector>
10 changes: 10 additions & 0 deletions library/src/main/res/drawable/tag_beer.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="15dp"
android:height="15dp"
android:viewportWidth="14.45"
android:viewportHeight="14.45">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M12.28 0c0.57 0 1.12 0.23 1.53 0.63 0.4 0.41 0.64 0.96 0.64 1.54v10.1c0 0.58-0.23 1.13-0.64 1.54-0.4 0.4-0.96 0.64-1.53 0.64H2.17c-0.58 0-1.13-0.23-1.54-0.64C0.23 13.41 0 12.85 0 12.28V2.17c0-0.58 0.23-1.13 0.63-1.54C1.04 0.23 1.6 0 2.17 0h10.1ZM4.93 2.64H4.86c-0.23 0.02-0.44 0.12-0.6 0.3C4.1 3.1 4 3.31 4 3.55v1.83 0.16c0.03 0.6 0.15 1.08 0.43 1.7l0.14 0.32 0.07 0.15c0.18 0.48 0.28 0.99 0.28 1.5v1.67 0.07c0.02 0.23 0.12 0.44 0.3 0.6 0.16 0.16 0.39 0.25 0.62 0.25H8.6h0.07c0.23-0.02 0.44-0.13 0.6-0.3 0.16-0.17 0.24-0.39 0.24-0.62V9.23 9.05c0.03-0.5 0.15-1.01 0.35-1.48L10 7.25l0.08-0.17c0.25-0.6 0.35-1.08 0.35-1.69V3.56 3.49c-0.02-0.23-0.12-0.45-0.3-0.6-0.16-0.16-0.38-0.25-0.62-0.25H4.93Zm4.58 0.92v1.37H4.93V3.56h4.58Z"/>
</vector>
10 changes: 10 additions & 0 deletions library/src/main/res/drawable/tag_check.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="15dp"
android:height="15dp"
android:viewportWidth="14.45"
android:viewportHeight="14.45">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M12.28 0c0.57 0 1.12 0.23 1.53 0.63 0.4 0.41 0.64 0.96 0.64 1.54v10.1c0 0.58-0.23 1.13-0.64 1.54-0.4 0.4-0.96 0.64-1.53 0.64H2.17c-0.58 0-1.13-0.23-1.54-0.64C0.23 13.41 0 12.85 0 12.28V2.17c0-0.58 0.23-1.13 0.63-1.54C1.04 0.23 1.6 0 2.17 0h10.1Zm-0.96 3.8c-0.3-0.27-0.75-0.25-1.02 0.05L5.58 9.04l-0.45-0.5-0.98-1.08C3.88 7.16 3.42 7.14 3.13 7.4 2.83 7.68 2.8 8.14 3.08 8.43l0.98 1.08 0.99 1.09c0.13 0.15 0.33 0.23 0.53 0.23s0.4-0.08 0.54-0.23l5.25-5.78c0.27-0.3 0.24-0.75-0.05-1.02Z"/>
</vector>
10 changes: 10 additions & 0 deletions library/src/main/res/drawable/tag_exclamation.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="15dp"
android:height="15dp"
android:viewportWidth="14.45"
android:viewportHeight="14.45">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M12.28 0c0.57 0 1.12 0.23 1.53 0.63 0.4 0.41 0.64 0.96 0.64 1.54v10.1c0 0.58-0.23 1.13-0.64 1.54-0.4 0.4-0.96 0.64-1.53 0.64H2.17c-0.58 0-1.13-0.23-1.54-0.64C0.23 13.41 0 12.85 0 12.28V2.17c0-0.58 0.23-1.13 0.63-1.54C1.04 0.23 1.6 0 2.17 0h10.1ZM7.22 10.6c-0.4 0-0.72 0.32-0.72 0.72 0 0.4 0.32 0.72 0.72 0.72 0.4 0 0.72-0.32 0.73-0.72 0-0.4-0.33-0.73-0.73-0.73Zm0-8.43c-0.4 0-0.72 0.32-0.72 0.72v6.02c0 0.4 0.32 0.72 0.72 0.72 0.4 0 0.73-0.32 0.73-0.72V2.89c0-0.4-0.33-0.72-0.73-0.72Z"/>
</vector>
10 changes: 10 additions & 0 deletions library/src/main/res/drawable/tag_x.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="15dp"
android:height="15dp"
android:viewportWidth="14.45"
android:viewportHeight="14.45">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M12.28 0c0.57 0 1.12 0.23 1.53 0.63 0.4 0.41 0.64 0.96 0.64 1.54v10.1c0 0.58-0.23 1.13-0.64 1.54-0.4 0.4-0.96 0.64-1.53 0.64H2.17c-0.58 0-1.13-0.23-1.54-0.64C0.23 13.41 0 12.85 0 12.28V2.17c0-0.58 0.23-1.13 0.63-1.54C1.04 0.23 1.6 0 2.17 0h10.1Zm-2.05 4.21c-0.28-0.28-0.74-0.28-1.02 0l-1.99 2-1.99-2c-0.28-0.28-0.74-0.28-1.02 0s-0.28 0.74 0 1.02l2 2-2 1.98c-0.28 0.28-0.28 0.74 0 1.02 0.28 0.29 0.74 0.29 1.02 0l2-1.99 1.98 2c0.28 0.28 0.74 0.28 1.02 0 0.29-0.29 0.29-0.75 0-1.03L8.24 7.22l2-1.99c0.28-0.28 0.28-0.74 0-1.02Z"/>
</vector>