Android:Why endless loop in onDraw()? - android

I want to draw several pie chart by using a for loop, and the data is stored in an array (data[][][]). When each chart draw is completed, I make the top and bottom plus 450 (top+=450; bottom+=450;) to avoid the charts to overlap. But the result is that a pie chart moves to the bottom constantly. I try to add invalidate() at the end of line but it doesn't work.
private String data[][][]=... //store some data
private float left=50,top=100,right=450, bottom=500;
public void onDraw(Canvas c) {
super.onDraw(c);
Paint paint = new Paint();
RectF rec;
for(int j=0;j<data.length;j++){
rec=new RectF(left,top,right,bottom);
//draw a pie chart
for (int i = 0; i < data[j].length; i++) {
float drawDegree = (float)Integer.parseInt(data[j][i][1])/100* 360f;
c.drawArc(rec, -90, drawDegree, true, paint);
}
top+=450;
bottom+=450;
}
}

top and bottom are instance variables. Try to make them local, move them to onDraw method. Now you change them every time onDraw called and they don't reset to top=100,bottom=500 after onDraw finished.

The title of your question is pretty misleading... does not sound like there is really any endless loop there.
You should initialize top and bottom in your onDraw function. So you start from the "top" of the screen every time you draw.
Also, you should try to not allocate a new Rect for every pie chart.
Try to allocate one, use offsetTo(50,100) and then use offset(0, 450) to move it down.
Calling invalidate() every time will just make your pie charts being drawn again and again...probably will not do much good.

Related

MPAndroidChart line chart label attached to the line

I want to achieve an effect similar to this using the MPAndroidChart library
I want my label to be attached to the Chart line or at least be close to it. Is something like this possible using this library? Or is there a walkaround for this? Something that will have a similar result
A possible way is to use the marker, chart.setMarker(), but marker only shows when user tap on the chart, if you want the marker to show constantly, an custom chart can be invoked and override the function drawMarkers like this, the marker is drawn in the middle of chart:
#Override protected void drawMarkers(Canvas canvas) {
int j = 0;
IDataSet set = mData.getDataSetByIndex(j);
int i = set.getEntryCount() / 3;
Entry e = set.getEntryForIndex(i);
Highlight highlight = new Highlight(e.getX(), e.getY(), i);
highlight.setDataIndex(j);
// callbacks to update the content
mMarker.refreshContent(e, highlight);
MPPointD pix = getTransformer(set.getAxisDependency()).getPixelForValues(e.getX(), e.getY());
highlight.setDraw((float)pix.x, (float)pix.y);
// draw the marker
float[] pos = getMarkerPosition(highlight);
mMarker.draw(canvas, pos[0], pos[1]);
}
Picture as below, the yellow tag is the marker, it is displayed constantly and without a user tap:
----Updated July 5th
In the demo LineChartActivity1(https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java), line 98, chart.setMarker(mv); sets the Marker, and it allocated by new an MyMarkerView. The drawMarkers can be added at https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartLib/src/main/java/com/github/mikephil/charting/charts/LineChart.java, but it's better to use a customed Chart extends from LineChart

Can you draw multiple Bitmaps on a single View Method?

currently I am trying to make an animation where some fish move around. I have successfully add one fish and made it animate using canvas and Bitmap. But currently I am trying to add a background that I made in Photoshop and whenever I add it in as a bitmap and draw it to the canvas no background shows up and the fish starts to lag across the screen. I was wondering if I needed to make a new View class and draw on a different canvas or if I could use the same one? Thank you for the help!
Here is the code in case you guys are interested:
public class Fish extends View {
Bitmap bitmap;
float x, y;
public Fish(Context context) {
super(context);
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.fish1);
x = 0;
y = 0;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(bitmap, x, y, null);
if (x < canvas.getWidth())
{
x += 7;
}else{
x = 0;
}
invalidate();
}
}
You can draw as many bitmaps as you like. Each will overlay the prior. Thus, draw your background first, then draw your other images. Be sure that in your main images, you use transparent pixels where you want the background to show through.
In your code, don't call Invalidate() - that's what causes Android to call onDraw() and should only be called from somewhere else when some data has changed and needs to be redrawn.
You can do something like this, where theView is the view containing your animation:
In your activity, put this code in onCreate()
myAnimation();
Then
private void myAnimation()
{
int millis = 50; // milliseconds between displaying frames
theView.postDelayed (new Runnable ()
{
#Override public void run()
{
theView.invalidate();
myAnimation(); // you can add a conditional here to stop the animation
}
}, millis);
}

How to Draw a point on over a bitmap image drawn on a canvas by specifying the x and y co-ordinate in android?

