Issue with Canvas.rotate() in Android .How does it exactly work? - android

Please find the code of my onDraw method below(.I'm trying to rotate the canvas(//Rotate call -b) by 25 degrees after drawing the arc .But i find that the arc is still drawn from 0 to 50 degrees.I was expecting it to move by another 25 degrees.
public class CustomView extends View {
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.RED);
int px = getMeasuredWidth() / 2;
int py = getMeasuredHeight() / 2;
// radius - min
int radius = 130;
// Defining bounds for the oval for the arc to be drawn
int left = px - radius;
int top = py - radius;
int right = left + (radius * 2);
int bottom = top + (radius * 2);
RectF rectF = new RectF(left, top, right, bottom);
paint.setColor(Color.RED);
paint.setStyle(Style.FILL);
//canvas.rotate(25,px,py);//Rotate call -a
canvas.drawArc(rectF, 0, 50, true, paint);
canvas.rotate(25,px,py);//Rotate call -b
}
}
But if i place the rotate call(//Rotate call -a) before drawing the arc i see that the arc drawn is shifted by 25 degrees more .What exactly is happening here? Can someone explain to me?
Thanks

The Canvas maintains a Matrix that is responsible for all transformations on it. Even for rotation. As you can see in the documentation, the rotate method says:
Preconcat the current matrix with the specified rotation.
All transformations are done on the Canvas Matrix,hence, on the Canvas. The arc you draw isn't rotated. You first rotate the Canvas and then draw on it.
Hence, in your code, call -a works, not call -b.
EDIT:
For issues like postrotate and prerotate check the Matrix class(postRotate and preRotate methods).
Few Examples: this and this.
And few things you might want to read : this and this.

Related

Drawing line programmatically on a Layout

The red Margin represents an AbsoluteLayout, I have and arbitrary number of 'Board' objects placed on the screen. All I want is to draw a line on the screen using the coordinates of the Board object and the center of the screen. Each board object is responsible to draw this line.
Also I want the line to be behind the Board objects I'm guessing I have to change the z-index, or maybe draw the line on the AbsoluteLayout?
I have something like this:
public class Board {
ImageView line; //Imageview to draw line on
Point displayCenter; //Coordinates to the center of the screen
int x;
int y;
Activity activity;
Board(Point p, Point c, Activity activity) // Point c is the coordinates of the Board object
{
x = c.x
y = c.y
displayCenter.x = p.x;
displayCenter.y = p.y;
this.activity = activity;
updateLine();
}
public void updateLine(){
int w=activity.getWindowManager().getDefaultDisplay().getWidth();
int h=activity.getWindowManager().getDefaultDisplay().getHeight();
Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
line.setImageBitmap(bitmap);
Paint paint = new Paint();
paint.setColor(0xFF979797);
paint.setStrokeWidth(10);
int startx = this.x;
int starty = this.y;
int endx = displayCenter.x;
int endy = displayCenter.y;
canvas.drawLine(startx, starty, endx, endy, paint);
}
}
first af all,
you should never ever use the absolute layout, it is deprecated for a good reason.
With that said you have two options. For both options you need to implement your own Layout.
For option no. 1 you can override the dispatchDraw(final Canvas canvas) see below.
public class CustomLayout extends AbsoluteLayout {
...
#Override
protected void dispatchDraw(final Canvas canvas) {
// put your code to draw behind children here.
super.dispatchDraw(canvas);
// put your code to draw on top of children here.
}
...
}
Option no. 2 If you like the drawing to occur in the onDraw me you need to set setWillNotDraw(false); since by default the onDraw method on ViewGroups won't be called.
public class CustomLayout extends AbsoluteLayout {
public CustomLayout(final Context context) {
super(context);
setWillNotDraw(false);
}
...
#Override
protected void onDraw(final Canvas canvas) {
super.onDraw(canvas);
// put your code to draw behind children here.
}
}

Android 6.0 incorrectly handles drawCircle method

In my app I need to draw circles using bitmap and the drawCircle() method.
Everything was working fine and exactly as it should up until Android 6.0.
It still draws circles on all the previous versions, but draws rectangles when I use the app on 6.0. But if I change it to be filled, it draws a circle both in api 22 and api 23.
Anyone has the same problem or any idea why this happens?
Here is the source code and a screenshot (app running on API 23 on the left, and API 22 on the right). same app on different api's
public final class Circle1View extends View {
private float xCenter, yCenter;
private Bitmap grid = null;
public Circle1View (Context context) {
super(context);
init();
}
private void init() {
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
#Override
protected void onDraw(Canvas canvas) {
int w = getWidth();
int h = getHeight();
xCenter = w / 2;
yCenter = h / 2;
drawBitmaps(w, h);
canvas.translate(xCenter, yCenter);
canvas.scale(xCenter, yCenter);
canvas.drawBitmap(grid, null, new RectF(-1, -1, 1, 1), null);
}
private void drawBitmaps(int w, int h) {
grid = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas();
canvas.translate(xCenter, yCenter);
canvas.scale(xCenter, yCenter);
Paint gridPaint = new Paint();
gridPaint.setStrokeWidth(0.01f);
// Works with FILL
// gridPaint.setStyle(Paint.Style.FILL);
gridPaint.setStyle(Paint.Style.STROKE);
canvas.setBitmap(grid);
canvas.drawCircle(0, 0, 0.5f, gridPaint);
}
}
I think it has something to do with the scaling and translation you do. Imagine the circle that is drawn is so small, it only takes 4 pixels. When enlarging this back to the full size, you are left with 4 straight lines between these pixels.
When I change the stroke width to 0.04f, the issue is gone. I would suggest you simplify your code by drawing on the supplied Canvas directly:
#Override
protected void onDraw(Canvas canvas) {
int w = getWidth();
int h = getHeight();
xCenter = w / 2;
yCenter = h / 2;
Paint gridPaint = new Paint();
gridPaint.setStrokeWidth(1f);
gridPaint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(xCenter, yCenter, w/4, gridPaint);
}
As for your question about the difference between API levels: Marshmallow introduced changes for drawBitmap(). You can have a look at the respective source code for Lollipop and Marshmallow.

Draw Circle at Center of Canvas

I'm just getting into basic drawing with Android. I'm starting off with a few simple shapes but I'm having a few issues. I'd like to draw a circle at the center of a canvas. I looked at a few examples but can't seem to make it work. I think it's because I don't really understand what variables go where.
Could someone please explain the proper way to draw my circle at the center of my screen. Here is my code:
public class Circle extends View{
int width = this.getWidth();
int height = this.getHeight();
public Circle(Context context) {
super(context);
setFocusable(true);
}
protected void onDraw(Canvas canvas){
canvas.drawColor(Color.CYAN);
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
//canvas.drawCircle(100, 100, 50, paint);
canvas.drawCircle(width/2, height/2, 100, paint);
Display disp = ((WindowManager)this.getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
float radius = 0;//Radius caused an error so I initialized this variable
canvas.drawCircle(disp.getWidth()/2, disp.getHeight()/2, radius, paint);
}
}
width and height of the view have not been yet initialized when getWidth() and getHeight() are called, just use getWidth() and getHeight() in onDraw:
canvas.drawCircle(getWidth()/2, getHeight()/2, 100, paint);
You can also override onSizeChanged and get view width and height.
PS: do not create anything in onDraw, create the paint object in the constructor.
public void drawCircle(Graphics2D g, int x, int y, int radius) {
x = x-(radius/2);
y = y-(radius/2);
g.fillOval(x,y,radius,radius);
}
here x,y is the position of canvas where you want to draw circle and you can find it with motion listener if you want to set x,y position dynamically hope this will help you
There are some links which are very useful for us and I hope they will work for you and other.
https://github.com/swapgo20/Android-Hand-Drawing
https://github.com/codepath/android_guides/wiki/Basic-Painting-with-Views
https://github.com/Korilakkuma/CanvasView
I hope above links are very useful to draw shapes on canvas.
I suggest you use third link and use only Path class (http://developer.android.com/reference/android/graphics/Path.html) of android to draw shapes.

canvas drawtext direction

how to make that text was written vertically? how to rotate text 90 degrees?
Write each letter individually is stupid, but now ,i don't know another way.
Paint paint = new Paint();
public DrawView(Context context, double arr[])
{
super(context);
paint.setColor(Color.BLACK);
}
#Override
public void onDraw(Canvas canvas)
{
canvas.drawText("Test",50, 50, paint);
}
Simply rotating text (or anything else) is easy: Use the rotate() method to rotate the canvas (afterwards it is rotated back, otherwise everything you draw becomes rotated):
canvas.save();
canvas.rotate(90f, 50, 50);
canvas.drawText("Text",50, 50, paint);
canvas.restore();
The save() and restore()methods respectively save the state of the canvas and restores it. So the rest of your drawn elements are not rotated. If you only want to paint the text these two methods are not necessary.
If you want to put the characters of the string under each other, you need to process each character separately. First you'd need to obtain the font height and when drawing each character you need to increase the y-coordinate with this height over and over again.
int y = 50;
int fontHeight = 12; // I am (currently) too lazy to properly request the fontHeight and it does not matter for this example :P
for(char c: "Text".toCharArray()) {
canvas.drawText(c, 50, y, paint);
y += fontHeight;
}
Correct version is :
Canvas canvas_front = new Canvas(bitmap_front);
Paint paint = new Paint();
paint.setColor(Color.rgb(140, 0, 0));
paint.setAlpha(80);
paint.setStrokeWidth(2);
canvas_front.drawLine(0, (float) (frontIV.getHeight() * 0.9),frontIV.getWidth(), (float) (frontIV.getHeight() * 0.9), paint);
canvas_front.save();
canvas_front.rotate((float) 90 , 50, 50);
canvas_front.drawText("Text",50, 50, paint);
canvas_front.restore();
frontIV.setImageBitmap(bitmap_front);

Compass with bitmap is going everywhere in the screen

i want my compass to spin like this
but my result is that:
the compass is going everywhere in my screen...
where is my problem please?this is my compass.java code:
#Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.GRAY);
int w = canvas.getWidth();
int h = canvas.getHeight();
int cw = w / 2;
int ch = h / 2;
Bitmap myBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.compass);
canvas.translate(cw, ch);
if (mValues != null) {
canvas.rotate(-mValues[0]);
}
int cx = (mWidth - myBitmap.getWidth()) / 2;
int cy = (mHeight - myBitmap.getHeight()) / 2;
canvas.drawBitmap(myBitmap, cx, cy, null);
}
p.s.: i m sorry for the bad pictures but i really dont know how to explain my problem in english!Thanks
Since you have already translated to the center of the canvas, you may only need to offset the compass with its half width/height to center it. Try:
int cx = -myBitmap.getWidth() / 2;
int cy = -myBitmap.getHeight() / 2;
canvas.drawBitmap(myBitmap, cx, cy, null);
Also to get a good hang of transformations (translate, rotate), read The OpenGL Red book chapter 3, specifically the part Thinking about Transformations. While this is about OpenGL, you can use the knowledge for non-OpenGL transforms too.
EDIT:
Think in turtle logic. Your first translation takes your pencil to the center of your canvas. The rotation rotates your pencil. So now you could draw the compass exactly where the pencil is (no offsets), except that drawing the compass image is done starting from its top-left corner instead of its center. Therefore you need a last translation of (-compassWidth/2, -compassHeight/2). Note that this translation already occurs on the rotated x & y axes. Also note that you may pass 0/0 for cx/cy in drawBitmap if you manually apply that translation to the canvas itself.
So the full sequence is: translate to canvas center, rotate, translate negated to image center.
Don't decode the Bitmap in onDraw - do it when the view is created and reuse the Bitmap.
Make a Matrix and matrix.postRotate(mValues[0], half_width_of_bitmap, half_height_of_bitmap); and matrix.postTranslate(cw, ch);
Draw the bitmap with canvas.drawBitmap(bitmap, matrix, null);

Categories

Resources