Android Development

Playing with graphics in Android – Part III

by Martin on May.18, 2009, under tutorial

You are new to this series? Please start with the first part.

The third part of this series will add some interactivity to our little application. We will add the possibility the icon appears at the point you touch the screen.

Just to remember, we have 3 classes: Tutorial2D (our Activity class), Panel (our SurfaceView class) and TutorialThread (our Thread class).
Our interaction will be handled in our Panel class, so we must be sure, that the touch events will processed by the Panel class. To ensure that, we simply have to set the focus to our panel (line 5).

1
2
3
4
5
6
public Panel(Context context) {
    super(context);
    getHolder().addCallback(this);
    _thread = new TutorialThread(getHolder(), this);
    setFocusable(true);
}


To show the bitmap on different locations, we have to add 2 variables for the x and y coordinates and we have to modify the onDraw() method.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Panel extends SurfaceView implements SurfaceHolder.Callback {
    private TutorialThread _thread;
    private int _x = 20;
    private int _y = 20;
 
    // code snipped
 
    @Override
    public void onDraw(Canvas canvas) {
        Bitmap _scratch = BitmapFactory.decodeResource(getResources(), R.drawable.icon);
        canvas.drawColor(Color.BLACK);
        canvas.drawBitmap(_scratch, _x, _y, null);
    }
    // code snipped
}

Finally we need to override the onTouchEvent() where we get the coordinates of the touch and set our variables.

1
2
3
4
5
6
@Override
public boolean onTouchEvent(MotionEvent event) {
    _x = (int) event.getX();
    _y = (int) event.getY();
    return true;
}

Now we can compile and start our application.
You will see, that the application will draw the bitmap not exactly where you touch the screen. The reason is that the coordinates are for the upper left corner of the bitmap. To change that, we have to use a little bit math.

1
2
3
4
5
6
@Override
public void onDraw(Canvas canvas) {
    Bitmap _scratch = BitmapFactory.decodeResource(getResources(), R.drawable.icon);
    canvas.drawColor(Color.BLACK);
    canvas.drawBitmap(_scratch, _x - (_scratch.getWidth() / 2), _y - (_scratch.getHeight() / 2), null);
}

As you see, we have to subtract half of the bitmap width from x and half of bitmap height from y to get the middle of the bitmap on the point we touched the screen.

Finally our Panel class looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class Panel extends SurfaceView implements SurfaceHolder.Callback {
    private TutorialThread _thread;
    private int _x = 20;
    private int _y = 20;
 
    public Panel(Context context) {
        super(context);
        getHolder().addCallback(this);
        _thread = new TutorialThread(getHolder(), this);
        setFocusable(true);
    }
 
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        _x = (int) event.getX();
        _y = (int) event.getY();
        return true;
    }
 
    @Override
    public void onDraw(Canvas canvas) {
        Bitmap _scratch = BitmapFactory.decodeResource(getResources(), R.drawable.icon);
        canvas.drawColor(Color.BLACK);
        canvas.drawBitmap(_scratch, _x - (_scratch.getWidth() / 2), _y - (_scratch.getHeight() / 2), null);
    }
 
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        // TODO Auto-generated method stub
    }
 
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        _thread.setRunning(true);
        _thread.start();
    }
 
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        // simply copied from sample application LunarLander:
        // we have to tell thread to shut down & wait for it to finish, or else
        // it might touch the Surface after we return and explode
        boolean retry = true;
        _thread.setRunning(false);
        while (retry) {
            try {
                _thread.join();
                retry = false;
            } catch (InterruptedException e) {
                // we will try it again and again...
            }
        }
    }
}

Go to Playing with graphics in Android – Part IV

  • Share/Bookmark
:, , , ,

23 Comments for this entry

  • Leo Romanovsky

    Thanks for the good post.

    What are your thoughts on placing the onTouchListener in the thread instead of the panel?

  • Martin

    I always think about the fact, who is responsible for what. In this case I ask you: What does the game loop have to do with touch events?
    I don’t touch the game loop, but I touch the view which means the panel… also the focus is on the view.

    To handle touch events in the thread is possible, but I wouldn’t recommend that.

  • Udai

    Hey Martin,
    Hello, i am a beginner in Android field. I just saw your post and i just liked it. I wanted to know that who calls he onDraw() method.

  • Martin

    Hey Udai,
    as I mentioned in the second part, we have a thread class called TutorialThread. In this class, we call the onDraw() method.

  • Udai

    Thanks Martin,
    But i meant that how onDraw() method is called internally…and if possible can we call it by our own as per our requirement..

  • Martin

    and if possible can we call it by our own as per our requirement
    Of course we can call it by ourself.

    How Android use the View is explained on How Android Draws Views.

  • Michael

    Fantastic post and I agree with you on the on touch event being associated with the view. Extremely well done article and I am happy to see that you follow good design principles.

  • Tom

    Great series Martin,

    keep up the tutorials!

  • Vishnu Pedireddi

    hi Martin,

    The series have been very good. They are really helpful. however, isn’t there a need to synchronize the integer variable when you modify them in the “OnTouchEvent” ? The thread continuously accesses the value of _x and _y, and all of a sudden it is changed by some its parent thread. Is there a need for mutual exclusion ?? or is it an overkill ???

  • Mike

    Hey I had great luck with parts 1 and 2 but somehow it is telling me “the method onTouchEvent must override a supertype method” regarding the code

    @Override
    public boolean onTouchEvent(MotionEvent event) ”

    I’d think “onTouchEvent” would be super enough.
    what am I doing wrong?

  • Martin

    Your Java is not uptodate.

    There are two possible solutions:
    1. Simply remove the @Override annotations
    2. (the better solution) update your java sdk to version 1.6

  • Harald

    Thank you for this and other fantastic tutorials.

    Keep up the good work!

  • Rob

    Hey, thanks for the great tutorials. I too have the “the method onTouchEvent must override a supertype method” error, and my java is up to date. I have the latest version of the JDK, the latest version of Eclipse, and the latest version of the Android SDK. Any suggestions?

    Thanks,
    Rob

  • Martin

    Hi Rob.
    Maybe you have an old Java installation somewhere around and your Eclipse is using this one?
    Check in you cmd/console the output of

    1
    2
    3
    4
    
    # java -version
    java version "1.6.0_0"
    OpenJDK Runtime Environment (IcedTea6 1.6.1) (6b16-1.6.1-3ubuntu1)
    OpenJDK Server VM (build 14.0-b16, mixed mode)

    Another possibility is, you have the wrong compiler compliance level. Check this in Eclipse -> Preferences -> Java -> Compiler. The compliance level should be 1.6.

    Hope it solves the problem :)

  • John

    I am a little confused on what tells the app to redraw the canvas when the screen is touched.

  • Martin

    The screen will be automatically redrawn on every run of the thread loop. Check the class TutorialThread.

  • John

    Oh, so even when we don’t click the screen the canvas is redrawn with the image in the same place?

  • Martin

    Your right! Its not necessary at the moment, but at the end of the series, you will see the reason (also mentioned in some comments there).

  • John

    Ok, I understand it now. Tanks Martin for your help and for the great tutorials!

  • abhi

    mind blowing… really very nice article..

  • Sam

    Mate this is very very good! Please keep up the posts. Thanks

  • Sam

    I ran the onTouch event code and it compiles. This might be a silly question but I guess since its running in the emulator, there shouldn’t be a change is the position of the image.. should it?

  • Martin

    Sorry, but I really don’t understand what you are trying to say.
    Can you be more specific?

Leave a Reply