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.
Related
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.
As is, 100 pink circles (same bitmap) appear scattered randomly over the phone screen (as is supposed to). When I tap one of the circles, that circle should disappear (change to the background color). I think I have a fundamental misunderstanding of Android and View in general.I think I have a couple obvious errors (that are not so obvious to me, but I've been staring at it so long that I figured I needed some help). Currently, the screen shows the random circles but nothing more. Touching the screen does nothing. Any better ideas to make the circles disappear? It recently reorganized all the bitmaps when you touched it, but I did something recently, and it stopped. The bitmap is 30px by 30px.
public class DrawV extends View {
private Bitmap bit_dot;
private int width;
private int height;
public int[] width_array = new int[100];
public int[] height_array = new int[100];
private View dotV = (View)findViewById(R.id.bigdocpic);//bitmap
Random rand = new Random();
public DrawV(Context context) {
super(context);
bit_dot = BitmapFactory.decodeResource(getResources(), R.drawable.dot_catch);
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
width = metrics.widthPixels;
height = metrics.heightPixels;
}
#Override
//draws 100 randomly placed similar bitmaps
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int height_dimension;
int width_dimension;
for (int i = 0; i < 100; i++){
height_dimension = rand.nextInt(height) + 1;
width_dimension = rand.nextInt(width) + 1;
canvas.drawBitmap(bit_dot, width_dimension, height_dimension, null);
width_array[i] = width_dimension;//
height_array[i] = height_dimension;//
}
}
#Override
public boolean onTouchEvent(MotionEvent event){
Paint p = new Paint();
p.setColor(Color.WHITE);
Path path = new Path();
Canvas c = new Canvas();
for (int i = 0; i < 100; i++){
if ((event.getX() == width_array[i]) && (event.getY() == height_array[i]))
c.drawCircle(width_array[i], height_array[i], 15, p);
}
invalidate();
return false;//false or true?
}
//set visibility of bitmap to invisible
public boolean onTouch(View v, MotionEvent event) {
dotV.setVisibility(View.INVISIBLE);
invalidate();
return false;//false or true? not understanding
}}
Help?
Your onTouchEvent isn't really doing anything important as-is, and you don't have the concept of a circle object.
onDraw should really be drawing these circles from an array/list created earlier - say a List<MyCircles> or MyCircles[]. On touch, you could iterate through all of your circles until you find one that is closest, remove that circle from the array or list, then invalidate.
The reason nothing is happening at all is even though you're drawing those circles again in onTouchEvent, you're redrawing everything yet again in onDraw (invalidate() calls draw/onDraw).
Ideally, create your list of circles in your initializer, draw them in onDraw, and update them in onTouch (That is, delete). There may be a simpler way to do this but this is, at the very least, a more proper approach.
I am new to android programming and have run into a small problem. If you could help, that would be greatly appreciated.
I am making rectangles for a live wallpaper I am making using:
void drawFrame() {
final SurfaceHolder holder = getSurfaceHolder();
Canvas c = null;
try {
c = holder.lockCanvas();
if (c != null) {
Paint paint = new Paint();
paint.setColor(Color.CYAN);
Paint ypaint = new Paint();
ypaint.setColor(Color.RED);
Paint ppaint = new Paint();
ppaint.setColor(Color.GREEN);
drawVerStripes(c, ppaint, 0,20);
drawVerStripes(c, paint, 50,20);
drawVerStripes(c, ypaint,0,10);
}
} finally {
if (c != null)
holder.unlockCanvasAndPost(c);
}
The function:
Params:
width: width of the rectangle
space: the difference between last rect and this rect
int y=0;
int oldY=0;
private void drawVerStripes(Canvas c, Paint paint, int space, int width) {
y=oldY+space;
c.drawRect(y, 0, y+width,c.getHeight(), paint);
oldY=y;
}
The result is the rectangles moving to one side of screen really fast. I want them to stay on screen and not move.
In other words, is there a way that drawVerStripes is executed only once and not every frame.
Your drawFrame method is called with each frame. If your devices refresh rate is 70, then your method is called 70 times a second. If you increase x or y coordinate by 1, then you move 70 pixels in either x or y coordinates. You should do refresh rate independent movement.
1) Calculate delta time which is the time that passed since the last screen update.
2) Determine how many pixels you want your rectangle to move every second.
Let's say you want you rectangle to move 50 pixels in x.
Then do the following. x += deltaTime * 50.
long startTime = System.nanoTime(); // Execute before the first time your drawFrame executes
float deltaTime = (System.nanoTime()-startTime) / 1000000000.0f; // In drawFrame method calculate
long startTime = System.nanoTime(); // Execute after calculating delta time.
I believe that you need to set y and oldY to 0 again at the beginning of drawFrame.
I have a problem, I have tried resolve the problem but I haven't found a solution.
I have two columns of images. I want to join them through the midpoint of each image. The problem I have is that the attachment point moves down, like the image
I have a "main" class and I have the internal class: public class DrawView extends LinearLayout
with the atribute:
private Paint paint = new Paint();
and I set the next values:
paint.setColor(Color.BLACK);
paint.setStrokeWidth(6);
I use the next code for draw the lines:
public void onDraw(Canvas canvas) {
}
#SuppressLint("UseValueOf")
#Override
public void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (activateDraw) {
for (int i = 0; i < 5; i++) {
//I not include the color selection.
x1= Image[i].x + Image[i].width;
y1=Image[i].y+ (new Double(Image[i].height / 2).intValue()));
x2=ImagePr[i].x;
y2=ImagePr[i].y + (new Double((ImagePr[i].height) / 2).intValue()));
canvas.drawLine(x1, y1, x2, y2, paint);
}
activateDraw = false;
}
}
To set the x and y values I use the method:
public void setData(ImageView img) {
image = img;
int[] values = new int[2];
image.getLocationInWindow(values);
x = values[0];
y = values[1];
width = image.getWidth();
height = image.getHeight();
}
In the main class I have the atribute:
Canvas auxCanvas = new Canvas();
and I execute the onDraw(auxCanvas) method when I want draw the lines. Why the lines don't draw joining the "midpoints"?
Anyone can help me?Thanks!!
#Shaunak Sorry, it was a fail. I've removed it and it doesn't affect, the problem continues. Thank you!
#anthropomo I tried your change but the problem continues.
I don't understand why in the emulator seems to work fine, but not on the device.
SOLUTION:
(I thought I had written the answer, sorry)
The solution was very simple. The app is destinated to students that have 6-8 years, so I decided to hide the status bar and the above code works perfect without do changes!
Hide the status bar:
Hide Notification bar
How to hide the title bar for an Activity in XML with existing custom theme
If other people want to show the status bar, I suppose you need to subtract the status bar height.
reference: http://developer.android.com/reference/android/util/DisplayMetrics.html#density
does something like this work for you?:
float d = getResources().getDisplayMetrics().density;
canvas.drawLine(x1*d, y1*d, x2*d, y2*d, paint);
note: if the multiplication doesn't work try dividing by d... i can never remember what to do.
I want to make a small app. You will touch the screen and draw something and it will list points you pass and draw small green 3x3 rectangles for each fifth point. I use onTouchEvent for listing points using TextView and send it to setContentView. However, I have problem in drawing. I checked examples for drawing (onDraw) but I am not able to get it working for both printing point plus drawing green dots. Any help would be great, thanks.
Here you are, a quick sample of drawing on SurfaceView.
public class FunPanel extends SurfaceView {
class Point {
int X;
int Y;
public Point() {
X = Y = -1;
}
}
private ArrayList<Point> mPoints = new ArrayList<Point>();
private Point mCurPoint = new Point();
private Bitmap mBitmap = ....// your desired image
#Override
public void doDraw(Canvas canvas) {
if( !(mPoints.size() % 5) ) {
canvas.drawBitmap(mBitmap, mCurPoint.X, mCurPoint.Y, null);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
mCurPoint.X = (int) event.getX() - mBitmap.getWidth() / 2;
mCurPoint.Y = (int) event.getY() - mBitmap.getHeight() / 2;
mPoints.add(mCurPoint);
return super.onTouchEvent(event);
}
}
It's not entirely clear what you're trying to do, but have a look at this It should get you started in the right direction. Basically extend a View and override the onDraw(Canvas) to draw the Rectangles and override the onTouchEvent(MotionEvent) to grab the touch points from the screen.