Hello All first post on Stack overflow!!
I am attempting to draw multiple lines on a canvas but using different colours. My problem is that the line colours are always the same as the last line in the array. I am drawing vertical lines progressively across the screen at 30Hz, similar to a bar chart without any spacing. I am calling view. Invalidate() for the view onDraw() to run and draw the lines.
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
if (plotInfo == null)
return;
for (int i = 0; i < plotInfo.length; i++) {
//paintlineinfo = String.valueOf( plotInfo[i].paintOfLine.getColor());
canvas.drawLine(i, mDisplay.getHeight(), i, mDisplay.getHeight()-plotInfo[i].linePositionY, plotInfo[i].paintOfLine);
}
}
I believe I may to use Open GL but I am trying to avoid it for the moment unless anyone can point me in the right direction for a good article that may help me.
At some point i would like to make the lines multiple colours.
Any help would be greatly appreciated...
I don't know from there you ge the colour, try:
#Override
protected void onDraw(Canvas canvas)
{
// TODO Auto-generated method stub
super.onDraw(canvas);
if (plotInfo == null)
return;
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
for (int i = 0; i < plotInfo.length; i++) {
paint.setColor( yourArrayOfColours.get(i) );
canvas.drawLine(i, mDisplay.getHeight(), i, mDisplay.getHeight()-plotInfo[i].linePositionY, paint);
}
}
Related
Im trying to draw colors between limit lines in a LineChart, by extending the LineChart class and overriding the onDraw method. Im doing it as seen in https://github.com/PhilJay/MPAndroidChart/issues/485 where colored rectangles are simply drawn on the canvas:
public class CustomLineChart extends LineChart {
protected Paint mYAxisZonePaint;
protected String[] arrayColors = {"#FFFFFF","#F4D03F","#F5B041","#EB984E","#DC7633"};
#Override
protected void init() {
super.init();
mYAxisZonePaint = new Paint();
mYAxisZonePaint.setStyle(Paint.Style.FILL);
}
#Override
protected void onDraw(Canvas canvas) {
List<LimitLine> limitLines = mAxisLeft.getLimitLines();
if (limitLines == null || limitLines.size() < 4){
super.onDraw(canvas);
return;
}
float[] pts = new float[2];
float startPts = 0;
for (int i = 0; i < limitLines.size(); i++) {
pts[0] = limitLines.get(i).getLimit();
pts[1] = startPts;
mLeftAxisTransformer.pointValuesToPixel(pts);
mYAxisZonePaint.setColor(Color.parseColor(arrayColors[i]));
canvas.drawRect(mViewPortHandler.contentLeft(), pts[1], mViewPortHandler.contentRight(), pts[0], mYAxisZonePaint);
startPts = limitLines.get(i).getLimit();
}
super.onDraw(canvas);
}
}
With the code above im able to draw the colored rectangles as a background behind the data on the line chart as seen here
However when I scroll on the Y-axis, the rectangles will "overflow" into the area where X-axis labels are drawn as demonstrated in this image
How should i draw the colored background so it doesn't color the area where X-axis labels are drawn, similar to how the line data and limit lines are cut off just above the X-axis?
It seems like i have found a solution to my issue. Inside the onDraw method i should restrict the area that the following draw operations can write to. I added the following to my onDraw method:
#Override
protected void onDraw(Canvas canvas) {
List<LimitLine> limitLines = mAxisLeft.getLimitLines();
// Saves current canvas
int clipRestoreCount = canvas.save();
// Makes sure that the colored background cannot be drawn outside the content-rect. Otherwise the colored background would be drawn above and beneath the graph "borders".
canvas.clipRect(mViewPortHandler.getContentRect());
if (limitLines == null || limitLines.size() < 4){
super.onDraw(canvas);
return;
}
float[] pts = new float[2];
float startPts = 0;
for (int i = 0; i < limitLines.size(); i++) {
pts[0] = limitLines.get(i).getLimit();
pts[1] = startPts;
mLeftAxisTransformer.pointValuesToPixel(pts);
mYAxisZonePaint.setColor(Color.parseColor(arrayColors[i]));
canvas.drawRect(mViewPortHandler.contentLeft(), pts[1], mViewPortHandler.contentRight(), pts[0], mYAxisZonePaint);
startPts = limitLines.get(i).getLimit();
}
// Removes the clipping rectangles so rest of graph elements can be drawn (like x-axis/y-axis labels)
canvas.restoreToCount(clipRestoreCount);
super.onDraw(canvas);
}
Result:
I'm trying to draw a parabola with delay, using custom view. So far I've learned that I need to use #Override onDraw method, but 1. I can't make my parabola discrete and 2. I don't know how to program it so the shape is created step-by-step (with delay).
I also need to draw it after click of a button, so that is another complication for me. Right now I'm trying to draw a simple line step-by-step but this snippet don't work:
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(path, paint);
int x1 = 10;
int x2 = 100;
int y1 = 10;
int y2 = 100;
int diff = x2-x1;
for (int i = 0; i<diff; i++){
canvas.drawLine(x1, y1, x1+1, y1+1, paint);
x1++;
y1++;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
I can give you some tricks but please finish it your self.
You have to use a timer to refresh your component, if use this, it will refresh "onDraw" each 100ms.
private Handler handler = new Handler();
private Runnable AlarmRunnable = new Runnable() {
#Override
public void run() {
handler.postDelayed(this, 100);
invalidate();
}
};
define global variables instead of local.
int cc = 0;
int x1 = 10;
int x2 = 100;
int y1 = 10;
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (cc++ > 100) // STOP HANDLER AFTER COUNTER GETS DONE
handler.removeCallbacks(AlarmRunnable);
System.out.println("CC:" + cc);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStrokeWidth(lineWidth);
paint.setColor(Color.WHITE);
for (int i = 0; i<cc; i++){
canvas.drawLine(x1, y1, x1 + 1, y1 + 1, paint);
x1++;
y1++;
}
}
start handeler in Constructor
public YOURCOMPONENT(Context context, AttributeSet attrs) {
super(context, attrs);
handler.post(AlarmRunnable);
....
}
Rendering a Discrete Parabola
As for drawing a discrete parabola, you should draw points (or circles with a radius size of your choice, but centered at the points) along the different x and y coordinates with a larger step size.
For example, you can draw a parabola from x=-1 to x=1 with a step size of 1 by drawing at the following (x, y) points: (-1, 0), (0, 4), (1, 0).
You should make sure that the way you scale your x-axis on your graph, is in a way that there is greater than 1 pixel distance between the points to make it look discrete.
Animated onDraw
Regardless of whether your drawing logic within onDraw is correct or not, you are running a long operation with Thread.sleep() on a UI callback, which is bad practice.
Since you are drawing the whole parabola within one call of onDraw, I would assume the whole image is rendered at once rather than animated.
Looking at a similar question, you should create another thread that is in charge of a rendering loop for your custom view, to create an animation where you draw each frame.
I have a function, y = 50sin(x/50) + 100.
Now what i want to do is to draw a curve for this function, but without any additional info from the graph plotters, I just need a wave on the screen. The X should correspond to my X parameter of the screen and Y to the Y parameter of the screen.
Here is the code of my current view.
public class Sinusoid extends View {
Paint paint = new Paint();
public Sinusoid(Context context) {
super(context);
paint.setColor(Color.RED);
}
#Override
public void onDraw(Canvas canvas) {
//todo draw the line using myFunction
}
private double myFunction(double x){
return 50 * Math.sin(x / 50) + 100;
}
}
Now the question is, what should i fill in my TODO?
Please help, any documentation or example will be very useful.
Thanks in advance.
The idea is to have something like this:
#Override
public void onDraw(Canvas canvas) {
for (int x=0; x<canvas.getWidth();x++) {
canvas.drawPoint(x,(float) myFunction(x),mPaint);
}
}
But you'll eventually need to adjust the value of your function given the canvas height.
Try something like this:
float xv=x0,yv= yo,x,y;
for (int i = 0; i < maxPoints; i++) {
x=i * scalex;
y=function(i)*scaley;
canvas.drawLine(xv, yv, x, y, paint);
xv=x;
yv=y;
}
The graphic will appears continuous.
I'm making a graphing library in Android and am aware that if I update the canvas by calling invalidate it can cause a heap memory problem in some devices especially when the user is using a Relative Layout and has some views loading or has multiple graphs stacked on each other. So what I want to do is change the graph of the view without android having to redraw all other views on the Relative Layout.
The only solution I have come across is adding a boolean system to the custom view's on draw to check if the same view requested the on draw, otherwise it would keep the same canvas. However, this will only prevent the same view from being redrawn again and will also draw the other views when it is drawn.
Here is the solution mentioned above.
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(cropx != 0){
croppedBmp = Bitmap.createBitmap(drawGraph2, 0, 0, (drawGraph2.getWidth()*cropx/100), drawGraph2.getHeight());
canvas.drawBitmap(croppedBmp, 0, 0, new Paint());
croppedBmp.recycle();
System.gc();
Runtime.getRuntime().gc();
}
status = false;
}
public void plotPoints(ArrayList<Double> yvalue, ArrayList<Double> xpoint){
drawpath2 = new Path();
drawCanvas = new Canvas(drawGraph2);
drawCanvas.scale(1, -1);
boolean first = false;
for(int i = 0; i< xpoint.size(); i++){
if(!Double.isNaN(yvalue.get(i))){
if(first == false){
drawpath2.moveTo(xpoint.get(i).floatValue(),yvalue.get(i).floatValue());
first = true;
}else if(i==xpoint.size()-1){
drawCanvas.drawPath(drawpath2, paint);
drawpath2.reset();
}else{
drawpath2.lineTo(xpoint.get(i).floatValue(), yvalue.get(i).floatValue());
}
}
}
Canvas singleUseCanvas = new Canvas(drawGraph2);
singleUseCanvas.drawPath(drawpath, paint);
status = true;
invalidate();
}
Firstly I am adding my cards to a list like that:
private void initCards() {
for (int i = 0; i < 20; i++) {
Bitmap tempBitmap = BitmapFactory.decodeResource(myContext.getResources(),R.drawable.ic_launcher);
scaledCardW = (int) (screenW/10);
scaledCardH = (int) (scaledCardW*1.28);
Bitmap scaledBitmap = Bitmap.createScaledBitmap(tempBitmap,scaledCardW, scaledCardH, false);
deck.add(scaledBitmap);
}
But when it came to on draw method,my program is transforming a thing like an animation.
My onDraw method is here:
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
int left=newInteger.nextInt(screenW);
int right=newInteger.nextInt(screenH);
for(int i=0 ; i<k ;i++){
canvas.drawBitmap(deck.get(i), left, right, null);
}
}
These bitmaps must have random location and if I change the k from our main Activity different numbers of bitmaps must be drawen.Because of that,ı cannot remove the invalite() function.
Can anybody help me?Unfortunately,another topics about this problem are not providing my request.
Your bitmaps keep moving because you are randomizing their position every time you draw. Try moving these lines:
left = newInteger.nextInt(screenW);
right = newInteger.nextInt(screenH);
into the same function that changes the value of k.