Android OS Development v.letslearntogether!
I’ve been working almost daily with android OS in my free time and have been learning a lot. I have a rudimentary game already complete (lacking polish but it functions with the bare requirements for it to be called a "game").
I was wondering if anyone here is also new to Android development or would like to start and we can use this thread to help each other out and post regularly about what we learn and do each day… If there’s no interest that’s cool too, I might make this a tutorial instead.
I’ll start the ball rolling with a quick "getting started"… it’s really easier than you think!
First, it helps to have an Android device to debug on, but it’s not entirely necessary. The Android SDK includes hardware simulators so you can debug your app in a virtual instrument in a window on your PC. I almost always use my Evo though because the simulator is kind of slow and as far as I can tell cannot simulate tilt controls. Since I have no idea why you would get into android development without owning an android device I’ll assume you have one…
The first thing you need to do is to download and install the latest version of the Android SDK from:
There are a few options for the IDE but by far the most common is Eclipse. Most tutorials you will find assume you are using Eclipse and Google has developed some really nice plugins to assist with app development under Eclipse, so go ahead and download and install the latest version of Eclipse if you don’t already have it as well as the ADT (Android development tools) plugin.
After installing Eclipse and running it go to Help>Install New Software, click "Add" in the top right corner. In the window that is displayed type "ADT Plugin" for the name and "https://dl-ssl.google.com/android/eclipse/" for the URL. In the next screen check the "developer tools" item and continue to click next until complete. Finally you need to shut down and restart Eclipse.
Now with the Android SDK, Eclipse, and the ADT Plugin installed you need to setup the plugin to point to the SDK directory. In Eclipse select Window>Preferences, select Android on the left panel, for the "SDK Location" click browse and navigate to your Android SDK install folder.
The next step is to setup a virtual device. Even if you plan on using hardware to debug it is still a good idea to have one or more virtual devices available. In Eclipse, select Window>Android SDK and AVD Manager. Click new to create a new AVD (Android virtual device). Set it up the way you want with a name, a platform target, a SD card size (uses HDD space), etc… then click create device.
Now it’s time to setup your hardware device for debugging. Click the home button to go to the home screen, then click menu and select Applications>Development>Enable USB Debugging. Next you will need to install a driver so that your computer can communicate with your device, it is likely that you have already done this in order to sync your device or access it’s SD card on your computer, but if you have not there is a list of device manufacturer websites where you can find these drivers here:
Once that is complete you can verify that your device is recognized by executing adb.exe from your SDK platform tools directory with the parameter "devices".
That’s it, now you are ready to create your first android project…
If you want to develop games for Android, there are several free options for gaming libraries, but they’re a bit rough at the edges right now.
AndEngine (what I’ve been using)
- 2D Only
- No proper documentation, but lots of examples and tutorials, and someone generated javadocs and posted them
- Forums with a lot of posts but activity isn’t very high
libgdx
- 2D and 3D I believe
- Larger community than AndEngine
- Feature set seemed volatile and incomplete
- Lets you build for desktop and device with same codebase so you don’t have to test with an emulator or device all the time
There are others, but they’re significantly less polished and active.
The good thing is, there are quite a few open source examples of games in the wild that you can browse.
Hey thanks for the links. It doesn’t seem like there is much interest here for my original proposal, so I’ll probably abandon this thread, but I appreciate the response.
As far as libraries go, I’ll probably avoid them until there is a compelling reason to do otherwise. I’ve done everything from scratch using nothing but the SDK documentation and it’s worked out quite well so far. I can do 2D graphics with animations (frame and "tween"), I can use the touchscreen and accelerometer and already have used both to control the ship in my first little game, and I can already do sound effects and music and whatnot as well. I even have multi-threading working to separate the main game loop from the input handling.
What I really need is a graphic artist, because I can’t create graphics worth a damn myself.
|
As far as libraries go, I’ll probably avoid them until there is a compelling reason to do otherwise. I’ve done everything from scratch using nothing but the SDK documentation and it’s worked out quite well so far. I can do 2D graphics with animations (frame and "tween"), I can use the touchscreen and accelerometer and already have used both to control the ship in my first little game, and I can already do sound effects and music and whatnot as well. I even have multi-threading working to separate the main game loop from the input handling.
What I really need is a graphic artist, because I can’t create graphics worth a damn myself. |
Programmer art for the lose, right? I know exactly what you mean. I’m teaching myself Illustrator right now, watching a bunch of video tuts and stuff.
Sounds like you’ve done pretty well on your own without a library. You’ll probably get better performance with your own well-written code than a generic library anyway.
Oh cool, I’ll write up some more tomorrow now that some people seem interested!
I am planning on picking up an Android device in 6 months or so just for this use. Any advice on which phone to get and use from Verizon? Also, do you ever run the risk of bricking the phone when using it to test new applications?
I have no input on any devices other than Evo 4g that I own, along with the droid X it seems to be among the best android phones, but this year will see many newer phones and some have already been released. You should talk to dumpy (aka LOLILIKECOCKS), he knows a lot about the different cell phones and is an android fanboy.
No you can’t brick the phone by playing around with app development. There is nothing special you have to do to use it for debugging beyond enabling the "USB debugging" option in the settings menu. Each application runs in it’s virtual machine in order to segregate it from the rest of the system so that a problem in any given app cannot effect other apps or the operating system.
I’ve used the Droid X and Droid Incredible.. they’re both pretty good. If you’re getting one JUST to develop and test, get the Droid X.
Okay here we go, I hope you’re ready we’re going to fly!
First I am skipping all of the normal android view context junk… we are going to write some simple code to get us a black screen where we can quickly draw bitmaps or graphic primitives (or pixels if you want). If you want to know how to use pre-built buttons or other UI widgets this is not the tutorial for you.
As you go you may get errors when referencing classes that are part of the SDK, this is because you have not imported them into the file. The Android dev tools have a handy way to automatically correct this, just use ctrl-shift-o.
I’ve included links to the SDK documentation for various things I mention, look for them.
Make a new Android project, got to File>New>Android Project. In the dialog shown enter a project name, call it "one" or whatever you want, set the build target to Android 2.2 (that’s what I use, use less at your own risk), set the application name to whatever you want, the package name to whatever (tutorial.one perhaps…), and min SDK version = 8 if you used 2.2 above… otherwise your on your own.
Okay, in the package explorer pane at the left of the screen navigate to [project name]>src>main.java. You’ll see some shit has been entered for you already, you can leave it for now.
The first thing we are going to do is make a new class that will represent our UI thread. So go to file>new>class and enter a name for the UIThread class (I recommend UIThreadClass).
Open the new file under the src directory, it will have have the class definition already in it for you, but you want to add "extends " to it, like this:
public class UIThreadClass extends Thread
{
}
You also want to add a private global boolean variable called running, or run, or ThreadRunning, or whatever you want along those lines. This will be used to stop the thread as is deprecated.
Now add a function called StopThread which sets your running variable to false (you could just as easily make that variable public and use it directly, I don’t really care)
public void StopRunning()
{
running = false;
}
Give the class an empty constructor, we’ll come back and fill it in later:
public UIThreadClass()
{
}
Now you must override Thread.run(), which will essentially be the main loop of your UI thread, like this:
@Override public void run()
{
while(running)
{
}
}
Okay that’s it so far for this class, we’ll come back to it later after implementing the next class, which the rest relies on. Right now the file should look something like this:
package one.main;
public class UIThreadClass extends Thread
{
public boolean running = false;
public UIThreadClass()
{
}
public void StopThread()
{
running = false;
}
@Override public void run()
{
while(running)
{
}
}
}
A surface view is the best kind of Android "view" because it gives us direct access to the "canvas" (screen memory buffer) for drawing.
So, we are going to override , so go to file>new>class and name it something like SurfaceViewClass or MySurfaceViewClass…
Change it’s definition to include "extends implements ", like so:
public class SurfaceViewClass extends SurfaceView implements SurfaceHolder.Callback
{
}
We are going to add a global instance of our UIThreadClass to this class, like so:
public UIThreadClass MyUIThread;
We are also going to add a default constructor. This constructor will accept an object, which I’ll explain later. In this constructor we will call the super classes constructor and pass along this context object, we will add a callback to the , and we will instantiate the UIThread object:
public SurfaceViewClass(Context MyContext)
{
super(MyContext);
this.getHolder().addCallback(this);
MyUIThread = new UIThreadClass(this);
}
First we call the constructor of the super class because it sets up some behind the scenes stuff that google doesn’t give us details about. The "this" refers to this class, which is derived from … so we are actually calling on line 2… which returns the surface holder object, which we use to call the method () passing in a reference to this object… so basically we setup this object to receive broadcast messages (callbacks) from the surface holder, which I can only assume is setup in the super classes constructor. Practically, this allows us to listen for SurfaceCreated, SurfaceDestroyed, and SurfaceChanged events, all of which must be overridden with handlers.
So we are going override the , , , and event handlers, like so:
@Override public void onDraw(Canvas canvas)
{
}
@Override public void surfaceCreated(SurfaceHolder holder)
{
}
@Override public void surfaceDestroyed(SurfaceHolder holder)
{
}
@Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
}
The event handle is of particular interest as it’s where we will draw everything… initially.
That’s it for this class so far, your file should look similar to this:
package one.main;
import android.content.Context;
import android.graphics.Canvas;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class SurfaceViewClass extends SurfaceView implements SurfaceHolder.Callback
{
public UIThreadClass MyUIThread;
public SurfaceViewClass(Context MyContext)
{
super(MyContext);
this.getHolder().addCallback(this);
MyUIThread = new UIThreadClass(this);
}
@Override public void onDraw(Canvas canvas)
{
}
@Override public void surfaceCreated(SurfaceHolder holder)
{
}
@Override public void surfaceDestroyed(SurfaceHolder holder)
{
}
@Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
}
}
cont’d…
bookmarked. I got a xoom i am going to be making apps for.
Now that we have the SurfaceView class complete we can go back and fill in the blanks in the UIThread class. First we are going to add some new global variables, an android.view.surfaceHolder and a SurfaceViewClass (the one we just made).
public class UIThreadClass extends Thread
{
public SurfaceHolder MySurfaceHolder;
public SurfaceViewClass MySurfaceView;
public boolean running = false;
Next we are going to change the constructor to accept an object of SurfaceViewClass and we are going to assign that to MySurfaceView. We are also going to call the method and assign the returned to MySurfaceHolder:
public UIThreadClass(SurfaceViewClass view)
{
MySurfaceView = view;
MySurfaceHolder = MySurfaceView.getHolder();
}
And that’s all for now… moving on…
Main (finally)
So we finally come back to the main class, the one created for you right after you made this new project.
In the overridden onCreate() method that was automatically in the file you can go ahead and keep the call to super(), but delete the call to setContentView.
Add a global variable to this class of the type SurfaceViewClass (our overridden SurfaceView class) and call it something like MySurfaceViewClass. Initialize it in the constructor, passing "this" in for the context, right after the call to super(). Then we are going to pass it to :
public class main extends Activity
{
public SurfaceViewClass MySurfaceView;
@Override public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
MySurfaceView = new SurfaceViewClass(this);
setContentView(MySurfaceView);
}
}
Now we need to implement three very important event handlers from the Activity class (which we are overriding here): , , and .
@Override protected void onPause()
{
super.onPause();
}
protected void onResume()
{
super.onResume();
}
protected void onDestroy()
{
super.onDestroy();
MySurfaceView.MyUIThread.StopThread();
}
We call the super classes functions for each of these, but notice in onDestroy we also make sure we stop the UI thread.
So now the file should look something like this:
package one.main;
import one.main.SurfaceViewClass;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.PowerManager;
public class main extends Activity
{
//Instance of my SurfaceView class (overridden android.view.surfaceView), will be main content view
public SurfaceViewClass MySurfaceView;
//Called automatically on application startup, consider it the entry point of the application (overridden from Activity.onCreate())
@Override public void onCreate(Bundle savedInstanceState)
{
//Call super class method (Activity.onCreate()) first, let it do it's thing
super.onCreate(savedInstanceState);
//Create an instance of my overridden surface view class (in SurfaceViewClass.java)
MySurfaceView = new SurfaceViewClass(this);
//Set the content view to the instance of my surface view class
setContentView(MySurfaceView);
}
//Called when the application loses focus (phone call, home screen or back button pressed, etc)
@Override protected void onPause()
{
//Call super classes onPause() method first
super.onPause();
}
//Called when the application regains focus after losing it
protected void onResume()
{
//Call super classes onResume() method first
super.onResume();
}
protected void onDestroy()
{
//Call super classes onDestroy() method first
super.onDestroy();
//Stop the UI thread
MySurfaceView.MyUIThread.StopThread();
}
}
Well, I know this doesn’t look like much, but we now have a rudimentary skeleton to create a game with. You should be able to run this on your device and get a black screen. The next update I promise we will be drawing shit all over the screen and exploring some input methods as well.
Just a quick update for today, I am going to show you how to obtain a "wait lock". A wait lock allows you to politely ask the system to not dim the screen or turn the display or keypad off in order to save batteries as it normally would. This is important because you don’t want these things happening in the middle of your game.
In our main class "main.java" we are going to add a global variable of the type android.os.PowerManager, call it MyPowerManager. We are also going to add a variable of the type android.os.PowerManager.WakeLock, call that MyWakeLock.
Now, in the onCreate() method of the main class we are going to initialize the power manager by calling android.content.Context.getSystemService() and we are going to pass android.content.Context.POWER_SERVICE to it in order to request a handle to the power service. We need to typecast the result of this function to a PowerManager object and assign it to the variable you made above (MyPowerManager). We also have to initialize MyWakeLock and we do this by calling a method of the power manager called newWakeLock(). To this function we pass PowerManager.FULL_WAKE_LOCK for the first parameter and tag string that can be anything you want (I used "MyWakeLock") for the second parameter. That’s all that we have to do to the onCreate() method but the wake lock won’t work yet, we still have to enable and disable it properly. The onCreate method should look like this now:
@Override public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
MyPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
MyWakeLock = MyPowerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "MyWakeLock");
MySurfaceView = new SurfaceViewClass(MyContext);
setContentView(MySurfaceView);
}
Now we are going to modify the onPause(), onResume(), and onDestroy() event handler to properly release and reaquire the wait lock, respectively.
To release the lock call MyWaitLock.release(), to reacquire it after releasing it call MyWaitLock.acquire(). In pause and destroy we want to release it, in resume we want to acquire it, so those event handlers should look like this:
@Override protected void onPause()
{
super.onPause();
if(MyWakeLock.isHeld()) MyWakeLock.release();
}
protected void onResume()
{
super.onResume();
MyWakeLock.acquire();
}
protected void onDestroy()
{
MySurfaceView.MyUIThread.StopThread();
if(MyWakeLock.isHeld()) MyWakeLock.release();
super.onDestroy();
}
Okay so now when the application loses focus (if you get a phonecall or whatever), the wake lock will be released, as soon as your application regains focus it will be restored. This is important because you don’t want your application imposing restrictions on the phone when it is not the active application. If you don’t release the wake lock in the onDestroy method your app will always report an error when it is closed.
We’re still not done though, we need to explicitly request permission to do this from the OS, and we do this by adding a permission to the manifest file. So open AndroidManifest.xml and click on the permissions tab. Click the "Add" button and select "uses permission" from the list. Scroll down until you find android.permission.WAKE_LOCK and select it, or just type that in manually. Now save your project and you’re good to go, the phone will not try to do any power saving things while your game is running now.
So here are all 3 source files, from my project with comments and minor differences, up to this point:
main.java:
package one.main;
import one.main.SurfaceViewClass;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.PowerManager;
public class main extends Activity
{
//Instance of PowerManager class, used to request a wake lock
public PowerManager MyPowerManager;
//Instance of wake lock class, used to prevent phone from dimming or turning off display while game is running
public PowerManager.WakeLock MyWakeLock;
//Global reference to main application context
public Context MyContext;
//Instance of my SurfaceView class (overridden android.view.surfaceView), will be main content view
public SurfaceViewClass MySurfaceView;
//Called automatically on application startup, consider it the entry point of the application (overridden from Activity.onCreate())
@Override public void onCreate(Bundle savedInstanceState)
{
//Call super class method (Activity.onCreate()) first, let it do it's thing
super.onCreate(savedInstanceState);
//Set the global context variable
MyContext = this;
//Get a wake lock object to be used to prevent phone from dimming or turning off display while game is running
MyPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
MyWakeLock = MyPowerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "DoNotDimScreen");
//Create an instance of my overridden surface view class (in SurfaceViewClass.java)
MySurfaceView = new SurfaceViewClass(MyContext);
//Set the content view to the instance of my surface view class
setContentView(MySurfaceView);
}
//Called when the application loses focus (phone call, home screen or back button pressed, etc)
@Override protected void onPause()
{
//Call super classes onPause() method first
super.onPause();
//Release the wake lock (allow screen to dim)
if(MyWakeLock.isHeld()) MyWakeLock.release();
}
//Called when the application regains focus after losing it
protected void onResume()
{
//Call super classes onResume() method first
super.onResume();
//Re-acquire wake lock
MyWakeLock.acquire();
}
protected void onDestroy()
{
//Stop the UI thread
MySurfaceView.MyUIThread.SetRunning(false);
//Release the wake lock (allow screen to dim)
if(MyWakeLock.isHeld()) MyWakeLock.release();
//Call super classes onDestroy() method first
super.onDestroy();
}
}
package one.main;
import android.content.Context;
import android.graphics.Canvas;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class SurfaceViewClass extends SurfaceView implements SurfaceHolder.Callback
{
public UIThreadClass MyUIThread;
//Constructor
public SurfaceViewClass(Context MyContext)
{
//Calls SurfaceViews constructor
//http://developer.android.com/reference/android/view/SurfaceView.html#SurfaceView%28android.content.Context%29
super(MyContext);
//Registers this class as a callback for the surface (so I get the messages it broadcasts)
// gives access to SurfaceHolder.SurfaceCreated, SurfaceHolder.SurfaceDestroyed, SurfaceHolder.SurfaceChanged
//http://developer.android.com/reference/android/view/SurfaceView.html#getHolder%28%29
//http://developer.android.com/reference/android/view/SurfaceHolder.Callback.html
this.getHolder().addCallback(this);
//Initialize the UI thread with this SurfaceView
MyUIThread = new UIThreadClass(this);
}
//Override SurfaceView.onDraw() to do all drawing to the surface
@Override public void onDraw(Canvas canvas)
{
}
//Called when the surface is first created, must not draw to it before this
@Override public void surfaceCreated(SurfaceHolder holder)
{
}
//Called when the surface is destroyed, must not draw to it after this
@Override public void surfaceDestroyed(SurfaceHolder holder)
{
}
@Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
}
}
package one.main;
import android.view.SurfaceHolder;
public class UIThreadClass extends Thread
{
public SurfaceHolder MySurfaceHolder;
public SurfaceViewClass MySurfaceView;
public boolean ThreadRunning = false;
//Constructor, pass in the instance of the surface view class
public UIThreadClass(SurfaceViewClass view)
{
MySurfaceView = view;
MySurfaceHolder = MySurfaceView.getHolder();
}
//Outside access to start or stop thread execution
public void SetRunning(boolean run)
{
ThreadRunning = run;
}
@Override public void run()
{
while(ThreadRunning)
{
}
}
}
… and don’t forget the permission to implement a wake lock in AndroidManifest.xml:
<uses-permission android:name="android.permission.WAKE_LOCK"></uses-permission>
Let me know if you have any questions
In for this. I notice that there are a lot of iOS based apps that don’t have an Android equivalent. Hopefully I could take advantage of that while the going is good.
It’s going to be a bit of an initial struggle since I don’t know java (so I’ll have to learn as I go), but it’ll be fun!
|
In for this. I notice that there are a lot of iOS based apps that don’t have an Android equivalent. Hopefully I could take advantage of that while the going is good.
It’s going to be a bit of an initial struggle since I don’t know java (so I’ll have to learn as I go), but it’ll be fun! |
I have no experience with Java either outside of a couple of college courses a decade ago
Yah, can we get this thread stickied?
Working on setting up the IDE and SDK now this could take me a few days, but I am finally going to do this after seeing your motivation Codex. Once I am set up I will check out your tutorials and try to add a little of information from my trial and error.
-Still no android device, I will not have one until my current phone contract is up.
-My JAVA experience is limited to my two JAVA classes I took in college 4 years ago, so it may be a little rough at first.
Sorry guys I’ve been MIA for about a week, I’m planning on updating this tomorrow night though… I have once again switched focuses for my personal development and am working on a Mario 3 clone for Android, which was a tough call between that and a themed tower defense game… but now I have to revise some of the code examples I’ve already given you here since I’ve been learning better ways to do things!
here is my 100% homemade title screen for Super Marzio Bros.
So let’s get some images on the screen, eh?
First make a little picture in paint or photoshop or whatever, or take any image on your computer you want, and put it in the res/drawable folder. You’ll probably see three different drawable folders labelled "ldpi", "mdpi" or "hdpi", meaning low/medium/high dots per inch. This refers the the pixel density of the display on the phone, ideally if you want your apps to work with as many phones as possible you provide a copy of all your graphics in each of these three folders. Since I am developing on a high dpi device I put all of my image files into "res/drawable-hdpi", put yours in whichever is more appropriate for the device you are developing with.
Once you put an image or two into one of these directories you can right click on the res directory under your project name in eclipse (package explorer tool) and select "refresh"… the images should then be shown under that directory. If you don’t see it you might have to rename it, eclipse enforces some ridiculous naming conventions here, your resource filenames cannot have any spaces or capital letters… for some reason. rename your files and refresh that folder again to see if that fixes it.
Now, the file R.java in your "gen" folder will automatically be updated to include the assignment of a unique identifier to these images that you added to the project. Make sure you never edit the R.java file, I’ve had to completely remake an entire project after corrupting this file and not knowing how to make it work again (even if you get it back to the way it was it will stop automatically updating itself…).
So we have added this/these images as resources into our project but we still have to load them into the program, but before we do that I’ll teach you a quick way to make the equivalent of global variables in java (I’m new to java, coming from a C/C++ background, if there’s a better way to do this let me know). Add a new class to the project in a file named Globals.java. Inside this class you will put all of your "global" variables. The trick is to make them static, which means they exist as a single instance and do not rely on an object reference. So for example you could add some lines at the top of the Globals class like this:
public static final int DIRECTION_LEFT = 1;
public static final int DIRECTION_RIGHT = 2;
public static final int DIRECTION_UP = 3;
public static final int DIRECTION_DOWN = 4;
public static int xPos;
public static int yPos;
Since I also declared the first 4 to be final they are the equivalent of global constants in C. You can now reference these variables and definitions anywhere else in your program like so: Globals.DIRECTION_LEFT or Globals.xPos. You don’t need to instantiate an object of the Globals class, you can reference these static variables and constants through the class directly.
So, neat huh? It becomes very useful when you have hundreds of general purpose variables…
Okay, the reason I taught you that trick is because we are going to define some bitmap objects in this Globals class like so:
public static Bitmap Image1;
public static Bitmap Image2;
The bitmap class is in android.graphics.Bitmap so make sure you import that at the top of this file, or just use ctrl-shift-o to automatically add any missing imports.
So we’ve added some images to the project as resources, we’ve defined some bitmap objects, now we have to load those images to the objects. We will do this in our main class in the onCreate() method (which, again, is the entry point of our application).
Globals.Image1 = BitmapFactory.decodeResource(getResources(), R.drawable.image1);
Globals.Image2 = BitmapFactory.decodeResource(getResources(), R.drawable.image2);
Easy as that, the android API actually makes a lot of things like this super easy and for that we all should all be greatfull. The BitmapFactory class is from android.graphics.BitmapFactory.
Okay, so now we need to draw these images to the screen. Remember previously we set up a class that implemented the SurfaceView interface, I think we called it MySurfaceView… and we also setup a UI thread class? Well, if you look back our UI thread class accepts as the only argument to it’s constructor an object of our SurfaceView implementation. Using this we hold a reference to this SurfaceView and from it we get a reference to it’s SurfaceHolder… which has a method that allows us to lock it’s canvas. When we lock the canvas we get a reference to that canvas and we can use that to then draw to it… it seems convoluted but you have to understand the mechanism of double buffering that the android graphics engine uses to fully understand all of this… kind of beyond the purpose of this so I’m not going to go into it further.
ANYWAY, in the loop of the UI thread (in the run() method, the while(threadrunning) loop) is where you will be updating the screen. To do this you call lockCanvas through the surface holder and from that call get a reference to the SurfaceViews canvas like so:
MyCanvas = MySurfaceHolder.lockCanvas(null);
You have to lock the canvas before you draw to it, and you need a reference to that canvas to draw to it, and this one call gives us both of these things. I don’t remember if we had the MyCanvas variable defined in our last code samples but if not it is of the type android.graphics.Canvas.
Now, MyCanvas will have all sorts of interesting methods exposed through it, but the ones we are interested in right now are drawColor() and drawBitmap(). drawColor fills the entire screen with the color passed, and you can guess what drawBitmap does. But before we draw anything we have to synchronize with the thread that the surfaceview we are accessing is running on. Remember we are in the UI thread class, which is it’s own thread, and cannot safely access anything running on another thread without synchronizing with it first. We do this simply like so:
synchronized(MySurfaceHolder)
{
…
}
This is not a function call, synchronized is actually a keyword here, and the parameter is any object that belongs to the thread you want to synchronize with, we could probably use MyCanvas, or even MySurfaceView, but this worked and I haven’t tried anything else to be honest.
Inside that block we will first clear the screen to black. We must do this because unless you want to mess with declaring dirty regions and updating different portions of the screen asynchronously (which doesn’t buy you much in the way of efficiency anyway since the display is double buffered…) then you have to compose the entire image on the screen each time you update it. This might sound like a burden but it is actually very common and if you structure your program correctly it is simple to work with.
To clear the screen to black we call drawColor() mentioned previously:
MyCanvas.drawColor(Color.BLACK);
Now that you’ve cleared the screen from the last frame you can start drawing your images or text or whatever you’d like. To draw a previously defined and loaded bitmap do this:
MyCanvas.drawBitmap(Globals.Image1, 10, 20, null);
The parameters are the Bitmap object, the x-coordinate of the upper left pixel of the image, the y-coordinate of the upper left pixel of the image. The final parameter that we pass in null for is a Paint object… I’m not sure what effect defining a paint object would have here, since it’s a bitmap… but feel free to experiment if you’d like.
If you want to draw some text it’s just as easy as well:
MyCanvas.drawText("Hello Android!", 10, 10, new Paint());
If you want to change the font characteristics you can make an actual Paint object and set it’s attributes by calling various set() methods… (as far as I know this will draw black text on a black background so don’t be surprised if this doesn’t work… experiment with creating a paint object to customize the text on your own)
Once you are done drawing this frame you need to unlock the canvas and let the android graphics system know that you are ready to display this new frame. You do this by calling the method unlockCanvasAndPost(Canvas), which is a member of the SurfaceHolder class. Do so like this:
MySurfaceHolder.unlockCanvasAndPost(MyCanvas);
Make sure you call this outside of the synchronization block…
And that’s it, now you can draw images and text to the screen and if you explore the other methods of the Canvas class you’ll be able to draw some other things as well such as pixels and lines and circles and other graphics primitives.
Of course… I must add that there is no error handling in any of this… you may want to make sure that any object you attempt to reference is non-void before you do so… that when you expect LockCanvas to return a valid reference to a canvas to you that it actually does so, etc etc… You also probably don’t want to be updating the screen as fast as you possibly can, in a real game you would implement timers to time the update rate of the main screen and synchronize it with the game logic update rate and so on, but that is more along the lines of general game development theory and has nothing to do with Android development specifically so I’ll just leave it at this mention…
Have fun, experiment, and if you need any help post right here and I’ll get back to you. Next time we will make the touchscreen and the accelerometer work so you can use either or both of them to move things around on the screen.
Since you’re talking about images – I have this problem.
I’m using a database query to create a list of things I want. In that query is a database field that’s called "ImageRes" (TEXT field) – basically it references the name of the drawable in /res/drawable-mdpi .
So I’m creating a layout in xml:
<ImageView android:id="@+id/pic_name" android:src="???????" > <TextView android:id="@+id/ingredient">
I’m using a SimpleCursorAdapter to help me populate the TextView, but I’m not sure how to set the reference to help establish the ImageRes field as a drawable in the ImageView.
String[] from = new String[]{myDbAdapter.KEY_INGREDIENT,myDbAdapter.KEY_IMAGERES};
String[] to = new String[]{R.id.ingredient, R.??.?????};
SimpleCursorAdapter ingredientlist = new SimpleCursorAdapter(this, R.layout.ingredient_row, myCursor, from, to);
setListAdapter(ingredientlist);
The question marks is indicative of me not knowing what to put.
any ideas?
R.drawable.imagename references an integer ID assigned to that resource… it seems you want the actual name of the image?
You can get the name of a resource by calling getResources().getString(ID) where ID is the integer identified by R.drawable.imagename… so do something like this to get the name of a resource:
getResources().getString(R.drawable.imagename);
The name returned is in the form "R.drawable.imagename"… so if you want only the name itself you’ll have to remember to cut the rest out of the string.
As far as the XML I’m not sure but you could probably just use R.drawable.imagename directly as a string…
|
R.drawable.imagename references an integer ID assigned to that resource… it seems you want the actual name of the image?
You can get the name of a resource by calling getResources().getString(ID) where ID is the integer identified by R.drawable.imagename… so do something like this to get the name of a resource: getResources().getString(R.drawable.imagename); The name returned is in the form "R.drawable.imagename"… so if you want only the name itself you’ll have to remember to cut the rest out of the string. As far as the XML I’m not sure but you could probably just use R.drawable.imagename directly as a string… |
well the name of the image comes from the db…
So with ImageRes as the field
Ingredient |ImageRes ------------------------- Salt salt_pic Pepper pepper_pic
salt_pic actually exists in res/drawable-mdpi. When I use the cursor, I want my code to reference the ImageRes field as a drawable image when that particular entry comes up. (ex. I want salt_pic as my image when I pull up Salt)
I fixed this, after TWO HOURS:
SimpleCursorAdapter ingredientlist = new SimpleCursorAdapter(this, R.layout.ingredient_row, myCursor, from, to);
// HERE - I use a ViewBinder to look for an ImageView
ingredientlist.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
@Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
if (view instanceof ImageView) {
String imageRes = cursor.getString(cursor.getColumnIndex("ImageRes"));
int imgID = getResources().getIdentifier("drawable/"+imageRes, null, getPackageName());
((ImageView) view).setImageResource(imgID);
return true;
}
return false;
}
});
setListAdapter(ingredientlist);
If the View is an ImageView, then I get the string from ImageRes, then get the Indentifier based on the string. then I set the Image Resource. It’s a bit slow, but I’m just using test images, which I plan to optimize later.
Yeah I wasn’t sure what it was you were having trouble with specifically, glad you got it working though
Here are a few issues I’ve encountered the way… just in case somebody even reads this.
- I’m trying to use an existing database. I found that I had to drop the existing database into the assets folder, then basically copy the existing database to the one that the app actually uses. It’s pretty shitty – there should be an easier way, like just directly referencing the DB. I get a crash when the app first gets installed, but it’s the least of my problems right now as it works on the second time it attempts to run.
- You create a listview that contains a clickable relative layout. If using the list view, the background does NOT change automatically when you press/long press. You have to set it for that to happen.
- PNGs can be transparent, obviously. I forgot about that.
- Creating layouts is a lot trickier than it would be in HTML or the Visual Studio IDE.
I sometimes loathe these growing pains – but it’s helping me familiarize myself with Java and Android, and hopefully I’ll gain some self-satisifaction (and money) from this.
Oh yeah… to use the default highlight color:
Good luck with the android layouts and other pre-canned GUI stuff… I got a good look at that stuff and said no thanks and went straight for pixel plotting on a blank canvas
Finally got my HDMI cable so hopefully I’ll be able to record some video of the project I’m working on, and I also PLAN on updating this thread this weekend but only if it rains like it’s supposed to
Bumping this so we don’t lose it. I am about to start this tutorial. I just finished the Android dev blogs tutorials. This seems like a nice starting point.
I am programming for a NookColor, I think it runs Android 2.1
What platforms are the rest of you using?
Shit, I haven’t done any android programming in weeks. It’s been crunch time at work and I’ve been working 60+ hours and weekends, and OT lawyer crew crushed my dreams of releasing the game I was developing, saying I would be sued…
…I’ve already gone over how to load and display bitmap images to the screen, and from that you should be able to figure out anything you want to do graphics wise… I guess I could write a bit about various types of input, like using the accelerometer and the touch screen… Maybe also add something about playing sound/music.
No related posts.
Related posts brought to you by Yet Another Related Posts Plugin.