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();
}
Related
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!
Can someone explain exactly how rotation of the canvas about a point works ?
I have lines and I want to draw text parallel to each line. I have worked out the trigonometry required to calculate the angle of the line and it`s centre point.
When I try rotate the canvas about the start point of the line, then draw the text and restore, I am always getting strange offsets which obviously means I do not quiet get how the rotation is working ...
Can someone explain what happens when you rotate the canvas about a point and how the then drawing on co-ords X,Y translates back ?
see the following class:
class V extends View {
private Paint mPaint;
public V(Context context) {
super(context);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(0xffeeeeee);
mPaint.setTextSize(24);
}
#Override
protected void onDraw(Canvas canvas) {
float x = 100;
float y = 50;
float dx = 60;
float dy = 40;
canvas.drawLine(x, y, x + dx, y + dy, mPaint);
canvas.save();
float degrees = (float) (180 * Math.atan2(dy, dx) / Math.PI);
canvas.rotate(degrees, x, y);
canvas.drawText("text", x, y, mPaint);
canvas.restore();
}
}
now i hope everything should be clear how canvas rotation works...
I want to draw a circle (it is more but I have problems with the circle) on a canvas with the View Class.
I have started with this link: http://mindtherobot.com/blog/272/
The onMeasure() function is the same and I am scaling the same. In my emulator and on my Nexus 7 everything looks perfect but on my LG it isn´t
It looks loke the color of the circle is blurred.
The code is:
protected void onDraw(Canvas canvas) {
float scale = (float) getWidth();
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.scale(scale, scale);
drawCircle(canvas);
canvas.restore();
}
private void drawCircle(Canvas canvas) {
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.drawOval(rimRect, debug);
canvas.restore();
}
private void initDrawingTools() {
float O1x = -0.5f;
float O1y = -0.5f;
float O2x = 0.5f;
float O2y = 0.5f;
rimRect = new RectF(O1x, O1y, O2x, O2y);
debug = new Paint();
debug.setColor(Color.RED);
}
use AntiAlias property for Paint. it protect the image from blurring and gives smoothing edges
debug = new Paint();
debug.setAntiAlias(true);
debug.setColor(Color.RED);
I am trying to make an application in which i can zoom in and draw some thing on the image
i have used following link to make a zoomable image view which is working fine
zoom functionality for images
now the problem is that i dont know how to scale and translate whatever i am drawing on my canvas to the image size when it is zoomed out.
following is the onDraw() function.
i have recorded the touch points added them into a list and in this onDraw() function of my view i am retrieving those points but these points are relative to screen .
i have to translate and scale it according to the image operation(in case of zoom in/out of the image) .
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(mMarkers!=null)
{
for(Marker m : mMarkers) {
// draw the marker
Log.v("IP","position"+ m.x+" "+ m.y);
Paint paint=new Paint();
paint.setARGB(255, 2, 255, 2);
canvas.drawText("O", m.x, m.y, paint);
if(x!=-1){
Log.v("IP","LINE"+x+" " + y+ " "+ m.x+" "+ m.y);
canvas.drawLine(x, y,m.x, m.y, paint);
}
x=m.x;
y=m.y;
}
x=y=-1;
}
}
Use this code
public boolean onTouchEvent(MotionEvent env) {
float x = env.getX() / mScaleFactor + rect.left;
float y = env.getY() / mScaleFactor + rect.top;
}
Please try this code
public boolean onTouchEvent(MotionEvent env) {
float x = env.getX() / mScaleFactor + rect.left;
float y = env.getY() / mScaleFactor + rect.top;
}
you can get 9 point array from the matrix which contain the relative x,y and scale factor in x and y use this to convert you current x,y (which is with respect to screen) to x,y on original image and reconvert it before drawing again on different zoom levels.