1. Download our Official Android App: Forums for Android!

Apps onTouch help

Discussion in 'Android Development' started by 9Ate7, May 9, 2012.

  1. 9Ate7

    9Ate7 Member
    Thread Starter
    Rank:
    None
    Points:
    15
    Posts:
    33
    Joined:
    May 9, 2012

    May 9, 2012
    33
    0
    15
    Hi i have a problem where the touch is not recognized at all, i don't know if the event is evening happening or not, or its an error with my display. My program as of now contains 4 classes

    1 - the main activity class
    2 - the draw class(simple draw everything)
    3 - Character class, (problem is here)
    4 - Sprite class, for simple animation

    Everything is working fine including the animation, but for some reason the touch is not working, here my code please help me.

    Code (Text):
    1. import java.util.ArrayList;
    2.  
    3. import android.content.Context;
    4. import android.graphics.Bitmap;
    5. import android.graphics.BitmapFactory;
    6. import android.graphics.Canvas;
    7. import android.graphics.Color;
    8. import android.graphics.Paint;
    9. import android.view.MotionEvent;
    10. import android.view.View;
    11. import android.view.View.OnTouchListener;
    12.  
    13. public class Character implements  OnTouchListener{
    14.  
    15.     private Paint paint = new Paint();
    16.     private String name;
    17.     private float mx,my;
    18.     private float x = 300;
    19.     private float y = 300;
    20.     private float Bwidth =25;
    21.     private float BHeight = 25;
    22.     private Sprite sprite;
    23.     private ArrayList<Bitmap> pics = new ArrayList<Bitmap>();
    24.     private Bitmap bm;
    25.  
    26.     public Character(String name,Context context){
    27.         this.name = name;
    28.         pics.add(bm = BitmapFactory.decodeResource(context.getResources(),R.drawable.test1));
    29.         pics.add(bm = BitmapFactory.decodeResource(context.getResources(),R.drawable.test2));
    30.         pics.add(bm = BitmapFactory.decodeResource(context.getResources(),R.drawable.test3));
    31.         pics.add(bm = BitmapFactory.decodeResource(context.getResources(),R.drawable.test4));
    32.         pics.add(bm = BitmapFactory.decodeResource(context.getResources(),R.drawable.test5));
    33.         pics.add(bm = BitmapFactory.decodeResource(context.getResources(),R.drawable.test6));
    34.         sprite = new Sprite(pics);
    35.  
    36.     }
    37.    
    38.    
    39.     public boolean onTouchEvent(MotionEvent e)
    40.     {
    41.              if(e.getAction() == MotionEvent.ACTION_DOWN)
    42.              {
    43.                      mx = e.getX();
    44.                      my = e.getY();
    45.              }
    46.              return true;
    47.     }
    48.     @Override
    49.     public boolean onTouch(View arg0, MotionEvent event) {
    50.         this.mx =  event.getRawX();
    51.         this.my =  event.getRawY();
    52.         return false;
    53.  
    54.     }
    55.    
    56.     public void delay(int i){
    57.         try {  
    58.              Thread.sleep(i);  
    59.          } catch (InterruptedException e) { }
    60.     }
    61.  
    62.     public void drawButtons(Canvas canvas){
    63.         paint.setColor(Color.YELLOW);
    64.         canvas.drawRect(45,295, 45 + Bwidth, 295 + BHeight, paint);//up
    65.         canvas.drawRect(10,330, 10 + Bwidth, 330 + BHeight, paint);//down
    66.         canvas.drawRect(45,365, 45 + Bwidth, 365 + BHeight, paint);//right
    67.         canvas.drawRect(80,330, 80 + Bwidth, 330 + BHeight, paint);//left
    68.         canvas.drawRect(750,30, 750 + Bwidth, 30 + BHeight, paint);
    69.     }
    70.  
    71.     public void Draw(Canvas canvas){
    72.         this.drawButtons(canvas);
    73.         this.sprite.draw(canvas);
    74.         this.delay(300);
    75.         paint.setColor(Color.BLUE);
    76.         canvas.drawText("X: "+ (int)this.mx +" "+ "Y: "+ (int)this.my, 370, 30 ,paint);
    77.         this.sprite.update();
    78.     }
    79.  
    80. }
    The set up and style of the class is really bad, but i amjust getting started and ran into this problem.

    Thank You
     

    Advertisement

  2. GeorgeN

    GeorgeN Well-Known Member
    Rank:
    None
    Points:
    73
    Posts:
    132
    Joined:
    Apr 16, 2012

    Apr 16, 2012
    132
    38
    73
    Male
    London
    Have you called View.setOnTouchListener() to register your Character class as a touch event listener?
     
  3. 9Ate7

    9Ate7 Member
    Thread Starter
    Rank:
    None
    Points:
    15
    Posts:
    33
    Joined:
    May 9, 2012

    May 9, 2012
    33
    0
    15
    No how do i use that, do i extend View in my character class? Do i use this, cause i tried this and i dont know how to get x and y values back from here.
    Code (Text):
    1. public class Character extends View implements  OnTouchListener{
    2.  
    3.         public Character(String name,Context context){
    4.         super(context);
    5.         View.setOnTouchListener(new onTouchListener()){
    6.                      public onTouch(blah)
    7.          }    
    8.  
     
  4. GeorgeN

    GeorgeN Well-Known Member
    Rank:
    None
    Points:
    73
    Posts:
    132
    Joined:
    Apr 16, 2012

    Apr 16, 2012
    132
    38
    73
    Male
    London
    Unfortunatly this sort of thing is quite complex. The problem is that the Android framework doesn't know anything about your Character class. It doesn't know where your character is on the screen or how big it is. There is no way for it to know when you are touching the Character object (For example if you have 2 Characters it won't be able to figure out which one you are touching).

    You don't need to extend the View class - you must already have some sort of View that you are drawing on. You should register your Character class as it's touch event handler. Then when the view is clicked your character will be notified. If you have multiple characters you need to figure out a way of detecting which Character you are clicking on. So you will probably need to tweak your design so that your touch handler isn't a part of the Character class.

    In my game I had a class that extended the GLSurfaceView object, then I added my own implementation of the onTouchEvent() function. I wrote a load of my own layout classes to layout all the sprites (a list layout, a flow layout, a grid layout etc..). When I get a touch event it is passed to the main layout class, this class then looks at any child layouts and figures out which one we are clicking on. The event is passed to the child and the process repeats. Eventually the event is matched up to an individual sprite and it handles the event.

    Assuming you are planning on having lots of sprites which aren't arranged in a nice grid, you will need to store them all in a Vector. Then when you get a touch event you need to iterate over all the sprites and see if the event collides with a sprite - if it does then that sprite handles the event.
     
  5. 9Ate7

    9Ate7 Member
    Thread Starter
    Rank:
    None
    Points:
    15
    Posts:
    33
    Joined:
    May 9, 2012

    May 9, 2012
    33
    0
    15
    I see, in my case the touch has nothing to do with the sprite, the touch is for a button, (right/left/ect), these button are drawn in the canvas for now, and they are rectangles, what i wanted to do was that see if the touch was in the rectangle of the button and then just execute a command, i tried adding the touch listener to the Draw class, which simply draws everything and extends view, but that didn't seems to work either
     
  6. GeorgeN

    GeorgeN Well-Known Member
    Rank:
    None
    Points:
    73
    Posts:
    132
    Joined:
    Apr 16, 2012

    Apr 16, 2012
    132
    38
    73
    Male
    London
    Google have some documentation here: Input Events | Android Developers

    Make sure your view is enabled and focusable. If you are extending View directly then you don't really need to use an OnTouchListener. You can just override the View.onTouchEvent() API.

    Also, put a Log statement in your touch handler to print a message to logcat when it detects a touch event. Its handy to get a feel for the different types of touch events you get.
     
  7. 9Ate7

    9Ate7 Member
    Thread Starter
    Rank:
    None
    Points:
    15
    Posts:
    33
    Joined:
    May 9, 2012

    May 9, 2012
    33
    0
    15
    alright i will try and do what i can, but while we are on the development topic i have been trying to get sprites to load properly in my class, but for now i just load them in the constructor, the problem is i have to load each file separately, is there a way where i pass in a String and it gets all files form that directory, and load them to the Bitmap

    the decodeFile dosent work because it crashes, but the

    decodeRecource(context.resource,r.location.file) works, but with that i have to specify each file

    i would like to get all pictures from a file, given a String path and store then in a list so i can draw them
     
  8. GeorgeN

    GeorgeN Well-Known Member
    Rank:
    None
    Points:
    73
    Posts:
    132
    Joined:
    Apr 16, 2012

    Apr 16, 2012
    132
    38
    73
    Male
    London
    I'm not aware of any API's that do that. I also manually load all my graphics data (triangulated mesh data rather than bitmaps) as they are required.

    Rather than loading all the data at once in the constructor (and slowing down application startup). The files are loaded on demand the first time they are needed. In my case the files are assets rather than "resources" (basically just binary files that are lumped in with the apk file in the /assets directory) but the principle is the same. I wrote a simple resource manager which looks something like this:

    Code (Text):
    1.  
    2. static HashMap<String, ModelData> mModelData = new HashMap<String, ModelData>();
    3.  
    4. static Model GetModel(String aFilename) {
    5.     if(!mModelData.containsKey(aFilename)) {
    6.         ModelData data = new ModelData(aFilename);
    7.         if(!data.isValid())
    8.             return null;
    9.         mModelData.put(aFilename, data);
    10.         return new Model(data);
    11.     }
    12.        
    13.     ModelData data = mModelData.get(aFilename);
    14.     return new Model(data);
    15. }
    16.  
    Basically the first time a model is loaded it gets cached in a HashMap. Then in the future, if I request the same model again it grabs the data from the HashMap. It makes sure that even if I reuse the same model over and over, the model data is only ever loaded from disk once, and there is only ever one copy of the data in ram at a time. If you are reusing the same bitmaps over and over, you might want to do something similar.

    You might like to look at the JetBoy example application JetBoy - JetBoy | Android Developers, they are also calling decodeResource over and over in the constructor.
     
  9. 9Ate7

    9Ate7 Member
    Thread Starter
    Rank:
    None
    Points:
    15
    Posts:
    33
    Joined:
    May 9, 2012

    May 9, 2012
    33
    0
    15
    Thank you very much, i tried handling all the Touch events in the draw class but that doesn't seem to work either, i having it print out a log message on each touch but that log is never printed, so for some reason its not registering the touches
     
  10. GeorgeN

    GeorgeN Well-Known Member
    Rank:
    None
    Points:
    73
    Posts:
    132
    Joined:
    Apr 16, 2012

    Apr 16, 2012
    132
    38
    73
    Male
    London
    I just wrote a quick test.

    I have two classes:

    TouchDemoActivity.java
    Code (Text):
    1.  
    2. package com.androidforums.touchdemo;
    3.  
    4. import android.app.Activity;
    5. import android.os.Bundle;
    6.  
    7. public class TouchDemoActivity extends Activity {
    8.     /** Called when the activity is first created. */
    9.     @Override
    10.     public void onCreate(Bundle savedInstanceState) {
    11.         super.onCreate(savedInstanceState);
    12.         TouchView myView = new TouchView(this);        
    13.         setContentView(myView);
    14.     }
    15. }
    16.  
    TouchView.java
    Code (Text):
    1.  
    2. package com.androidforums.touchdemo;
    3.  
    4. import android.content.Context;
    5. import android.util.Log;
    6. import android.view.MotionEvent;
    7. import android.view.View;
    8.  
    9. public class TouchView extends View {
    10.     final String TAG = "TouchDemo";
    11.    
    12.     public TouchView(Context context) {
    13.         super(context);
    14.         // TODO Auto-generated constructor stub
    15.     }
    16.    
    17.     public boolean onTouchEvent(MotionEvent motion) {
    18.         Log.d(TAG, "Got a touch event (action="+motion.getAction()+" @ "+motion.getX()+", "+motion.getY()+")");
    19.         return true;
    20.     }
    21. }
    22.  
    When you run it you will get a black screen. When you touch the screen you will see messages in logcat indicating the touch event type and location, for example:

    Code (Text):
    1.  
    2. 05-09 23:07:33.084: D/TouchDemo(796): Got a touch event (action=0 @ 116.0, 357.0)
    3. 05-09 23:07:33.184: D/TouchDemo(796): Got a touch event (action=2 @ 115.0, 350.0)
    4. 05-09 23:07:33.208: D/TouchDemo(796): Got a touch event (action=2 @ 120.0, 336.0)
    5.  
     
  11. 9Ate7

    9Ate7 Member
    Thread Starter
    Rank:
    None
    Points:
    15
    Posts:
    33
    Joined:
    May 9, 2012

    May 9, 2012
    33
    0
    15
    Thanks! i got the touch to work finally, i was wondering since i have sprites, and i when i play an animation, i wanna play the whole thing once then exit the function or whatever, but other animation i want to have running continuously, should i thread the whole sprite class or simply add methods to get what i want?
     
  12. GeorgeN

    GeorgeN Well-Known Member
    Rank:
    None
    Points:
    73
    Posts:
    132
    Joined:
    Apr 16, 2012

    Apr 16, 2012
    132
    38
    73
    Male
    London
    I wouldn't create a thread per sprite, you would defiantly run into synchronisation issues. Just create a single periodic timer thread that runs say 20 or 30 times a second. When it runs update the state of all your sprites (i.e. increment the frame counter and move them about). Each individual sprite should store some information - like the current frame number and whether the animation loops or stops at the end.

    Using a single timer with a fixed interval is good because it will ensure you game always runs at the same speed on every device. And that each sprite animation is running at the same speed as the others.

    You will also need some way of invalidating the portions of the screen that you have redrawn, so that the android framework knows the screen is dirty and it needs to redraw it.
     
  13. 9Ate7

    9Ate7 Member
    Thread Starter
    Rank:
    None
    Points:
    15
    Posts:
    33
    Joined:
    May 9, 2012

    May 9, 2012
    33
    0
    15
    Alright i will try that, back to loading the sprites issues how should i load the sprites because cause the way i was thinking of formating the structure is:

    res>drawable>character>punch,kick,stand (the punch and these folders will contain pictures and the total amount of the pictures will vary.

    i have this set up to some extent now, should i change this and if so how, where should i put the pictures and by what methods should i get them. Should i use a hashmap or a hash set ir oder for loading them, cause there will be multiple sprites on the screen but only one set of controls, i do plan on including a loading screen so that all nessecary items can be loaded so the program runs nicely

    You mentioned before to put the files in the assets folder, do i categorize them like character>moves or just dump all files in there
     
  14. GeorgeN

    GeorgeN Well-Known Member
    Rank:
    None
    Points:
    73
    Posts:
    132
    Joined:
    Apr 16, 2012

    Apr 16, 2012
    132
    38
    73
    Male
    London
    I think you are probably best sticking with resources. I used assets because my model data is stored in my own made up file format. If you have something that works then stick with it for now. But if you find your game takes forever to load, then you might need to rethink it a bit.
     
  15. 9Ate7

    9Ate7 Member
    Thread Starter
    Rank:
    None
    Points:
    15
    Posts:
    33
    Joined:
    May 9, 2012

    May 9, 2012
    33
    0
    15
    Thanks once again, but if i do it my way i need to load multiple files, and i wanna use a string, if not i am gonna have 200 lines of just loading pictures in my constructor.

    private int[] getAllRawResources() {
    int[] ids = null ;
    R.raw r = new R.raw() ;

    Field fields[] = R.raw.class.getDeclaredFields() ;
    ids = new int[fields.length] ;

    try {
    for( int i=0; i<fields.length; i++ ) {
    Field f = fields ;
    ids = f.getInt(r) ;
    Log.i(TAG, "R.raw."+f.getName()+" = 0x"+Integer.toHexString(ids)) ;
    }
    } catch (IllegalArgumentException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    } catch (IllegalAccessException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }

    return ids ;
    }

    I found that little piece of code, but i don't know how i can pass in a string and it gets me a bitmap form that file, i tried playing around with it on my own by failed, i think this return a list with all the file names, but when i try and use the decode File and try to draw from a list of type Bitmap it crashes and that's where i stopped.
     
  16. GeorgeN

    GeorgeN Well-Known Member
    Rank:
    None
    Points:
    73
    Posts:
    132
    Joined:
    Apr 16, 2012

    Apr 16, 2012
    132
    38
    73
    Male
    London
  17. 9Ate7

    9Ate7 Member
    Thread Starter
    Rank:
    None
    Points:
    15
    Posts:
    33
    Joined:
    May 9, 2012

    May 9, 2012
    33
    0
    15
    Alright thank you very much for your help.:)
     
  18. 9Ate7

    9Ate7 Member
    Thread Starter
    Rank:
    None
    Points:
    15
    Posts:
    33
    Joined:
    May 9, 2012

    May 9, 2012
    33
    0
    15
    I decided to restart and my code follows a similar layout, 4 classes:

    Activity Class --> set context to world
    World --> draw everything including background/players/ect
    Characters --> either the user or the computer
    Sprite --> animation class

    Before i had it such that the World (Draw class from previous post) would handle all the input, but that is wrong, each character should handle its own input, so i decided to start over again following that format, the problem is that it doesn't recognize the touch event but if i copy and paste the same method in the "World.class" then its working fine. What is the problem, is it my code or android? Should i follow a different format of making this? How do i fix it if it occurs again?

    I have tried implements onTouchListener in my character class but that didn't seem to work

    Code (Text):
    1. package com.test;
    2.  
    3. import android.content.Context;
    4. import android.graphics.Canvas;
    5. import android.graphics.Color;
    6. import android.util.Log;
    7. import android.view.MotionEvent;
    8. import android.view.View;
    9.  
    10. public class World extends View {
    11.     private Character test;
    12.    
    13.    
    14.     public World(Context context) {
    15.         super(context);
    16.         test = new Character("Test", context);
    17.     }
    18.    
    19.     public void delay(int i){
    20.         try {  
    21.              Thread.sleep(i);  
    22.          } catch (InterruptedException e) { }
    23.     }
    24.    
    25.    
    26.     public void onDraw(Canvas canvas){
    27.         canvas.drawColor(Color.BLACK);
    28.         this.test.draw(canvas);
    29.         this.delay(100);
    30.         invalidate();
    31.     }
    32.  
    33. }
    34.  
    Code (Text):
    1. package com.test;
    2.  
    3. import android.content.Context;
    4. import android.graphics.Canvas;
    5. import android.graphics.Color;
    6. import android.graphics.Paint;
    7. import android.util.Log;
    8. import android.view.MotionEvent;
    9. import android.view.View;
    10. import android.view.View.OnTouchListener;
    11.  
    12. public class Character extends View{
    13.     private String name;
    14.     private Paint paint = new Paint();
    15.     private float x,y;
    16.    
    17.     public Character(String a, Context context){
    18.         super(context);
    19.     }
    20.    
    21.     public boolean onTouchEvent(MotionEvent motion) {
    22.         Log.d("TouchEvent", "Got a touch event (action="+motion.getAction()+" @ "+motion.getX()+", "+motion.getY()+")");
    23.         return true;
    24.     }
    25.  
    26.     public void draw(Canvas canvas){
    27.         paint.setColor(Color.YELLOW);
    28.     }
    29.  
    30.     public String getName() {
    31.         return name;
    32.     }
    33.  
    34.     public void setName(String name) {
    35.         this.name = name;
    36.     }
    37.  
    38.  
    39. }
    40.  
     
  19. GeorgeN

    GeorgeN Well-Known Member
    Rank:
    None
    Points:
    73
    Posts:
    132
    Joined:
    Apr 16, 2012

    Apr 16, 2012
    132
    38
    73
    Male
    London
    The Android framework doesn't know where you are actually drawing your Character on the screen so it won't know if you have touched it.

    The easiest thing to do is trap the touch event in your world class, then iterate over all the character objects and offer them the event. Each character object must look at the touch co-ordinates and figure out if the event occurred somewhere inside its bounding box. If so, it should deal with the event otherwise it can ignore it.
     
  20. 9Ate7

    9Ate7 Member
    Thread Starter
    Rank:
    None
    Points:
    15
    Posts:
    33
    Joined:
    May 9, 2012

    May 9, 2012
    33
    0
    15
    How do i tell andorid i am drawing in the Character class?
     
  21. GeorgeN

    GeorgeN Well-Known Member
    Rank:
    None
    Points:
    73
    Posts:
    132
    Joined:
    Apr 16, 2012

    Apr 16, 2012
    132
    38
    73
    Male
    London
    You don't!

    The whole UI is made up of nested views. Some views are simple controls like buttons or your character. Others are containers like a list view or a grid view.

    When you touch the screen the event gets picked up by Android and then passed to the foreground app. If the main view in the foreground app has a list view, then the list view object will be passed the event. It then looks at all its children and figures out which one it needs to be passed to. Eventually it gets to a view which can handle the event.

    In your case you aren't using the standard layout classes to size and position your character objects. So there isn't anything to pass you events. You need to do the hard work yourself instead. It should be pretty easy to implement something (you can easily optimised this further - e.g. ignore Characters which are off the edge of the screen). For starters try something like this..

    In your world class you need:

    Some sort of list of all the characters:
    Code (Text):
    1. Vector<Character> characters;
    A touch handler to offer the event to characters:

    Code (Text):
    1.  
    2. public boolean onTouchEvent(MotionEvent motion) {
    3.     boolean consumed = false;
    4.     Iterator it = v.iterator();
    5.     while(it.hasNext()) {
    6.         if(it.next().onTouchEvent(MotionEvent motion)) {
    7.             consumed=true;
    8.         }
    9.     }
    10.     return consumed;
    11. }
    12.  
    And in your character class, another touch event handler (this one gets called by the handler in the world class):

    Code (Text):
    1.  
    2. public boolean onTouchEvent(MotionEvent motion) {
    3.     boolean consumed = false;
    4.     if(motion.getX()>=x && motion.getX()<(x+width) &&
    5.         motion.getY()>=y && motion.getY()<(y+height)) {
    6.         // This event occurred inside the Character, so handle the event!
    7.         consumed = true;
    8.     }
    9.     return consumed;
    10. }
    11.  
     
  22. 9Ate7

    9Ate7 Member
    Thread Starter
    Rank:
    None
    Points:
    15
    Posts:
    33
    Joined:
    May 9, 2012

    May 9, 2012
    33
    0
    15
  23. 9Ate7

    9Ate7 Member
    Thread Starter
    Rank:
    None
    Points:
    15
    Posts:
    33
    Joined:
    May 9, 2012

    May 9, 2012
    33
    0
    15
    I have a sprite class such that i pass in a string path and it gets all the files from that directory and loads them in a list. I then use that list to get bitmaps my name, but i keep getting a null pointer exception. The file is being found, but the bitmap is null, the decodeStream returns null if the bitmap could not be decoded, any ideas why this is happening?

    EDIT: nvm i got it, it was a stupid error

    And Thanks for your help! :D
     

Share This Page

Loading...