How do you horizontally center a String in a surface view?
I don't want
c.drawText(""+score, c.getWidth()/2, y);
Because this will display text starting at the center of the screen.
I want something like
c.drawText(""+score, c.getWidth()/2-score.length()/2*fontSize, y);
Is there an equation to solve this problem? If not, are there any other ways to center text in a surface view?
BTW I'm using a custom font, I don't know if that changes anything or not. If you need to know any extra information about the font please comment.
You don't have to calculate manually. You just need to specify some extra parameters in your Paint object:
Paint paint = new Paint();
paint.setColor(textColor);
paint.setTextSize(28);
paint.setTypeface(typeface);
paint.setTextAlign(Align.CENTER);
The key is that you need to specify Align.CENTER, in this way you can just call:
canvas.drawText("CENTERED TEXT", canvas.getWidth()/2, posY, paint);
Related
I am trying to draw a heart shaped Canvas using Path in Android. The code is as follows :
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Fill the canvas with background color
canvas.drawColor(Color.WHITE);
// paint.setShader(null);
// Defining of the heart path starts
path.moveTo(left + WIDTH / 2, top + HEIGHT / 4); // Starting point
// Create a cubic Bezier cubic left path
path.cubicTo(left+WIDTH/5,top,
left+WIDTH/4,top+4*HEIGHT/5,
left+WIDTH/2, top+HEIGHT);
// This is right Bezier cubic path
path.cubicTo(left + 3 * WIDTH / 4, top + 4 * HEIGHT / 5,
left + 4 * WIDTH / 5, top,
left + WIDTH / 2, top + HEIGHT / 4);
paint.setShader(new LinearGradient(0, canvas.getHeight()/4, canvas.getWidth(), canvas.getHeight()/4, new int[]{Color.RED, Color.YELLOW, Color.GREEN}, new float[]{0, 0.6f, 1}, Shader.TileMode.CLAMP));
canvas.drawPath(path, paint);
heart_outline_paint.setColor(getResources().getColor(R.color.heart_outline_color)); // Change the boundary color
heart_outline_paint.setStrokeWidth(4);
heart_outline_paint.setStyle(Paint.Style.STROKE);
canvas.drawPath(path, heart_outline_paint);
}
I am able to draw heart without any issue and I am able to fill color inside the heart using the Fill option in Paint. But I should be able to fill the heart dynamically according to some data and it cannot be filled fully all the time. What I have achieved so far is as follows :
I have made an extensive search and came across a lot of things similar to this. Some of which includes :
Android fill in part of a path?
filling a circle gradually from bottom to top android
I also came across the concept of converting the canvas to bitmap and filling color inside the bitmap using Flood Fill Algorithm which lets users to fill colors inside the bitmap. However, I do not want the bitmap to fill the color while touching inside the heart but to fill while a button click action.
I thought that filling a circle gradually from bottom to top android
would give help me but it makes use of a circle and I am not well-versed in Canvas which makes me very weak in adapting the circle code to such a shape.
If anybody has some ideas or any insights on how to achieve this, it will be really helpful. Cheers. Thanks in advance.
P.S : I also tried some tricks using setShader in Paint but nothing would give me what I want.
EDIT :
I just stumbled upon a idea of drawing a rectangle over the heart with another color same as the background of the canvas so that it will look like its half filled !! I am still working on the idea and not sure how accurate this is gonna be for me. If someone has a better idea, you're most welcome.
I used clipPath function available in Canvas to achieve what I needed. I draw the heart by above method and draw a rectangle over it, and I use the clipPathfunction to clip out the region that is outside the heart.
public static double filled_amount = .90;
path.moveTo(left_x_moveto, left_y_moveto);
path.cubicTo(left_x1, left_y1, left_x2, left_y2, left_x3, left_y3);
path.cubicTo(right_x2, right_y2, right_x1, right_y1, left_x_moveto, left_y_moveto);
path.close();
Rect rect = new Rect((int)(canvas.getWidth()*.10),(int)(canvas.getHeight()*filled_amount),(int) canvas.getWidth(), (int) canvas.getHeight());
canvas.clipPath(path);
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.FILL);
canvas.drawPath(path, paint);
canvas.drawRect(rect, rect_paint);
heart_outline_paint.setColor(getResources().getColor(R.color.heart_outline_color)); // Change the boundary color
heart_outline_paint.setStrokeWidth(15);
heart_outline_paint.setStyle(Paint.Style.STROKE);
canvas.drawPath(path, heart_outline_paint);
This will give me the desired result of filling the heart dynamically. Changing the value of filled_amount dynamically and calling invalidate() will make it look like the heart is being filled dynamically.
#Henry's answer might be a better one but this did the trick for me and I dont look deeply in to the edges so a bit of zig-zags here and there is all right.
You could use Bitmap Masking to get a partially filled Heart. What you ideally do here is use one bitmap to mask the other.
In your case you could have a filled rectangle in the canvas and you have then have the heart shape in a new bitmap to act as the mask. You could then dynamically change the filling of the heart by changing the height of the background rectangle.
Refer this: https://stackoverflow.com/a/33483600/4747587. This contains the implementation of partially filling a Star. The idea is the same.
I need to draw a bar diagram like the picture showing below. I am able to draw a ordinary bar diagram with filling a color on text view or some layouts. But this is a bit different.
How do I draw a bar diagram with slanting bars?
.
Try Android canvas and custom View. You can use View's onDraw method. The method provides the canvas. You should try drawArc, drawLine, etc. If your bar view contains a textView, you must use a custom viewgroup.
Do something like this in your onDraw of View.
RectF oval = new RectF();
Paint paint = new Paint();
paint.setColor(ContextCompat.getColor(context, R.color.color1));
paint.setStrokeWidth(widthOfArc);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
canvas.drawPath(path, yourPaint);
oval.set(x1,y1,x2,y2);
//eg:- startAngle = 10, sweepAngle = 40
canvas.drawArc(oval, startAngle, sweepAngle, false, paint);
Please read about how RectF works.
I will update the answer if I found any related blog/tutorial
Calculating start and end angle is pure Mathematics
refer this
http://www.html5canvastutorials.com/tutorials/html5-canvas-arcs/
You have to change the color and keep drawing Arc to get the result.
I would like to know if it is possible to add a textview programmatically in a custom view that herites from View. In this customView, I draw some shapes with a canvas and I need to put some text too. I tried to use drawText but it doesn't offer enough possiblities, especially for the position of the text. Basically, i want to put text inside, and precisely in the middle of a circle and at the same time that my text fits right in the circle (I already know how to do that).
That's why I'm wondering if it is possible to add a textview by just declaring it and draw it. Maybe I need to use an Inflater ?
I don't really know what are the best choices so I need your help :)
You can use drawText(String text, float x, float y, Paint paint) method for this.
Like this :
Paint mPaint = new Paint();
mPaint.setColor(Color.BLACK);
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawText("YOUR TEXT HERE", 10, 20, mPaint);
Instead of extending View you should extend a ViewGroup subclass like
FrameLayout.
This way you can still use the canvas to draw your custom view, but also add children views to your custom drawn viewgroup.
I am creating a custom view that extends the TextView. What I need is a couple of labels on the left handside and another checker on the right hand side of the TextView.
So I can draw those labels and then save the canvas and translate it right so that the text can be drawn beside the bitmaps. This is how I am doing that.
canvas.drawBitmap(bitmap1, 0, top, paint);
canvas.drawBitmap(bitmap2, bitmap1_width, top, paint);
canvas.save();
canvas.translate(bitmap1_width + bitmap2_width, 0);
Here I want to call super.onDraw but ask it to use the width so that the widths of the two bitmaps and the checker are subtracted.
Then I am can call my
canvas.drawBitmap(checker, checkerLeft, top, null);
So after translating the canvas, how can I can tell the canvas in the super.onDraw to use only the measured width? Please feel free to throw in any alternatives. I do want to use canvas though since my requirement is a little more complicated than this example.
Ok I did it by changing the scale factor using canvas.scale(sx, sy) before calling super.onDraw().
This is displaying the text in the way I want. If there is a better answer, please post it.
canvas.save();
canvas.translate(margin, 0);
canvas.scale(scaleX, 1f);
super.onDraw(canvas);
canvas.restore();
How to "set up" a paint to accomplish the "second" image above?
paint.setColor(Color.BLACK);
canvas.drawText(strValue, x, y, paint);
First Image: text all in black as result of that code above.
Second Image: better constrat to backgroud colors (edited with graphic editor just to illustrate here)
Note that "31" is partially black and partially white (but it could be any other color with a better contrast to red, as "36" could be to blue).
You could draw with PixelXorXfermode.
the only solution I could think of is that first on your onDraw you have a variable Canvas that you equals to the actual one and then you draw your number,
paint.setColor(Color.BLACK);
canvas.drawText(strValue, x, y, paint);
then you draw the red Rect
canvas.drawRect(myRect, redPaint);
then you draw your line
canvas.drawline(mStartX,mStartY, mFinishX, mFinishY, myLinePaint);
and at the very end outside your onDraw, you call a method like this one:
public void myMethod(){
Paint paint = new Paint();
paint.setColor(Color.BLACK);
this.canvas.drawText(strValue, x, y, paint);
//here you will define the area that you will mark as dirty
//(wich can have the same values as your red Rect)
Rect myRect = new Rect();
myRect.set(x0,y0,x1,y1);
//and finally here you invalidate ONLY the red area
this.canvas.invalidate(myRect);
}
Note: this will require that on your onDraw you verify that the global Canvas is not null
and if so, then you equals your global to the actual.
I'm not sure if this will actually work, however is the only solution I could think of doing so.
PixelXorXfermode is not good method when AntiAlias is set.
if you can get the red rectangle, I think use canvas.clipRect is better. like this
textpaint.setColor(black);
canvas.drawText(str,x,y,textpaint);
Rect oldClipRect = canvas.getClipBounds();
canvas.clipRect(rcRed,Op.REPLACE);
textpaint.setColor(white);
canvas.drawText(str,x,y,textpaint);
canvas.clipRect(oldclipRect,Op.REPLACE);