Skip to content

Commit

Permalink
Fix issue #1484
Browse files Browse the repository at this point in the history
  • Loading branch information
PhilJay committed Feb 20, 2016
1 parent 72cabd1 commit 19a98c2
Show file tree
Hide file tree
Showing 6 changed files with 29 additions and 15 deletions.
Expand Up @@ -87,6 +87,7 @@ protected void onCreate(Bundle savedInstanceState) {
xAxis.setTypeface(mTf);
xAxis.setDrawGridLines(false);
xAxis.setSpaceBetweenLabels(2);
xAxis.setLabelRotationAngle(90);

YAxisValueFormatter custom = new MyYAxisValueFormatter();

Expand Down
Expand Up @@ -85,6 +85,8 @@ public void renderAxisLabels(Canvas c) {
mAxisLabelPaint.setTextSize(mXAxis.getTextSize());
mAxisLabelPaint.setColor(mXAxis.getTextColor());



if (mXAxis.getPosition() == XAxisPosition.TOP) {

drawLabels(c, mViewPortHandler.contentTop() - yoffset,
Expand Down Expand Up @@ -148,6 +150,7 @@ public void renderAxisLine(Canvas c) {
protected void drawLabels(Canvas c, float pos, PointF anchor) {

final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle();
final float normalizedLineHeight = Utils.calcTextHeight(mAxisLabelPaint, "Q");

// pre allocate to save performance (dont allocate in loop)
float[] position = new float[] {
Expand Down Expand Up @@ -182,14 +185,14 @@ protected void drawLabels(Canvas c, float pos, PointF anchor) {
}
}

drawLabel(c, label, i, position[0], pos, anchor, labelRotationAngleDegrees);
drawLabel(c, label, i, position[0], pos, anchor, labelRotationAngleDegrees, normalizedLineHeight);
}
}
}

protected void drawLabel(Canvas c, String label, int xIndex, float x, float y, PointF anchor, float angleDegrees) {
protected void drawLabel(Canvas c, String label, int xIndex, float x, float y, PointF anchor, float angleDegrees, float normalizedLineHeight) {
String formattedLabel = mXAxis.getValueFormatter().getXValue(label, xIndex, mViewPortHandler);
Utils.drawText(c, formattedLabel, x, y, mAxisLabelPaint, anchor, angleDegrees);
Utils.drawText(c, formattedLabel, x, y, mAxisLabelPaint, anchor, angleDegrees, normalizedLineHeight);
}

@Override
Expand Down
Expand Up @@ -31,6 +31,7 @@ public XAxisRendererBarChart(ViewPortHandler viewPortHandler, XAxis xAxis, Trans
protected void drawLabels(Canvas c, float pos, PointF anchor) {

final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle();
final float normalizedLineHeight = Utils.calcTextHeight(mAxisLabelPaint, "Q");

// pre allocate to save performance (dont allocate in loop)
float[] position = new float[] {
Expand Down Expand Up @@ -76,7 +77,7 @@ protected void drawLabels(Canvas c, float pos, PointF anchor) {
}
}

drawLabel(c, label, i, position[0], pos, anchor, labelRotationAngleDegrees);
drawLabel(c, label, i, position[0], pos, anchor, labelRotationAngleDegrees, normalizedLineHeight);
}
}
}
Expand Down
Expand Up @@ -101,6 +101,7 @@ public void renderAxisLabels(Canvas c) {
protected void drawLabels(Canvas c, float pos, PointF anchor) {

final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle();
final float normalizedLineHeight = Utils.calcTextHeight(mAxisLabelPaint, "Q");

// pre allocate to save performance (dont allocate in loop)
float[] position = new float[] {
Expand All @@ -125,7 +126,7 @@ protected void drawLabels(Canvas c, float pos, PointF anchor) {
if (mViewPortHandler.isInBoundsY(position[1])) {

String label = mXAxis.getValues().get(i);
drawLabel(c, label, i, pos, position[1], anchor, labelRotationAngleDegrees);
drawLabel(c, label, i, pos, position[1], anchor, labelRotationAngleDegrees, normalizedLineHeight);
}
}
}
Expand Down
Expand Up @@ -26,6 +26,7 @@ public void renderAxisLabels(Canvas c) {
return;

final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle();
final float normalizedLineHeight = Utils.calcTextHeight(mAxisLabelPaint, "Q");
final PointF drawLabelAnchor = new PointF(0.5f, 0.0f);

mAxisLabelPaint.setTypeface(mXAxis.getTypeface());
Expand All @@ -50,7 +51,7 @@ public void renderAxisLabels(Canvas c) {
+ mXAxis.mLabelRotatedWidth / 2f, angle);

drawLabel(c, label, i, p.x, p.y - mXAxis.mLabelRotatedHeight / 2.f,
drawLabelAnchor, labelRotationAngleDegrees);
drawLabelAnchor, labelRotationAngleDegrees, normalizedLineHeight);
}
}

Expand Down
25 changes: 16 additions & 9 deletions MPChartLib/src/com/github/mikephil/charting/utils/Utils.java
Expand Up @@ -527,29 +527,30 @@ public static float getNormalizedAngle(float angle) {

public static void drawText(Canvas c, String text, float x, float y,
Paint paint,
PointF anchor, float angleDegrees) {
PointF anchor, float angleDegrees, float normalizedLineHeight) {

float drawOffsetX = 0.f;
float drawOffsetY = 0.f;

paint.getTextBounds(text, 0, text.length(), mDrawTextRectBuffer);

final float lineHeight = mDrawTextRectBuffer.height();

// Android sometimes has pre-padding
// Android sometimes has pre-padding
drawOffsetX -= mDrawTextRectBuffer.left;

// Android does not snap the bounds to line boundaries,
// and draws from bottom to top.
// And we want to normalize it.
drawOffsetY += lineHeight;

// To have a consistent point of reference, we always draw left-aligned
Paint.Align originalTextAlign = paint.getTextAlign();
paint.setTextAlign(Paint.Align.LEFT);

if (angleDegrees != 0.f) {

float lineHeight = mDrawTextRectBuffer.height();

// Android does not snap the bounds to line boundaries,
// and draws from bottom to top.
// And we want to normalize it.
drawOffsetY += lineHeight;

// Move the text drawing rect in a way that it always rotates around its center
drawOffsetX -= mDrawTextRectBuffer.width() * 0.5f;
drawOffsetY -= lineHeight * 0.5f;
Expand Down Expand Up @@ -577,10 +578,16 @@ public static void drawText(Canvas c, String text, float x, float y,
c.restore();
}
else {

// Android does not snap the bounds to line boundaries,
// and draws from bottom to top.
// And we want to normalize it.
drawOffsetY += normalizedLineHeight;

if (anchor.x != 0.f || anchor.y != 0.f) {

drawOffsetX -= mDrawTextRectBuffer.width() * anchor.x;
drawOffsetY -= lineHeight * anchor.y;
drawOffsetY -= normalizedLineHeight * anchor.y;
}

drawOffsetX += x;
Expand Down

8 comments on commit 19a98c2

@PhilJay
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@danielgindi Please review this. I have verified that the issue actually exists and this fixes it. I'm just not sure if it induces any other problems.

I have tested rotated labels on barchart, that seems to work fine.

The issue basically was that the x-axis values had different line-heights, which caused them to be not vertically aligned. I generalized the line height to fix the issue.

@danielgindi
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll look into it. I remember fixing a vertical alignment problem already. But maybe something happened there along the way...

@tentypwtk
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was using a version at least week old and didn't know there's a fix for that already. The way I fixed it is similar but instead of just calculating text height of "Q" I use "0123456789ABCDEFGHIJKLMNOPRSTUWXYZabcdefghijklmnoprstuwxyz". My thinking was to make it ready for strange fonts where "7" or "S" may be higher than "Q". Just an idea. I'm glad you fixed it anyway.

@danielgindi
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've struggled a lot with Android's font system. I can safely say it sucks. There's no way to determine the real line height - so we have to do such tricks...

Anyway - If you have a "7" that's way above the normal character height - that shouldn't be calculated. We need to establish a normalized line height so we have a center point to rotate around. For vertical aligning of horizontal text - that's less important because in the worst case there's more offset and that could be adjusted using other properties.

@tentypwtk
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "7" was just an example - I see you got it covered :-) I'm happy with my simple fix for the moment.
Cheers! Great library btw, made my life easier.

@JeppeLeth
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to have fixed the problem for my project also. Should a pull request be made to merge it into the source?

@mliikanen
Copy link

@mliikanen mliikanen commented on 19a98c2 Jun 2, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This solution will fail if you happen use glyphs that reach lower or higher than Q. On some typefaces character Q actually does not reach below generic baseline.

Paint.getTextBounds(String) a is wrong thing to use when aligning text - it simply looks at the rendering bounds of the text. There already is method Utils.getLineHeight(Paint) that uses the correct way of calculating line heights (looking at the difference of typeface's ascent and descent). Replacing the normalizedLineHeight with call to this method should do the trick, and get rid of Utils.calcTextHeight("thisShouldCoverRelevantCharactersInMyTypefaceRight")

http://stackoverflow.com/a/27631737/3432809 has a great write-up on Android font metrics.

@danielgindi
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an actual fix on 2.2.5 release

Please sign in to comment.