I want to fill the area outside a rectangle on a canvas. I use
canvas.drawRect(pTopLeft.x, pTopLeft.y, pBotRight.x, pBotRight.y, paint);
to draw the rectangle, but can't figure out how to fill outside the rectangle/clip.
Thanks
Geoff
Thanks ted and trojanfoe - the neatest solution I've come up with is
Point pTopLeft = new Point();
Point pBotRight = new Point();
//TODO:set x,y for points
Rect rHole = new Rect(pTopLeft.x, pTopLeft.y, pBotRight.x, pBotRight.y);
//assume current clip is full canvas
//put a hole in the current clip
canvas.clipRect(rHole, Region.Op.DIFFERENCE);
//fill with semi-transparent red
canvas.drawARGB(50, 255, 0, 0);
//restore full canvas clip for any subsequent operations
canvas.clipRect(new Rect(0, 0, canvas.getWidth(), canvas.getHeight())
, Region.Op.REPLACE);
You aren't going to fill outside the clip; that's the kind of thing clip is there to prevent! If you want to fill the space outside a rect and inside the drawing layer bounds, construct four auxiliary rects:
Rect above = new Rect(0, 0, canvas.getWidth(), pTopLeft.y);
Rect left = new Rect(0, pTopLeft.y, pTopLeft.x, pBotRight.y);
Rect right = new Rect(pBotRight.x, pTopLeft.y, canvas.getWidth(), pBotRight.y);
Rect bottom = new Rect(0, pBotRight.y, canvas.getWidth(), canvas.getHeight());
Then fill these.
ICS and above ...
canvas.clipRect(rHole, Region.Op.DIFFERENCE);
XOR, Difference and ReverseDifference clip modes are
ignored by ICS if hardware acceleration is enabled.
Just disable 2D hardware acceleration in your view:
myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
Reference Android: Howto use clipRect in API15
You can't draw outside of the Canvas; that area belongs to the parent View. Do you have the ability to subclass the parent View and do your drawing in that class instead?
If you want to draw outside of the Canvas clip then you'll have to invalidate() the areas you are interested in.
Related
I have the following code to delimit the area of a view to be drawn:
Rect rect = new Rect();
rect.set(0, 0, 100, 100);
View.setClipBounds(rect);
This will draw my view only on the specified rectangle (or square, in this case). However, I wanted the view to be clipped to a circle. Is there any way to somehow round the corners of a Rect object?
In this case, you've to subclass that view and add some extra logic to it.
Add these codes to its constructor method, or wherever you would like to initialize the view.
final Path path = new Path();
path.addRoundRect(new RectF(0,0,getWidth(),getHeight()),10,10,Direction.CW);
Using these codes, you're defining a path along which your view is going to be drawn (the area inside the boundaries of the patch).
Add this method to the class to apply this mask on your view.
#Override
protected void dispatchDraw(Canvas canvas){
canvas.clipPath(path);
super.dispatchDraw(canvas);
}
Credits: https://stackoverflow.com/a/7559233/1841194
Try to use
RectF r = new RectF(10,100,200,400);
canvas.drawRoundRect(r, 0, 0, mPaint);
about
or square case of it.
The other approach is to use clipping mask. The concept of this idea is to use PorterDuffXfermode or PorterDuff .
This is an example for the rounded corner view. I don't know what directly you want that's why I just can give to base methods I've used. The other example.
Try this:
val circlePath = Path().apply {
addCircle(x, y, radius, Path.Direction.CW)
}
canvas.clipPath(circlePath)
I tried to create a Maze with a moving ball and a hole using the Accelerometer Sensor. With the following code, the ball falls into the hole, but the performance is really bad, I set the Accelerometer Frequency to the fastest, but it's everything other than smooth. I made a second canvas, because so I could make a hole.
public RenderView(Context context, int width, int height) {
super(context);
playGround = new Rect(40, 40, width - 40, height - 40);
holes.addElement(new PointF(500f, 500f));
// Set background
this.setBackgroundResource(R.drawable.bottom);
// Set bitmap
woodGround= wood.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmapCanvas = new Canvas();
bitmapCanvas.setBitmap(woodGround);
// Set eraser paint properties
eraserPaint.setAlpha(0);
eraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
eraserPaint.setAntiAlias(true);
}
protected void onDraw(Canvas canvas) {
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG
| Paint.FILTER_BITMAP_FLAG);
paint.setStyle(Style.FILL_AND_STROKE);
paint.setAntiAlias(true);
if (ballInHole)
canvas.drawBitmap(ball, b.x, b.y, paint);
bitmapCanvas.drawBitmap(wall, 0, 0, paint);
bitmapCanvas.drawBitmap(wood, playGround, playGround, paint);
canvas.drawBitmap(bitmap, 0, 0, paint);
for (PointF h : holes) {
bitmapCanvas.drawCircle(h.x + radius, h.y + radius, radius,
eraserPaint);
}
if (!ballInHole)
canvas.drawBitmap(ball, b.x, b.y, paint);
invalidate();
}
It's solved very ugly, because I just draw the ball bellow the other bitmaps when he falls into a hole. Is there another way to do it?
The performance is also really bad, i set the Accelerometer-Sensor-Delay to the fastest, but the ball doesn't run smooth. When I remove the line canvas.drawBitmap(bitmap, 0, 0, paint);, then the ball is smoother, but then the wooden background is away.
The problem here is that you're doing A LOT of drawings all the time and that's take time to draw and the performance gets very low.
here a few tips on how you should approach it.
You probably better have one view with the static stuff (the background image and the holes) and on your layout have a second view on top of it just drawing the ball.
on the background image, do not call invalidate. That way you will draw the background just once.
and the top image (the ball only) you can invalidate, so it can redraw on the new position.
I'm not sure on this last part: but you may need to call invalidate(rect); passing the area where the ball was on the previous time, to make the background only re-draw that small area (instead of the whole screen)
happy coding.
My Desired Output:
Text on the widget(Home Screen Widget) with custom font
My Problems:
Can not use custom font on textview in widget (As TextView's are not directly accessable in widgets, we have to use them using remoteviews.)
So i thought to use imageview and draw text on bitmap using canvas and then set this bitmap on the imageview. So far everything is working but just two thing.
I dont know,
how to make my text to set vertically and horizontally on centre? (Vertically and horizontally means, to be in centre both vertically and horizontally)
how to make my text to fill the entire space of bitmap?
I am using following code to show some text on the widget.
Bitmap bitmap = Bitmap.createBitmap(200, 200, Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
paint.setColor(Color.YELLOW);
canvas.drawPaint(paint);
paint.setColor(Color.GREEN);
paint.setStyle(Style.FILL);
canvas.drawText("Test", 50, 50, paint);
and following is the output.
1- Setting font runtime on textview in widget
I dont know that either
2- Setting text to be in center and fill the container
Please see the following code:
float size = 1.0f;
Bitmap bitmap = Bitmap.createBitmap(200, 200, Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
paint.setColor(Color.YELLOW);
canvas.drawPaint(paint);
paint.setColor(Color.GREEN);
paint.setStyle(Style.FILL);
Rect rect = new Rect();
paint.getTextBounds("Test", 0, 4, rect);
float width = 1.0f;
while (width<200 && rect.height()<200)
{
size++;
paint.setTextSize(size);
width = paint.measureText("Test");
paint.getTextBounds("Test", 0, 4, rect);
}
size--;
paint.setTextSize(size);
canvas.drawText("Test", 100-width/2, 100+rect.height()/2, paint);
I was trying to attach the screen shot but it wont allow me to add as i am a new user.(rookie :P)
Thanks,
Hello Why you can't use a custom font on TextView: i think you can just subclass TextView and add this the three default text view constructor
if (!isInEditMode()) {
Typeface tf = Typeface.createFromAsset(getContext().getAssets(), "fonts/ds_digib.ttf");
setTypeface(tf);
}
To make your text set vertically and horizontally
see this link
hope this will help you to resolve your problem
Can not use custom font on textview in widget
TextView tv=(TextView)findViewById(R.id.custom);
Typeface face=Typeface.createFromAsset(getAssets(),"fonts/Verdana.ttf");
tv.setTypeface(face);
if you want to use same in canvas then set the face to paint obj which u r using.
how to make my text to set vertically and horizontally on centre?
In canvas as your drawing text by paint obj. then you can set paint obj
mPaint.setTextAlign(Align.CENTER);
or you can place to left or right. You can't set vertically or horizontally because ur drawing the text so you draw as you want vertically or horizontally.
example you need to draw text SAM
horizontally means canvas.drawText("SAM", 50, 50, paint);
veritically means
canvas.drawText("S", 50, 50, paint);
canvas.drawText("A", 50, 51, paint);
canvas.drawText("M", 50, 52, paint);
NOTE : then you might think what is mPaint.setTextAlign(Align.CENTER), right or left. so example if you ve 100 widht and you start writing from 20px to 50px means then its tries to keep in center from 20 to 50 px. NOT in center to widht 100px.
how to make my text to fill the entire space of bitmap?
You will not find any wrap content here because in Canvas we have to our self.
so first check how much space u have and fill the give x and y values and also change the text-size prepositionally .
I am drawing a grid and I want it to be larger than the screen size so that a user can drag the screen left/right/up/down to get to the rest of the grid.
What is the best way to do that? I've tried drawing a larger bitmap to the canvas, but didn't get anywhere.
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.WHITE);
Bitmap testBitmap = Bitmap.createBitmap(1000, 1000, Bitmap.Config.ARGB_8888);
canvas.drawBitmap(testBitmap, 0, 0, paint);
canvas.drawPaint(paint);
//other grid drawing code here
}
I used the View's scrollBy() method in the onTouch method of the Activity. It worked.
You can probably use the canvas.translate(x, y) method. That will adjust the origin for your canvas in relation to the screen. So canvas.translate(10, 10) will make you canvas origin (0, 0) be at the point of (10, 10) on the screen. Use a negative translation to scroll the screen.
I want to draw an image into shape of a Path and then add border on the Path. I was able to clip the image with Path but can't find a way to add border on it. I though it would be simple because the API supports Paint object on Canvas.draw* methods.
I asked another question at: Draw bitmap on current clip in canvas with border (Paint) and I accepted the answer. However, after that I found that I need to do a little bit more complicated processing. Because I use two options for clipping instead of one.
Below is my code to clip and image with two different Region.Op parameters
Bitmap srcImage = BitmapFactory.decodeStream(getAssets().open("panda.jpg"));
Bitmap bitmapResult = Bitmap.createBitmap(srcImage.getWidth(), srcImage.getHeight(), Bitmap.Config.ARGB_8888);
Path path = new Path();
// This is my border
Paint paint = new Paint();
paint.setStyle(Style.STROKE);
paint.setColor(Color.RED);
paint.setStrokeWidth(2);
paint.setAntiAlias(true);
Canvas canvas = new Canvas(bitmapResult);
// Overlay two rectangles
path.addRect(10, 10, 70, 70, Path.Direction.CCW);
path.addRect(40, 40, 120, 120, Path.Direction.CCW);
canvas.drawPath(path , paint);
canvas.clipPath(path, Region.Op.INTERSECT);
// Draw the circle
path.reset();
path.addCircle(40, 80, 20, Path.Direction.CCW);
canvas.drawPath(path , paint);
canvas.clipPath(path, Region.Op.DIFFERENCE);
// The image is drawn within the area of two rectangles and a circle
// Although I suppose that puting Paint object into drawBitmap() method will add a red border on result image but it doesn't work
canvas.drawBitmap(srcImage, 0, 0, paint);
((ImageView)this.findViewById(R.id.imageView1)).setImageBitmap(bitmapResult);
Here is the result from my code: http://i.stack.imgur.com/8j2Kg.png
And this is what I expect: http://i.stack.imgur.com/iKhIr.png
Do I miss anything to make it work ?