I have drawn a bitmap image over a canvas.
Bitmap image = BitmapFactory.decodeResource(getResources(), R.drawable.sq);
canvas.drawColor(color.black);
Rect dstRectForRender = new Rect(0,0,320,450);
canvas.drawBitmap(image, null,dstRectForRender,null);
The image gets displayed based on my screnn on a cnavs.
On my touch input, I need to pass the x and y co-ordinate position of the image and fill that pixel with a color to show that the image is painted on a drag event.
How can I pass the x and y coo-ordinate parameters? Which functions should I use to plot the pixels on the image?
I appreciate your help and sweet time.
I'm not sure if this is the best way to do this, but it would be much easier to do this if you defined your own subclass of ImageView and name it something like DrawableImageView. You'd have to make sure you implement all the basic constructors from ImageView, then override the onTouchEvent method. From that event you can get the touch coordinates and store them in an ArrayList<Point> and use that ArrayList by overriding the onDraw method and "painting" the image.
public class DrawableImageView extends ImageView {
ArrayList<Point> list = new ArrayList<Point>();
//constructors..
#Override
public boolean onTouchEvent (MotionEvent event) {
float x = event.getX();
float y = event.getY();
list.add(new Point(x,y));
invalidate();
}
This is just a very brief overview of how to start your class, and may not be the most accurate way of doing things (depending on your specific code). Now, instead of using <ImageView> tags in your xml (or, loading an ImageView programatically), you refer to your subclass like so:
<your.package.name.DrawableImageView
/>
Edit
In response to your comment, there is no predetermined way to draw over an image. You must implement this yourself, which is why I recommended storing Points in an ArrayList. I'm not really sure what you're trying to achieve here, but to draw (for example) black dots over an image you have to override onDraw:
public void onDraw(Canvas c) {
super.onDraw(c);
for(Point p : list) {
//Draw black point at x and y.. I'm posting from my cell so I can't go into much detail
}
}
Also, to force a View to redraw itself you need to use the invalidate() method in your onTouchEvent() (which I've added above).

Drawing to canvas onDraw works, drawing onTouchEvent doesn't

I've fooling around with 2D graphics in the Android SDK and I'm having trouble with what should be a simple example.
I'm assuming that I'm just misunderstanding something fundamental/basic.
public class DrawView extends View {
Paint paint = new Paint();
Canvas canvas = new Canvas();
public DrawView(Context context) {
super(context);
paint.setColor(Color.BLACK);
}
#Override
public void onDraw(Canvas canvas) {
this.canvas = canvas;
this.canvas.drawLine(0,0, 500, 500, paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
Log.d("DrawView", "onTouchEvent: " + event.getX() + "," + event.getY() );
canvas.drawLine(0,500, 500, 0, paint);
return true;
}
}
The code above draws a single line from 0,0 to 500,500 when the app start. That parts works just fine.
The issue is that the second line isn't drawn on the touch event. The onTouchEvent is definitely being called because I see the coordinates debug message in the log.
Can someone point out what silly thing I'm doing wrong?
You're supposed to call invalidate() at the end of onTouchEvent() to tell the system to update the screen. Calling invalidate() will call onDraw().
Also, what is fundamentally wrong is that you create a canvas in this class you have. That does absolutely nothing for you. The canvas to draw in is the one that you get from the onDraw() method. The call to canvas.drawLine() in onTouchevent isn't doing anything for you and shouldn't be there. That is an empty canvas and isn't the one that will get "posted."
In onTouchEvent() you should only gather the touch event data, and also do some processing on it if you need to. You shouldn't make any calls to drawing methods there. However, as I said, if you want to trigger a draw from onTouchEvent(), you call invalidate(). If you want to draw lines based on where you are touching, you will need to create class variables that are X and Y coordinates. You update these X and Y variables in onTouchEvent(), and then you use them in onDraw() to draw whatever you need based on these X and y variables.
Call postInvalidate() function. This function inform that view should be redrawed (event loop call onDraw() function).
You can declare a bool variable in your class, so that you can pass it to your ondraw() method that the user has touched and also pass X and Y with other float variables to ondraw() methode !
But you have to vall invalidate in onTouchEvet() so that the system will redraw the canvas using your new touch orders!

How I can draw an existent Canvas on another Canvas?

Now I need to draw a new Canvas in an existent Canvas (the parent Canvas), like Bitmap's works.
The logic is this:
Each Sprite can have others Sprite nodes (LinkedList<Sprite> nodes);
If don't have nodes (nodes = null) then, will draw the (Bitmap) Sprite.image directly to parent canvas, received on update method, like (currently working):
public void update(Canvas canvas){
if(this.nodes == null){
canvas.drawBitmap(this.image, ...);
}
...
}
If it have nodes, I need create a new Canvas, draw nodes bitmap on this, and draw the new Canvas on parent Canvas. This don't works, but the idea is like this:
public void update(Canvas canvas){
...
else { // this.nodes != null
Canvas newCanvas = new Canvas();
for(Sprite node: this.nodes){
node.update(newCanvas);
}
// Canvas working like Bitmap! (?)
canvas.drawCanvas(newCanvas, this.x, this.y, this.paint);
}
}
Real example usage: well, I have a Sprite called CarSprite. The CarSprite have a private class called WheelSprite extends Sprite. The CarSprite implements two nodes (two dimensional example) of WheelSprite, that will be positioned in the CarSprite to seems the whell of car. So far, no problem.
But if CarSprite is affected by a devil one object in the level, this will be turn transparent. I will do, for instance, objectOfCarSprite.paint.setAlpha(127);. The problem is, if I do it (currently working mode), only the car bitmap will turn transparent, but not the well, because it is drawed directly to global Canvas object.
This part does not influence much: currently, I'm getting the parent (of parentN...) Paint object alpha, and with some math, I get a transparent solution to wheel that works almost as well I need, but if, for instance, the WheelSprite object is over car bitmap, is possible to see the ground of car, like picture.
Exists a better way to do like I want, or only like work currently?
I do it. But is very slow, but works fine.
final Bitmap split = Bitmap.createBitmap(GameActivity.screenWidth,
GameActivity.screenHeight,
Bitmap.Config.ARGB_8888);
final Canvas subCanvas = new Canvas(split);
for(final Sprite sprite: this.nodes) {
sprite.update(subCanvas);
}
canvas.drawBitmap(split, this.x, this.y, this.paint);
First we need have a Canvas object (here just canvas);
After, we need make a Bitmap where we will draw. This need get a width and height from current canvas. I used a shortcut on this case;
Next, we need make a new Canvas, it will be passed to Sprite.nodes;
After all, we need draw the bitmap on global canvas;
Note: this works very well, but affect to much the speed.

Categories

Resources