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 use drawLines() on my dataset of points. It's drawn perfectly when it starts from (0,0), but when the dataset starts from another point it produces some gaps.
Here the chart starting with (0, 0) (added small line at left bottom):
Here it starts from from (83, 56) and produces gap:
What I've tried:
Setting on/off anti-alias.
Different datasets (they all produce gap at the top y point if there is nothing drawn at (0, 0).
I've read about trying drawPath(), but since I need to draw lots of lines it was said to be not as effecient as drawLines().
Here are some snippets from my project:
My dataset:
x = {
0,12,83,84,84,121,121,128,128,151,151,173,173,203,203,217,217,224,224,229,229,253,253,294,294,305,305,331,331,355,355,364,364,409,409,411,411,416,416,431,431,448,448,497,497,504,504,508,508,536,536,582,582,586,586,630,630,646,646,660,660,689,689,728,728,761,761,768,768,798,798,822,822,860,860,894,894,908,908,952,952,996,996,1039,1039,1085,1085,1085,1085,1099,1099,1119,1119,1133,1133,1169
}
y = {
0,12,56,115,115,220,220,232,232,170,170,350,350,117,117,157,157,205,205,290,290,184,184,127,127,181,181,231,231,210,210,278,278,142,142,120,120,299,299,29,29,290,290,50,50,258,258,127,127,203,203,168,168,27,27,27,27,83,83,116,116,228,228,295,295,62,62,299,299,121,121,216,216,266,266,164,164,234,234,116,116,182,182,130,130,208,208,218,218,202,202,85,85,59,59,114
}
How I set my Paint:
private void init() {
chartPaint = new Paint();
chartPaint.setStyle(Paint.Style.STROKE);
chartPaint.setStrokeWidth(4);
chartPaint.setColor(color);
}
My onDraw():
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
// let chart start from the most left without gaps
canvas.translate(0, viewHeight);
// TODO: find place to move calcs outside onDraw()
final float xScale = (float) viewWidth / (Util.getMax(x) - Util.getMin(x));
final float yScale = (float) -viewHeight / (Util.getMax(y) - Util.getMin(y));
valuesScaled = new float[values.length];
for (int i = 0; i < values.length; i++) {
valuesScaled[i] = (i%2==0) ? values[i] * xScale : values[i] * yScale;
}
canvas.drawLines(valuesScaled, chartPaint);
canvas.restore();
}
values array is where x and y arrays are stored alternatively, i.e. first 4 elements of values are {x[0], y[0], x[1], y[1], ...}
viewWidth and viewHeight are taken from onSizeChange().
I have canvas which draws pie chart for me. After I change radio button to disagree position, my pie chart has to show different values. I am doing this with Listener (setted up on radio group). It is working but for updating the canvas (not adding and overdrawing, I have to clear it first and redraw with new values) I use this:
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
Under the piechart I want to write number of percent. It is working without that line what is on top. But after I execute the line, only piechart is redrawn (with new values) but text with percentage is not shown (even if I set text to that again):
if (checkedId == R.id.agreeRadio) {
values[0] += 1;
values[1] -= 1;
setupPieChart();
setupPercentageValueToGraph();
}
Method calculating percentage and writing it:
public void setupPercentageValueToGraph() {
float[] degrees = calculateData(values);
// get percentage number from values
float percentage = (degrees[0] / (degrees[0] + degrees[1])) * 100;
// setup color of the number shown red/green
if (percentage >= 50) {
percentageTV.setTextColor(Color.parseColor("#47B243"));
} else {
percentageTV.setTextColor(Color.parseColor("#DB262A"));
}
// set the text
percentageTV.setText((int) percentage + "%");
}
Method to set piechart to linearLayout:
public void setupPieChart() {
float[] degrees = calculateData(values);
graphLayout.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
graphLayout.addView(new MyGraphView(this, degrees, size));
}
Class which is drawing the piechart where that clear is used:
public class MyGraphView extends View {
public static final int PADDING = 4;
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private float[] value_degree;
int strokeWidth;
private int[] COLORS = { Color.parseColor("#47B243"),
Color.parseColor("#DB262A") };
// size of bigger half circle
RectF rectf = new RectF(2, 2, 62, 62);
// size of smaller half circle
RectF rectf2 = new RectF(9, 9, 55, 55);
// size of the smallest half circle
RectF rectf3 = new RectF(16, 16, 48, 48);
int temp = 0;
public MyGraphView(Context context, float[] values, int size) {
super(context);
// setting up size of pie chart dynamically
int difference = size / 9;
rectf.set(PADDING, PADDING, size + PADDING, size + PADDING);
rectf2.set(difference + PADDING, difference + PADDING, size
- difference + PADDING, size - difference + PADDING);
rectf3.set(difference * 2 + PADDING, difference * 2 + PADDING, size
- difference * 2 + PADDING, size - difference * 2 +
PADDING);
// setting up brush size
strokeWidth = size / 15;
// assign degrees of agree and disagree to array
value_degree = new float[values.length];
for (int i = 0; i < values.length; i++) {
value_degree[i] = values[i];
}
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
//canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
for (int i = 0; i < value_degree.length; i++) {
// set type of "brush"
paint.setStrokeWidth(strokeWidth);
paint.setStyle(Paint.Style.STROKE);
// set shadow
paint.setShadowLayer(2, 1, 1, Color.BLACK);
// setLayerType(LAYER_TYPE_SOFTWARE, paint);
// agree
if (i == 0) {
final Path path = new Path();
paint.setColor(COLORS[i]);
// draw 3 paths to show 3 curves
path.addArc(rectf, 180, value_degree[i] - 4);
path.addArc(rectf2, 180, value_degree[i] - 5);
path.addArc(rectf3, 180, value_degree[i] - 6);
// draw the path
canvas.drawPath(path, paint);
// disagree
} else {
temp += (int) value_degree[i - 1];
paint.setColor(COLORS[i]);
final Path path = new Path();
path.addArc(rectf, temp + 180 + 4, value_degree[i] - 4);
path.addArc(rectf2, temp + 180 + 5, value_degree[i] - 5);
path.addArc(rectf3, temp + 180 + 6, value_degree[i] - 6);
// draw the path
canvas.drawPath(path, paint);
}
}
}
}
Any idea why text is not written? Or any other way how to update piechart after radiobutton is changed?
Finally I found a way how to do it. To draw the text on the same canvas as graph was drawn:
// draw the number of percent under piechart
// setup brush
Paint paint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
// set shadow
paint2.setShadowLayer(3, 2, 2, Color.parseColor("#404040"));
// fill it not stroke
paint2.setStyle(Style.FILL);
// set text family + make it bold
paint2.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
// convert dp to pixels and setup the size of text
int MY_DIP_VALUE = 23; // 5dp
int pixel = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, MY_DIP_VALUE,
getResources()
.getDisplayMetrics());
// setup color of the number shown red/green
if (percent >= 50) {
paint2.setColor(COLORS[0]);
} else {
paint2.setColor(COLORS[1]);
}
// draw the text
paint2.setTextSize(pixel);
float textWidth = paint2.measureText(percent + "%");
canvas.drawText(percent + "%", size / 2 - textWidth / 2 + PADDING,
size, paint2);
I have removed TextView and did few little changes, but the key was to draw it. Still dont understand why I was not able to write text on it but had to draw but it is working!
This is the onDraw method I have:
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(bitmap, 0, 0, mBitmapPaint); // Deseneaza Bitmapul mutabil
if (shapes.size() > 0) {
for (ShapeFromLines shapesfromline : shapes) {
if (shapesfromline.size() > 1) {
LogService.log("", "shapes: " + shapesfromline.size());
float startx, starty;
startx = shapesfromline.get(0).stopX;
starty = shapesfromline.get(0).stopY;
for (int i = 1; i < shapesfromline.size(); i++) {
LogService.log("", "----size color in ondraw: " + shapesfromline.get(i).getPaint().getColor());
canvas.drawLine(startx, starty, shapesfromline.get(i).getStopX(), shapesfromline.get(i).stopY, shapesfromline.get(i).getPaint());
LogService.log("", "shapes: drawn");
startx = shapesfromline.get(i).stopX;
starty = shapesfromline.get(i).stopY;
}
}
}
}
for (int i = 0; i < bitmaps.size(); i++) {
if ((bitmaps.get(i).bitmap != null)) {
canvas.save();
canvas.rotate(bitmaps.get(i).rectrotateVal, bitmaps.get(i).pX + (bitmaps.get(i).bitmap.getWidth() / 2), bitmaps.get(i).pY + (bitmaps.get(i).bitmap.getHeight() / 2));
mBitmapPaint.setAlpha(bitmaps.get(i).alpha);
// canvas.drawRect(bitmaps.get(i).rect, cPaint);
canvas.drawBitmap(bitmaps.get(i).bitmap, bitmaps.get(i).pX, bitmaps.get(i).pY, mBitmapPaint);
canvas.restore();
}
}
mBitmapPaint.setAlpha(255);
canvas.drawPath(mPath, paint);
}
}
As you can I I have some Shapes (each shape is created from an arraylist of points named ShapesFromLines. Now the first point of my shape is from the touchDown (ontouchevent), where I save the current location. then the other points are saved on touch up. When you press the screen the first time it will draw a point, then the second time, it will connect those 2 points, then 3, etc. I save the X,Y and Paint on each point.
As you can see, I have a logservice on draw, which returns the paint value for those points. Now I have a function with a color picker that changes the color of the current path. But this manages to change the color of all my straight lines. Now I checked, when I want to add a picture, the main paint is set to transparent. And then the lines are transparent, but the points (that should be connected) have the right color. Any ideea what could be wrong?
when I created the objects, i passed the paint as a reference, and when changing it, it would change the paint also.
I did this to fix it:
Paint linepaint = new Paint();
linepaint.setColor(paint.getColor());
linepaint.setAlpha(paint.getAlpha());
linepaint.setAntiAlias(true);
linepaint.setDither(true);
linepaint.setStyle(paint.getStyle());
linepaint.setStrokeJoin(paint.getStrokeJoin());
linepaint.setStrokeCap(paint.getStrokeCap());
linepaint.setStrokeWidth(paint.getStrokeWidth());
pointsForLines.add(new Points(stopX, stopY, linepaint));
Where paint was the global paint I was using before