Hi I am using a custom view with pinch zoom. But on zoom text looks stretched on tablet.
But on android phone its perfectly OK. I am using the following code to draw text.
What I need to do so that text looks normal on zoom ? Why this problem is only on tablet ?
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(0xff000000);
paint.setStyle(Style.FILL);
paint.setTextSize(cellWidth * 0.75f);
paint.setTextScaleX(cellWidth / cellHeight);
paint.setTextAlign(Paint.Align.CENTER);
FontMetrics fm = paint.getFontMetrics();
float x = cellWidth /2 ;
float y = cellHeight/2 - (fm.ascent + fm.descent ) /2;
canvas.drawText(sb.toString(), x , y, paint);
Related
I am rotating and scaling canvas using canvas.setMatrix() and then drawing text on canvas using canvas.drawText();. That's working as expected. But when I try to draw text that contains emojis (android device's default emojis), they don't get rotated. Everything else in the text gets rotated as expected. Also drawing and scaling works as expected for emojis, it's just the rotating that doesn't work,and I find it only appears in android7.0 or below 7.0.
public void draw(Canvas canvas) {
if (TextUtils.isEmpty(mText)) {
drawCursor(canvas);
return;
}
float allTextHeight = mTextLineHeight * mLines.length;
float y = (mHeight - allTextHeight) / 2;
float offset = mPaint.getFontMetrics().ascent;
canvas.save();
canvas.translate(mWidth / 2, 0);
float initY = y;
for (String line : mLines) {
drawText(canvas, line, 0f, y - offset, mPaint, initY);
y += mTextLineHeight;
}
canvas.restore();
}
I had an android app that displays a scale and it worked fine on all version until 5.0 (Lollipop) where the all the texts drawn with drawText method are somehow truncated.
Only the second letter of a tow letters text is displayed.
i.e. The first mark is only '0' letter but is not displayed.
For the second 2 marks (40, and 80) only 0 is displayed and for the rest, 140... 250, no text is displayed.
The text size seems to be OK but not all the chars are displayed.
The image is scaled in [0..1, 0..1] square.
I have found and try lot of posts on drawText but none of them helped.
(View.setLayerType() all combinations, setLinearText(true), Paint.Style.FILL...)
private void drawScale(final Canvas canvas) {
canvas.save(Canvas.MATRIX_SAVE_FLAG);
// On canvas, North is 0 degrees, East is 90 degrees, South is 180 etc.
// We start the scale somewhere South-West so we need to first rotate the canvas.
canvas.rotate(mScaleRotation, 0.5f, 0.5f);
final int totalTicks = mDivisions * mSubdivisions + 1;
for (int i = 0; i < totalTicks; i++) {
final float y1 = mScaleRect.top;
final float y2 = y1 + 0.015f; // height of division
final float y3 = y1 + 0.045f; // height of subdivision
final float value = getValueForTick(i);
final Paint paint = getRangePaint(value);
if (0 == value % mDivisions) {
// Draw a division tick
canvas.drawLine(0.5f, y1, 0.5f, y3, paint);
// Draw the text 0.15 away from the division tick
canvas.drawText(valueString(value), 0.5f, y3 + 0.045f, paint);
}
else {
// Draw a subdivision tick
canvas.drawLine(0.5f, y1, 0.5f, y2, paint);
}
canvas.rotate(mSubdivisionAngle, 0.5f, 0.5f);
}
canvas.restore();
}
where a range paint is create like this
mRangePaints[i] = new Paint(Paint.LINEAR_TEXT_FLAG | Paint.ANTI_ALIAS_FLAG);
mRangePaints[i].setColor(mRangeColors[i]);
mRangePaints[i].setStyle(Paint.Style.STROKE);
mRangePaints[i].setStrokeWidth(0.005f);
mRangePaints[i].setTextSize(0.05f);
mRangePaints[i].setTypeface(Typeface.SANS_SERIF);
mRangePaints[i].setTextAlign(Align.CENTER);
mRangePaints[i].setShadowLayer(0.005f, 0.002f, 0.002f, mTextShadowColor);
and canvas is created like this
mBackground = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(mBackground);
This is a capture from and android 5.0
Android 5.0
This is capture from an android 4.4
Android <5.0
Any help, clue, etc... will be deeply appreciated.
EDIT:
Thank you for suggestions,
I have wrote a function that re-scale all the sizes as for an textSize 10f.
Every original call to canvas.drawText was replaced with a call to this
drawScaledText(canvas, ... original values ...)
Everything seem to work fine.
Here is the code snippet:
public static void drawScaledText(Canvas canvas, String text, float x, float y, Paint paint, float scale) {
float originalStrokeWidth = paint.getStrokeWidth();
float originalTextSize = paint.getTextSize();
float textScaling = 10f/originalTextSize;
paint.setStrokeWidth(originalStrokeWidth * textScaling);
paint.setTextSize(originalTextSize * textScaling);
canvas.save();
canvas.scale(scale/textScaling, scale/textScaling);
canvas.drawText(text, x * textScaling, y * textScaling, paint);
canvas.restore();
paint.setStrokeWidth(originalStrokeWidth);
paint.setTextSize(originalTextSize);
}
canvas.DrawText() and canvas.DrawTextOnPath() do not work well at sizes under 1
Increase your font size. You can scale your canvas to compensate.
See answer to this question.
Android 4.2.1 wrong character kerning (spacing)
I'm working on Android project and I'm using View class to drawing image and overlays.
I'm using this code to drawing image and circle (it is working fine):
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
if (!isInitialized) {
w = getWidth();
h = getHeight();
position.set(w / 2, h / 2);
// Scaling image to be fit screen
if(width<=height) { scale=((float)w)/width; } else { scale=((float)h)/height; }
isInitialized = true;
}
Paint paint = new Paint();
matrix.reset();
matrix.postTranslate(-width / 2.0f, -height / 2.0f);
matrix.postRotate((float)(angle*180/Math.PI));
matrix.postScale(scale, scale);
matrix.postTranslate(position.getX(), position.getY());
canvas.drawBitmap(bitmap, matrix, paint);
canvas.concat(matrix);
canvas.drawCircle(testPoint.x, testPoint.y, 20, paint );
}
Now I want to show PopupWindow windows as a tip on the image and on the same position of circle (testPoint). but the PopupWindows is not drawable object, so how can I find the new coordinates of testPoint after scaling, rotation and translating to put the coordinates of the PopupWindow same.
I'm trying to write code and it working fine, but for rotation and translating only without scaling, this is the code I wrote:
int offsetX=(int) (-(width / 2.0f)+position.getX())+rc.left;
int offsetY=(int) (-(height / 2.0f)+position.getY())+rc.top;
Point RotatedPoint = RotatePoint(testPoint, centerPoint, angle);
RotatedPoint.x +=offsetX;
RotatedPoint.y +=offsetY;
popup.update(RotatedPoint.x, RotatedPoint.y, -1, -1);
Where rc is the offset difference between bitmap view coordinates and the screen coordinates.
And I'm tested RotatePoint function and it working correctly.
Note: when the scale is equal 1 (disable scaling) the position of popup window is updating correctly if I rotate or move the image.
How can I merged the scale (if not equal 1 only) with equations?
Or there is another way to find new coordinates of overlay object on the canvas after modifying on matrix?
Please help me to solved this problem.
I will be grateful for the help.
Thank you.
try to use matrix.mapPoints()
u must have the absolute coordinates of the image
how to absolute coord?
void calculaCoordenadasImagen(MotionEvent e){
float []m = new float[9];
matrix.getValues(m);
float transX = m[Matrix.MTRANS_X] * -1;
float transY = m[Matrix.MTRANS_Y] * -1;
float scaleX = m[Matrix.MSCALE_X];
float scaleY = m[Matrix.MSCALE_Y];
lastTouchX = (int) ((e.getX() + transX) / scaleX);
lastTouchY = (int) ((e.getY() + transY) / scaleY);
lastTouchX = Math.abs(lastTouchX);
lastTouchY = Math.abs(lastTouchY);
}
then use 2 different arrays to save the points
int [] absolute = new int[2];
absolute[0]=lastTouchX;
absolute[1]=lastTouchY;
int [] points = new int[2];
points = matrix.mapPoints(absolute)
in absolute u have the absolute coordinates and in points u have the points u want know
i wish it help!
I have following code:
protected void onDraw(Canvas canvas)
{
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.RED);
xRatio = getWidth()*1.0f / picWidth;
yRatio = getHeight()*1.0f / picHeight;
canvas.drawBitmap( sourceImage, null , new Rect(0,0,getWidth(),getHeight()),paint);
for (int i = 0; i < eyesMidPts.length; i++)
{
if (eyesMidPts[i] != null)
{
// where x and y are eyes mid point coordinates
float x = eyesMidPts[i].x*xRatio;
float y = eyesMidPts[i].y*yRatio;
float radius = (float) (eyesDistance[i]);
float left = x - radius;
float right = x + radius;
float top = y - radius;
// we want to increase the bottom radius by double to get other half of the face.
float bottom = (float) (y + radius * 2);
paint.setStrokeWidth(eyesDistance[i] /20);
RectF ovalBounds = new RectF();
ovalBounds.set(left, top, right, bottom);
canvas.drawOval(ovalBounds, paint);
}
}
}
Code encompass full face if face is straight. But does not get complete circle around if face is tilted. I am not sure how euler angels work but am hoping it is to get tilt on the face detection. Can someone please help me with this show me some example code so that circle encompasses whole face.
Why do you use RectF to draw a circle? Is it a 'true' circle, or is it really oval / ellipse?
If it's just a circle, why don't you use this.
canvas.drawCircle(x, y, radius, paint);
So you won't need to transform Rectf's points if it's tilted :)
update:
I believe this should work, I don't have an android workflow setup with me to really test it; sorry if it's a miss.
Matrix m = new Matrix(); //creates identity matrix
m.setRotate(tilt_angle); //rotate it by the tilt angle
m.mapRect(ovalBounds); //transform the rect
// RectF face: face position and dimentions
// Create Bitmap where to draw an oval
Bitmap ovalBmp = Bitmap.createBitmap( face.width(), face.height(), config );
canvasBmp = new Canvas(ovalBmp); // Get a canvas to draw an oval on the Bitmap
canvasBmp.drawOval( new RectF( 0, 0, face.width(), face.height ), paint );
// Create transformation matrix
transforMatrix = new Matrix();
// Rotate around center of oval
transforMatrix.postRotate( tilt_angle, face.width() / 2, face.height() / 2 );
transforMatrix.postTranslate( face.left, face.top );
canvas.drawBitmap( ovalBmp, transforMatrix, null );
ps: I assume by tilt angle you mean roll, where pitch is rotation around X axis, yaw is rotation around Y axis and roll is rotation around Z axis.
In hardware accelerated custom View added in ScrollView or ListView both of the following code snippets produces same result: (please ignore best practises for a sec)
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// centering stuff
float centerX = getWidth() / 2f;
float centerY = getHeight() / 2f;
float size = 80;
float halfSize = size / 2f;
float left = centerX - halfSize;
float top = centerY - halfSize;
RectF oval = new RectF(left, top, left + size, top + size);
Path path = new Path();
path.addArc(oval, 160, 359);
Paint paint = new Paint();
paint.setTextSize(30);
paint.setStyle(Style.STROKE);
canvas.drawTextOnPath("Hello world", path, 0, 0, paint); //<--- line A
canvas.drawCircle(centerX, centerY, 10, paint); //<--- line B
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// centering stuff
float centerX = getWidth() / 2f;
float centerY = getHeight() / 2f;
float size = 80;
float halfSize = size / 2f;
float left = centerX - halfSize;
float top = centerY - halfSize;
RectF oval = new RectF(left, top, left + size, top + size);
Path path = new Path();
path.addArc(oval, 160, 359);
Paint paint = new Paint();
paint.setTextSize(30);
paint.setStyle(Style.STROKE);
canvas.drawCircle(centerX, centerY, 10, paint); //<--- line B
canvas.drawTextOnPath("Hello world", path, 0, 0, paint); //<--- line A
}
Same Result:
But with later code snippet, as soon as you scroll the ScrollView (I have invisible dummy View below so I can scroll) and helloworld touches ActionBar, something very intersting happens and you see something that intelligent humankind used to see in old Windows OS .
I know drawTextOnPath() is not supported in hardware accelration mode, but then why it works if you call it first?
drawTextOnPath() is supported by hardware acceleration after Android 4.1
This is mentioned officially here: https://code.google.com/p/android/issues/detail?id=37925
but the next comment seems to indicate your problem in a way so maybe a bug.
Of course for pre 4.1 just dont make it use HW accel - Set a software layer type on your View by calling View.setLayerType(View.LAYER_TYPE_SOFTWARE, null) and try to get a tradeoff on perf vs errors