This is a continuation of my previous article on my attempts with a simple Android game.
After some more reading of the Android developer guide and some googl'ing, I finally managed to draw a ball on a background and make it move around. :)
To begin with I decided to only display a background with a ball on it and get this ball to bounce around within the boundaries of the screen.
So I first had to create a custom view by extending the View class and defining my own onDraw() method. As explained in the Developer guide, the Android framework would call my onDraw() method to request this View to be drawn and this is where I should perform all drawing to my Canvas.
I created a View named 'MyView' (not a very original name :)) and defined the default constructor in it. Within this constructor I created the Ball and Background image bitmaps from the resources (which I added under res/drawable-hdpi) using BitmapFactory.decodeResource() and initialized a few other variables initial placement of the ball and direction of bounce.
I also found that apart from the Constructor and onDraw() I had to override onMeasure() and invoke setMeasuredDimension() with the correct width and height for my view to correctly determine its boundaries. I also initialized a few more variables within this to help identify the maximum X and Y boundaries for the ball, initial placement and direction of bounce.
Within the onDraw() I just display the background, ball images and update the x and y co-ordinates for the next location the ball needs to be drawn at. Also quickly determine if a collision is going to occur with the boundaries and reverse directions accordingly.
I think the actual code would help explain this a lot better,
public class MyView extends View { private Context mContext; private Bitmap mBack; private Bitmap mBall; private int ballRadius; private int ballXMax, ballYMax; private int x,y; private int xInc, yInc; // c-tor for in code construction public MyView(Context context) { super(context); mContext = context; mBack = BitmapFactory.decodeResource(getResources(), R.drawable.wood); mBall = BitmapFactory.decodeResource(getResources(), R.drawable.ball2); ballRadius = mBall.getWidth(); } @Override protected void onMeasure(int width, int height){ int measuredWidth = MeasureSpec.getSize(width); int measuredHeight = MeasureSpec.getSize(height); xInc = 1; yInc = 1; ballXMax = measuredWidth - ballRadius; ballYMax = measuredHeight - ballRadius; x = ballXMax/2; y = ballYMax/2; } @Override protected void onDraw(Canvas canvas) { canvas.drawBitmap(mBack, 0, 0, null); x += xInc; y += yInc; canvas.drawBitmap(mBall, x, y, null); // boundary collision detection and reversal if (x > ballXMax) xInc = -1; if (x < 0) xInc = 1; if (y > ballYMax) yInc = -1; if (y < 0) yInc = 1; //and redraw now invalidate(); } }Further optimization:
I did however notice that with this code the simulation of the ball moving seemed "jumpy" on the emulator. i .e. it did not seem to be moving across the screen at a uniform speed. At some points it seemed to move slowly and then suddenly a little faster.
Also the onDraw() method where the static background image is redrawn every iteration seemed like something that could be avoided. With some searching I found that it is possible to create a Theme (theme.xml within the res/values directory) with the desired background image and use this in the AndroidManifest.xml for the Activity. This avoids having to redraw the background each time in our onDraw() method.
theme.xml <?xml version="1.0" encoding="UTF-8"?> <resources> <style name="Theme.StaticBackground" parent="android:Theme.NoTitleBar.Fullscreen"> <item name="android:windowBackground">@drawable/wood</item> </style> </resources>With this change the animation appeared much smoother on the emulator than before (I'm not aware of how this could be measured as yet though).
View construction from XML:
Also as you may have noticed, I only have one constructor in my above code. This constructor only allows in-code creation on my View. If you want to create the View from XML then you also need to add a constructor with the additional AttributeSet argument (as shown below).
// c-tor for construction from XML Layout public MyView(Context context, AttributeSet attr) { super(context, attr); mContext = context; mBall = BitmapFactory.decodeResource(getResources(), R.drawable.ball2); ballRadius = mBall.getWidth(); }In this case you can have something like this in your layout XML (res/layout/main.xml) to display the custom view,
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" > <com.meyn.android.mygame.MyView android:layout_width="fill_parent" android:layout_height="fill_parent" /> </FrameLayout>BTW, this is the onCreate() from the activity,
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // No Title bar requestWindowFeature(Window.FEATURE_NO_TITLE); //MyView mMyView = new MyView(this); //setContentView(mMyView); setContentView(R.layout.main); }