140 likes | 312 Views
Graphics with Canvas,. Approaches for Graphics. Load image from /res/ drawable Best for static images OpenGL ES 3D graphics (i.e., transforms such as spin can be applied to graphical objects ) Best for real animation Draw on Canvas or SurfaceView Canvas for drawing within the main thread
E N D
Approaches for Graphics • Load image from /res/drawable • Best for static images • OpenGL ES • 3D graphics (i.e., transforms such as spin can be applied to graphical objects) • Best for real animation • Draw on Canvas or SurfaceView • Canvas for drawing within the main thread • SurfaceView is faster, and better for detailed graphics • Note, if your main thread take too long, the OS will kill it, and it will be difficult to debug.
Drawable shapes • Make new app, edu.udel.eleg454.Graphics1 • In onCreate is setContentView(R.layout.main) • Instead of the view generated by R.layout.main, we use our own, which extends View • In Graphics1 class, add • private class MyView extends View { public MyView(Context context) { super(context); } @Override protected void onDraw(Canvas canvas) { ShapeDrawablemDrawable = new ShapeDrawable(new OvalShape()); mDrawable.getPaint().setColor(0xff74AC23); mDrawable.setBounds(10, 10, 310, 60); mDrawable.draw(canvas); } } • Then, in onCreate, replace setContentView(R.layout.main); with setContentView(new MyView(this)); • Run • Besides BitMaps, OvalShape, ArcShape, PathShape, RoundRectShape, and Shape
View Widget • The previous method required us to replace setContentView(R.layout.main); • This resulted in the entire view being controlled by our view object • E.g., we could not have a button in the view where we place the button with the layout editor • To fix this, we add a view widget • Move MyView to separate class • Make new class • In package explorer, under /src • Find edu.udel.eleg454.TestGraphics1 • Right click on edu.udel.eleg454.TestGraphics1 • Select: new->class • Dialog opens • Name: MyView • Superclass: View (then select browser to get full name: android.View) • OK • Move functions from private class MyView to this new MyView • Move public MyView( • MoveonDraw • Also, in MyView add public MyView(Context context, AttributeSetattrs) { super(context, attrs); }
View Widget • Go to main.xml graphical layout editor • Drag button to the screen • Leave id as button1 • Go to main.xml (not the editor) • Find second <Button …> </Button> • Before<Button>, add • < edu.udel.eleg454.TestGraphics1.MyView android:layout_width="wrap_content" android:layout_height=“116dpt" android:id="@+id/View01"></ edu.udel.eleg454.TestGraphics1.MyView> • Note that edu.udel.eleg454.TestGraphics1.MyView is the name of the separate class. If another name is used, then this name should be changed • Save and go back to graphical view. There should be a box labeled MyView. Drag the box to make it larger • Run
Canvas Drawing • Canvas has many drawing functions, e.g., drawPath(Path path, Paint paint) • In onDraw, add the following • Path: sequence of graphical objects • Path path = new Path(); • Make line between two points • path.moveTo(10,10); // starting place • path.lineTo(160,160); • add circle somewhere • path.addCircle(160,160,20, Path.Direction.CCW); • Paint – for setting color and line width • Paint paint = new Paint(); • paint.setDither(true); • paint.setColor(Color.RED); • paint.setStyle(Paint.Style.FILL_AND_STROKE); • paint.setStrokeJoin(Paint.Join.ROUND); • paint.setStrokeCap(Paint.Cap.ROUND); • paint.setStrokeWidth(10); • Draw view canvas.drawPath(path, paint); • run
Change graphics • At the end of TestGraphics1Activity.onCreate • MyViewmyView = (MyView) findViewById(R.id.View01); • Button button = (Button)findViewById(R.id.button1); • button.setOnClickListener(new View.OnClickListener() {}); • Let eclipse add unimplemented methods • In onClick, add • myView.redraw(); • In MyView • Add class variable • int radius = 20; • In onDraw, change • path.addCircle(160,160, 20, Path.Direction.CCW); • To • path.addCircle(160,160,radius, Path.Direction.CCW); • Add function • public void redraw() { • radius = 80; • invalidate(); // this is needed to force redraw • } • run
notes • Use invalidate() to force redraw • In TestGraphics, add variable • MyViewmyView; • In TestGraphics.onCreate(), add • myView = (MyView) findViewById(R.id.View01); • Then myView.invalidate(); will force redraw • Is canvas documentation for more graphics • E.g., drawBitmap has several functions • Avoid declaring and setting variables in onDraw, instead, setting them elsewhere and access them from draw • Use invalidate() to force redraw • Use SurfaceView for faster screen drawing
SurfaceView • Faster • You can draw on a SurfaceView from other threads, not just to UI thread • When drawing with the UI thread, if the drawing takes a long time, then everything else must wait for the drawing to complete, • e.g., the user cannot press any buttons • If you put a long activity in the UI thread, a message will pop up saying that the app has stopped responding • If you put a long drawing activity when starting the app, the system just kill it (thinking that it did not start correctly) • But, SurfaceViews are not transparent, nothing behind the view can be seen • Differences • In Canvas approach, your draw function is called and has argument canvas. You can draw on this canvas. You can force a redraw can calling invalidate. • Invalidate will result in onDraw being called from the UI thread • With surfaceview, you get a canvas and can draw on it whenever you want. Usually you draw on it from a new thread • E.g., You start the thread from the UI thread
SurfaceViewFun • Make a new app, SurfaceViewFun and package name edu.udel.eleg454.SurfaveViewFun • Make new class • Right click on edu.udel.eleg454.SurafceViewFun • Select New -> class • Name: MySurfaceView • SuperClass: SurfaceView • Go to res/layout/main.xml • Open graphical view • Click on “Advanced” • Drag SurfaceView • In xml view, find the <SurfaceView …. • Change <SurfaceView … to <edu.udel.eleg454.SurfaceViewFun.MyView. • Go back to graphical view and check that the surfaceView is now labeled MySurfaceView. If not, then something is wrong.
MySurfaceView • Open MySurfaceView • Eclipse will ask to implement some function. Add all three of these • public MySurfaceView(Context context, AttributeSetattrs, intdefStyle) • public MySurfaceView(Context context, AttributeSetattrs) { • public MySurfaceView(Context context) { • Each on has content • super(context, attrs); // eclipse migth add this part • ini(context); // a function to make
Change • public class MySurfaceView extends SurfaceView • To • public class MySurfaceView extends SurfaceViewimplements SurfaceHolder.Callback • Add unimplements functions
Thread class • Make new subclass in MySurfaceView and extend Thread, i..e, add • Class MyThread extends Thread {}; • This will be the thread we use for drawing • We will draw an oval, by the drawing is slightly animated • In MyThread, add member variables • SurfaceHoldersurfaceHolder = null; // this a key variable as it allow use to get a canvas • boolean done; • long lastTime, startTime; • Canvas canvas; • RectFrectForOval = null; • float duration = 5*1000; • Paint drawingPaint; • float arcSweep;
MyThread member functions • Constructor • public MyThread(SurfaceHolder _surfaceHolder) { • surfaceHolder = _surfaceHolder; • drawingPaint = new Paint(); • drawingPaint.setColor(Color.BLUE); • } • We will draw an oval, but it will • This is the function that will run in the thread. • @Override • public void run() { • Log.e("surface","running thread"); • setOval(); • lastTime = System.currentTimeMillis(); • startTime = System.currentTimeMillis(); • done=false; • while (!done) { • updateArcSweep(); • drawCurrentArc(); • } • Log.e("SurfaceViewFun","MyThread.run has finished"); • }