I know that creating objects inside the onDraw method is very costly. I want to draw a matrix of rounded rectangles, which coordinates are dynamic, and I can't cache all that rectangles, because I use a scroll view and there may be a lot of rectangles, there's no other overload for drawRoundRect method, which has primitive arguments, and I forced to create a Rectangle object in every iteration. Who can suggest an effective solution for that?
#Override
protected void onDraw(Canvas canvas) {
int h = getMeasuredHeight();
int tileSize = h / rows;
for(int i = 0; i < rows; ++i) {
for(int j = 0; j < columns; ++j) {
int x = j * tileSize;
int y = i * tileSize;
canvas.drawRoundRect(new RectF(x, y, x + tileSize, y + tileSize), 10, 10, tilePaint);
}
}
}
This is a just an example, rectangles can have arbitrary coordinates.
RectF has the set(left, top, right, bottom) method. You could allocate it on the constructor and it this method to change the Rectf's bounds.
mRect.set(x, y, x + tileSize, y + tileSize);
where mRect is RectF mRect = new RectF();
Related
Requirement is to keep a ball moving on the Grid path generated in Canvas. I have generated a Grid in canvas but not able to understand how to move the ball randomly means starting point show be different on the path. I am sharing what I have done. I have also plotted the ball in the screen but not getting the point how to put the ball exactly on the grid line randomly and start moving it
public class PixelGridView extends View {
//number of row and column
int horizontalGridCount = 11;
private Drawable horiz;
private Drawable vert;
private final float width;
long mInterpolateTime;
PointF mImageSource = new PointF();
public PixelGridView(#NonNull Context context) {
this(context, null);
}
public PixelGridView(#NonNull Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
horiz = new ColorDrawable(Color.WHITE);
horiz.setAlpha(160);
vert = new ColorDrawable(Color.WHITE);
vert.setAlpha(160);
width = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, .9f, context.getResources().getDisplayMetrics());
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
horiz.setBounds(left, 0, right, (int) width);
vert.setBounds(0, top, (int) width, bottom);
}
private float getLinePosition(int lineNumber) {
int lineCount = horizontalGridCount;
return (1f / (lineCount + 1)) * (lineNumber + 1f);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//drawTask.start();
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.WHITE);
paint.setAntiAlias(true);
canvas.drawCircle(120, 110, 10, paint);
int count = horizontalGridCount;
for (int n = 0; n < count; n++) {
float pos = getLinePosition(n);
// Draw horizontal line
canvas.translate(0, pos * getHeight());
Log.e("Position1", "" + pos * getHeight());
horiz.draw(canvas);
canvas.translate(0, -pos * getHeight());
// Draw vertical line
canvas.translate(pos * getHeight(), 0);
Log.e("Position2", "" + pos * getHeight());
vert.draw(canvas);
canvas.translate(-pos * getHeight(), 0);
}
}
}[![Canvas Image][1]][1]
//MainActivity
public class PathAnimationActivity extends AppCompatActivity {
LinearLayout rlLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_path);
rlLayout=findViewById(R.id.rlLayout);
PixelGridView pixelGrid = new PixelGridView(this);
rlLayout.addView(pixelGrid);
}
}
First thing i have noticed it that you haven't used invalidate (); at the end because thats critical in order to animate the canvas ( redraw the frames ) so please include that .
there may be several ways to achieve what you want follows is my idea
this canvas need to be divided into multiple x , y divided planes as follows and save them in array of points which you can randomize and give those points to ball to move ,
Step 1, get the canvas size
step 2, divide is in x and y coordinate depending on size of each device varies so you need to control that factor via Screen size
step 3, save the coordinates in matrix or array
step 4, set position of balls from those arrays values ( randomly can you define the random limits as per the max and min values of x and y from the coordinates division .
example , function move will take ball object and x, y are the positions move (ball, x, y ); and you can randomize the x and y based on max and min limits of your coordinates division example total y lines and total x lines values
in order to get an idea about how to move the ball on canvas you can see this code here : https://github.com/pintspin/ball_animation
I need to create ViewGroup custom shape. Its may be FrameLayout.It should look like
Its must be ViewGroup cause I need to add text or image inside.How can i do this? Thanks a lot.
There is a good tutorial here on creating custom polygon shapes. It is a fairly long process, but you will get there. In short, you will have to create custom XML attributes for a custom View.
The real magic is here:
#Override
protected void onDraw(Canvas canvas) {
int measuredWidth = getMeasuredWidth();
int measuredHeight = getMeasuredHeight();
int x = (measuredWidth/2) ;
int y = (measuredHeight/2) ;
int radius = Math.min(x,y) ;
if (sides < 3) return;
float a = (float) (Math.PI * 2)/sides;
int workingRadius = radius;
polyPath.reset();
// The poly is created as a shape in a path.
// If there is a hole in the poly, draw a 2nd shape inset from the first
for(int j = 0; j < ((fillPercent < 100) ? 2 : 1) ; j++){
polyPath.moveTo(workingRadius,0);
for (int i = 1; i < sides; i++) {
polyPath.lineTo((float)(workingRadius*Math.cos(a*i)),
(float)(workingRadius*Math.sin(a*i)));
}
polyPath.close();
workingRadius -= radius * fillPercent;
a = -a;
}
canvas.save();
canvas.translate(x, y);
canvas.rotate(startAngle);
canvas.drawPath(polyPath, fillPaint);
canvas.restore();
if(showInscribedCircle){
canvas.drawCircle(x,y,radius, inscribedCirclePaint);
}
super.onDraw(canvas);
}
This question is in continuation to my previous doubt
Now I am trying to make a guage view. I am drawing a scale but the alignment is not proper and I am not able to figure out the problem. Here is my code:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
w= canvas.getWidth();
h=canvas.getHeight();
DrawRange(canvas,innerRadius,outerRadius);
}
here innerRadius =250;
and outer radius = 300;
private void DrawRange(Canvas canvas,int r,int R) {
RectF rect = new RectF(canvas.getWidth()/2- r, canvas.getHeight()/2-r, canvas.getWidth()/2+r, canvas.getHeight()/2+r);
RectF rect1 = new RectF(canvas.getWidth()/2- R, canvas.getHeight()/2-R, canvas.getWidth()/2+R, canvas.getHeight()/2+R);
Paint scalePaint = new Paint();
scalePaint.setStyle(Paint.Style.STROKE);
scalePaint.setColor(0x9f004d0f);
scalePaint.setStrokeWidth(2);
scalePaint.setAntiAlias(true);
scalePaint.setTextSize(35.0f);
scalePaint.setTypeface(Typeface.SANS_SERIF);
scalePaint.setTextScaleX(0.4f);
scalePaint.setTextAlign(Paint.Align.CENTER);
canvas.drawOval(rect1, scalePaint);
canvas.drawOval(rect, scalePaint);
canvas.save(Canvas.CLIP_SAVE_FLAG);
int xc = 0;
for (int i = 0; i < totalNicks; i++) {
float y1 = 330;
float y2 = y1 + 5;
if (i % 5 == 0) {
canvas.drawText(""+xc, r-15, y2 , scalePaint);
xc+=5;
}else{
canvas.drawLine(r, y1, r, y2, scalePaint);
}
canvas.rotate(degreesPerNick, w/2, h/2);
}
canvas.restore();
}
I wonder if you are drawing the text and the dashes in the wrong place. The key reference point is the centre of the circles:
int cX = canvas.getWidth()/2;
int cY = canvas.getHeight()/2;
The other key reference is the difference between the two radii:
int deltaR = R - r;
The dashes and text are always drawn at 12 o'clock, say 20% above the inner circle to 1/3 of the way from the outer circle:
int dashInnerY = cY - r - deltaR/5; // 20% of the way between inner and outer radii
int dashOuterY = cY - R + deltaR/3; // 1/3 of the way between outer and inner radii
Then to render a dash:
canvas.drawLine(cX, dashInnerY, cX, dashOuterY, scalePaint);
And the number:
canvas.drawText(""+xc, cX, dashInnerY, scalePaint);
I just got started with Android and I was wondering how to use listeners in a canvas. Particularly as part of my project, the objective is for an event to occur when we drag click from one point to another. The concept is from a game called brainvita. The game need not be understood to answer my question. All I want to know is the simplest way to make a listener for the drag clicks from one point to another on the canvas?
Do I need to map the canvas to a grid and have multiple listeners? What is the simplest way?
Additionally I'm attaching the code of the game developed so far, just the basics that help in displaying the grid!
package com.project.android.brainvita;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.View;
public class GameView extends View {
Paint paint = new Paint();
Paint paintF = new Paint();
Paint paintG = new Paint();
int width, height;
int MARGIN = 4;
int MARGIN_BIG = 10;
int sx = 2;
public GameView(Context context) {
super(context);
paint.setColor(Color.WHITE);
paintF.setColor(Color.rgb(40, 60, 204));
paintG.setColor(Color.rgb(240, 30, 20));
}
public void onDraw(Canvas canvas) {
// Draw external circle
canvas.drawCircle(width / 2, height / 2, (width - MARGIN) / 2, paintF);
// Calculate radius of small circles
int radius = (width - MARGIN_BIG*2) / 14;
// Draw grid
for (int j = -3; j <= 3; j++) {
if (j >= -1 && j <= 1)
for (int i = -3; i <= 3; i++) {
canvas.drawCircle(width / 2 + (2 * radius * i), height / 2
+ (2 * radius * j), radius - sx, paint);
}
else
for (int i = -1; i <= 1; i++) {
canvas.drawCircle(width / 2 + (2 * radius * i), height / 2
+ (2 * radius * j), radius - sx, paint);
}
}
}
protected void onSizeChanged(int w, int h, int ow, int oh) {
width = w;
height = h;
}
}
In android development, a canvas is used for drawing. A view is used for interacting with the user. There isn't a direct mechanism for a canvas to receive user input. This must be handled through a view.
You'll want to add an onTouchListener to the View that is hosting your canvas and use that listener to store state information about touches from the user.
The following is supposed to draw an axis in the middle of the screen. However, nothing appears. I am positive that is has to do with my Paths.
#Override
protected void onDraw(Canvas canvas) {
//Variables declared here temporarily for testing purposes
int canterX = getWidth() /2;
int centerY = getHeight() /2;
int radius = 150;
Path verticalAxis = new Path();
Path horizontalAxis = new Path();
drawAxis();
}
private void drawAxis(Canvas canvas) {
int axisLineThickness = 1;
int verticalEndX;
int verticalEndY;
int horizontalEndX;
int horizontalEndY;
Paint axisPaint = new Paint();
axisPaint.setColor(Color.WHITE);
axisPaint.setStrokeWidth(axisLineThickness);
double theta;
for(int i = 90; i < 360; i += 180) {
theta = toRadians(i);
verticalEndX = centerX + (int) ((cos(theta)) * radius);
verticalEndY = centerY + (int) ((sin(theta)) * radius);
verticalAxis.moveTo(centerX, centerY);
verticalAxis.lineTo(verticalEndX, verticalEndY);
}
canvas.drawPath(verticalAxis, axisColor);
for(int i = 90; i < 360; i += 180) {
theta = toRadians(i);
horizontalEndX = centerX + (int) ((cos(theta)) * radius);
horizontalEndY = centerY + (int) ((sin(theta)) * radius);
horizontalAxis.moveTo(centerX, centerY);
horizontalAxis.lineTo(verticalEndX, verticalEndY);
}
canvas.drawPath(horizontalAxis, axisColor);
}
I know I can make the axis draw if I add the following to the vertical and horizontal for loops respectively:
Vertical For Loop:
canvas.drawLine(centerX, centerY, verticalEndX, verticalEndY, paint);
Horizontal For Loop:
canvas.drawLine(centerX, centerY, horizontalEndX, horizontalEndY, paint);
But I don't want to solve the issue this way, I want to correct what is wrong with my paths. Can anyone tell me why the points aren't adding to my path correctly? The loop should only go through twice which creates a line for each side of the axis. Ie. One loop creates the top of the vertical axis and the second loop creates the bottom part.
How do I get my paths create that full line and then draw it outside of the loop?
Paint's default style appears to be FILL, so maybe just having a line in your path is confusing things. Try setting it to STROKE:
axisPaint.setStyle(Paint.Style.STROKE);
See Paint.Style