Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ellipsize in drawEventTitle for text containing line breaks #123

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
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
4 changes: 3 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
buildscript {
repositories {
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
classpath 'com.android.tools.build:gradle:3.5.3'

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
Expand All @@ -19,5 +20,6 @@ allprojects {

repositories {
jcenter()
google()
}
}
4 changes: 2 additions & 2 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#Sun Aug 06 18:02:35 CEST 2017
#Sat Feb 01 16:17:02 CET 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
12 changes: 6 additions & 6 deletions library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,21 @@ repositories {
}

android {
compileSdkVersion 25
buildToolsVersion '25.0.2'
compileSdkVersion 28
buildToolsVersion '28.0.3'

defaultConfig {
minSdkVersion 9
targetSdkVersion 25
minSdkVersion 14
targetSdkVersion 28
}
}

configurations {
javadocDeps
}
dependencies {
compile 'com.android.support:appcompat-v7:25.1.0'
javadocDeps 'com.android.support:appcompat-v7:25.1.0'
implementation 'com.android.support:appcompat-v7:28.0.0'
javadocDeps 'com.android.support:appcompat-v7:28.0.0'
}

apply from: 'gradle-mvn-push.gradle'
165 changes: 133 additions & 32 deletions library/src/main/java/com/alamkanak/weekview/WeekView.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.*;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
Expand All @@ -11,18 +18,37 @@
import android.support.v4.view.GestureDetectorCompat;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.animation.FastOutLinearInInterpolator;
import android.text.*;
import android.text.Layout;
import android.text.SpannableStringBuilder;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.text.style.StyleSpan;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.*;
import android.view.DragEvent;
import android.view.GestureDetector;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.SoundEffectConstants;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.OverScroller;

import java.text.SimpleDateFormat;
import java.util.*;

import static com.alamkanak.weekview.WeekViewUtil.*;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;

import static com.alamkanak.weekview.WeekViewUtil.daysBetween;
import static com.alamkanak.weekview.WeekViewUtil.getPassedMinutesInDay;
import static com.alamkanak.weekview.WeekViewUtil.isSameDay;
import static com.alamkanak.weekview.WeekViewUtil.today;

/**
* Created by Raquib-ul-Alam Kanak on 7/21/2014.
Expand All @@ -38,6 +64,12 @@ private enum Direction {
public static final int LENGTH_SHORT = 1;
@Deprecated
public static final int LENGTH_LONG = 2;

public static final int ADDITIONAL_INFO_NONE = 0;
public static final int ADDITIONAL_INFO_YEAR = 1;
public static final int ADDITIONAL_INFO_MONTH = 2;
public static final int ADDITIONAL_INFO_WEEK = 3;

private final Context mContext;
private Calendar mHomeDate;
private Calendar mMinDate;
Expand Down Expand Up @@ -147,6 +179,7 @@ private enum Direction {
private boolean mAutoLimitTime = false;
private boolean mEnableDropListener = false;
private int mMinOverlappingMinutes = 0;
private int mAdditionalTimeInfo = ADDITIONAL_INFO_NONE;

// Listeners.
private EventClickListener mEventClickListener;
Expand Down Expand Up @@ -350,7 +383,7 @@ left < getWidth() &&
top < getHeight() &&
right > mHeaderColumnWidth &&
bottom > 0
) {
) {
RectF dayRectF = new RectF(left, top, right, bottom - mCurrentOrigin.y);
newEvent.setColor(mNewEventColor);
mNewEventRect = new EventRect(newEvent, newEvent, dayRectF);
Expand Down Expand Up @@ -709,7 +742,7 @@ private void drawTimeColumnAndAxes(Canvas canvas) {
canvas.save();
canvas.clipRect(0, mHeaderHeight + mHeaderRowPadding * 2, mHeaderColumnWidth, getHeight());
canvas.restore();

for (int i = 0; i < getNumberOfPeriods(); i++) {
// If we are showing half hours (eg. 5:30am), space the times out by half the hour height
// and need to provide 30 minutes on each odd period, otherwise, minutes is always 0.
Expand Down Expand Up @@ -929,7 +962,7 @@ else if (mNewHourHeight > mMaxHourHeight)
canvas.clipRect(0, 0, mTimeTextWidth + mHeaderColumnPadding * 2, mHeaderHeight + mHeaderRowPadding * 2);
canvas.drawRect(0, 0, mTimeTextWidth + mHeaderColumnPadding * 2, mHeaderHeight + mHeaderRowPadding * 2, mHeaderBackgroundPaint);
canvas.restore();

// Clip to paint header row only.
canvas.save();
canvas.clipRect(mHeaderColumnWidth, 0, getWidth(), mHeaderHeight + mHeaderRowPadding * 2);
Expand All @@ -940,6 +973,7 @@ else if (mNewHourHeight > mMaxHourHeight)

// Draw the header row texts.
startPixel = startFromPixel;
drawAdditionalInfo(canvas, leftDaysWithGaps, startPixel);
for (int dayNumber = leftDaysWithGaps + 1; dayNumber <= leftDaysWithGaps + getRealNumberOfVisibleDays() + 1; dayNumber++) {
// Check if the day is today.
day = (Calendar) mHomeDate.clone();
Expand All @@ -961,6 +995,27 @@ else if (mNewHourHeight > mMaxHourHeight)

}

private void drawAdditionalInfo(Canvas canvas, int leftDaysWithGaps, float startPixel) {
if (mAdditionalTimeInfo != ADDITIONAL_INFO_NONE) {
String pattern;
switch (mAdditionalTimeInfo) {
case ADDITIONAL_INFO_MONTH:
pattern = "MMM";
break;
case ADDITIONAL_INFO_WEEK:
pattern = "ww";
break;
default:
pattern = "yyyy";
break;
}
Calendar firstVisibleDay = (Calendar) mHomeDate.clone();
firstVisibleDay.add(Calendar.DATE, leftDaysWithGaps);
canvas.drawText(new SimpleDateFormat(pattern, Locale.getDefault()).format(firstVisibleDay.getTime()),
startPixel - mHeaderColumnWidth / 2, mHeaderTextHeight + mHeaderRowPadding, mHeaderTextPaint);
}
}

/**
* Get the time and date where the user clicked on.
*
Expand Down Expand Up @@ -1070,7 +1125,7 @@ left < getWidth() &&
top < getHeight() &&
right > mHeaderColumnWidth &&
bottom > mHeaderHeight + mHeaderRowPadding * 2 + mTimeTextHeight / 2 + mHeaderMarginBottom
) {
) {
mEventRects.get(i).rectF = new RectF(left, top, right, bottom);
mEventBackgroundPaint.setColor(mEventRects.get(i).event.getColor() == 0 ? mDefaultEventColor : mEventRects.get(i).event.getColor());
mEventBackgroundPaint.setShader(mEventRects.get(i).event.getShader());
Expand Down Expand Up @@ -1123,7 +1178,7 @@ left < getWidth() &&
top < getHeight() &&
right > mHeaderColumnWidth &&
bottom > 0
) {
) {
mEventRects.get(i).rectF = new RectF(left, top, right, bottom);
mEventBackgroundPaint.setColor(mEventRects.get(i).event.getColor() == 0 ? mDefaultEventColor : mEventRects.get(i).event.getColor());
mEventBackgroundPaint.setShader(mEventRects.get(i).event.getShader());
Expand All @@ -1149,11 +1204,13 @@ private void drawEventTitle(WeekViewEvent event, RectF rect, Canvas canvas, floa
if (rect.right - rect.left - mEventPadding * 2 < 0) return;
if (rect.bottom - rect.top - mEventPadding * 2 < 0) return;


if (mNewEventIdentifier.equals(event.getIdentifier())) return;

// Prepare the name of the event.
SpannableStringBuilder bob = new SpannableStringBuilder();
StringBuilder bob = new StringBuilder();
if (!TextUtils.isEmpty(event.getName())) {
bob.append(event.getName());
bob.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0, bob.length(), 0);
}
// Prepare the location of the event.
if (!TextUtils.isEmpty(event.getLocation())) {
Expand All @@ -1174,27 +1231,56 @@ private void drawEventTitle(WeekViewEvent event, RectF rect, Canvas canvas, floa
if (textLayout.getLineCount() > 0) {
int lineHeight = textLayout.getHeight() / textLayout.getLineCount();

if (availableHeight >= lineHeight) {
// Calculate available number of line counts.
int availableLineCount = availableHeight / lineHeight;
do {
// Ellipsize text to fit into event rect.
if (!mNewEventIdentifier.equals(event.getIdentifier()))
textLayout = new StaticLayout(TextUtils.ellipsize(bob, mEventTextPaint, availableLineCount * availableWidth, TextUtils.TruncateAt.END), mEventTextPaint, (int) (rect.right - originalLeft - mEventPadding * 2), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);

// Reduce line count.
availableLineCount--;

// Repeat until text is short enough.
} while (textLayout.getHeight() > availableHeight);

// Draw text.
canvas.save();
canvas.translate(originalLeft + mEventPadding, originalTop + mEventPadding);
textLayout.draw(canvas);
canvas.restore();
if (availableHeight < lineHeight) {
return;
}
String text = textLayout.getText().toString();
SpannableStringBuilder correctedText = getEllipsizedText(text, rect, originalLeft, availableHeight, availableWidth, lineHeight);

boolean wholeNameIsShown = correctedText.toString().startsWith(event.getName());
// make event name bold
if (wholeNameIsShown) {
correctedText.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0, event.getName().length(), 0);
} else { // text must be ellipsized and the location (if any) is totally cut of
correctedText.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0, correctedText.length(), 0);
}

textLayout = new StaticLayout(correctedText, mEventTextPaint, (int) (rect.right - originalLeft - mEventPadding * 2),
Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);

// Draw text.
canvas.save();
canvas.translate(originalLeft + mEventPadding, originalTop + mEventPadding);
textLayout.draw(canvas);
canvas.restore();
}
}

private SpannableStringBuilder getEllipsizedText(String text, RectF rect, float originalLeft, int availableHeight, int availableWidth, int lineHeight) {
// Calculate available number of line counts.
@SuppressWarnings("UnnecessaryLocalVariable")
int availableLineCount = availableHeight / lineHeight;
int availableLinesLeft = availableLineCount;
String[] lines = text.split("\n");
List<String> newLines = new ArrayList<>();
for (String s : lines) {
if (availableLinesLeft < 1) {
break;
}
CharSequence ellipsizedLine = TextUtils.ellipsize(
s,
mEventTextPaint, availableWidth * availableLinesLeft, TextUtils.TruncateAt.END);
newLines.add((String) ellipsizedLine);
StaticLayout textLayout = new StaticLayout(ellipsizedLine, mEventTextPaint, (int) (rect.right - originalLeft - mEventPadding * 2),
Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
availableLinesLeft -= textLayout.getLineCount();
}
SpannableStringBuilder correctedText = new SpannableStringBuilder();
for (String line : newLines) {
correctedText.append(line);
correctedText.append("\n");
}
return correctedText;
}

/**
Expand Down Expand Up @@ -2503,6 +2589,20 @@ public int getMinOverlappingMinutes() {
return this.mMinOverlappingMinutes;
}

/**
* Sets which information should be displayed in addition in the header next to the week day names
*
* @param additionalTimeInfo the info which should be displayed, one of {@literal ADDITIONAL_INFO_NONE},
* {@literal ADDITIONAL_INFO_YEAR}, {@literal ADDITIONAL_INFO_MONTH}, {@literal ADDITIONAL_INFO_WEEK}
*/
public void setAdditionalTimeInfo(int additionalTimeInfo) {
this.mAdditionalTimeInfo = additionalTimeInfo;
}

public int getAdditionalTimeInfo() {
return this.mAdditionalTimeInfo;
}

/////////////////////////////////////////////////////////////////
//
// Functions related to scrolling.
Expand Down Expand Up @@ -2776,6 +2876,7 @@ public interface AddEventClickListener {
public interface ZoomEndListener {
/**
* Triggered when the user finishes a zoom action.
*
* @param hourHeight The final height of hours when the user finishes zoom.
*/
void onZoomEnd(int hourHeight);
Expand Down
14 changes: 7 additions & 7 deletions sample/build.gradle
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
apply plugin: 'com.android.application'

android {
compileSdkVersion 25
buildToolsVersion '25.0.2'
compileSdkVersion 28
buildToolsVersion '28.0.3'

defaultConfig {
applicationId "com.alamkanak.weekview"
minSdkVersion 14
targetSdkVersion 25
targetSdkVersion 28
versionCode 1
versionName "1.0"
}
Expand All @@ -20,8 +20,8 @@ android {
}

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile project(':library')
compile 'com.android.support:appcompat-v7:25.1.0'
compile 'com.squareup.retrofit:retrofit:1.9.0'
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':library')
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.squareup.retrofit:retrofit:1.9.0'
}