-1

I am trying to move an ImageView whilst the user touches the screen. I originally tried a while loop from an onTouchEvent method but this halted some background processes that I needed. I now have a thread that I use to control my game which involves a call to update my ImageView's position based on a float called 'movementSpeed' which is determined by Motion events. The issue, I believe, is with the initialization of my ImageView but I have tried many solutions from other questions which don't seem to work for me. I considered whether this would be because I was calling the method from a Thread and tried running the method on the UI thread but it still did not work. However, I have discovered that the position will be updated if I call updateCharPos from my touch event method, although this would cause the aforementioned difficulty in carrying out other tasks.

Activity Java:

package fozard.backuptestapp;


public class Play extends AppCompatActivity {

private GameThread thread;
private float charX=0;
ImageView character;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_play);
    character = (ImageView) findViewById(R.id.character);
}

@Override
protected void onStart(){
    super.onStart();
    thread=new GameThread();
    thread.setRunning(true);
    thread.start();
}

public boolean onTouchEvent(MotionEvent event){
    switch (event.getAction()){
        case MotionEvent.ACTION_DOWN:
            thread.setMovementSpeed(5);
            thread.setMoving(true);
            break;
        case MotionEvent.ACTION_UP:
            thread.setMoving(false);
    }
    return true;
}

public void updateCharPos(float movementSpeed){
    Log.d("Updating", "Should be able to update");
    charX = character.getX();
    System.out.print(charX);
}
}

Thread code:

public class GameThread extends Thread {

public static final int maxFps=30;
private double averageFps;
private boolean isRunning;
private int score=0;
private Play play = new Play();
private boolean isMoving;
private float movementSpeed=0;


public void setMovementSpeed(float movementSpeed){
    this.movementSpeed = movementSpeed;
}

public void setMoving(Boolean isMoving){
    this.isMoving = isMoving;
}

public void setRunning(Boolean isRunning){
    this.isRunning = isRunning;
}


@Override
public void run(){
    long startTime;
    long timeMillis;
    long waitTime;
    int frameCount=0;
    long totalTime=0;
    long targetTime= 1000/maxFps;

    while (isRunning){
        startTime = System.nanoTime();

        timeMillis = (System.nanoTime()-startTime)/1000000;
        waitTime = targetTime-timeMillis;
        try{
            if (waitTime>0){
                currentThread().sleep(waitTime);
            }
        }catch (Exception e){

        }
        totalTime += System.nanoTime() - startTime;
        frameCount++;
        score=score+1;
        if (isMoving){
                try{
                    update();
                }catch (Exception e){
                    e.fillInStackTrace();
                }
        }

        if (frameCount==maxFps){
            averageFps=1000/((totalTime/frameCount)/1000000);
            frameCount=0;
            totalTime=0;
            System.out.println(averageFps);
        }
    }
}

public void update(){
    play.runOnUiThread(new Runnable() {
        @Override
        public void run() {
            play.updateCharPos(movementSpeed);
        }
    });

   }
}

and my stack trace:

11-23 19:46:51.715 27465-27504/fozard.backuptestapp I/System.out: 30.0
11-23 19:46:52.710 27465-27504/fozard.backuptestapp I/System.out: 30.0
11-23 19:46:53.705 27465-27504/fozard.backuptestapp I/System.out: 30.0
11-23 19:46:54.105 27465-27465/fozard.backuptestapp D/Updating: Should be able to update
11-23 19:46:54.105 27465-27465/fozard.backuptestapp D/AndroidRuntime: Shutting down VM
11-23 19:46:54.105 27465-27465/fozard.backuptestapp W/dalvikvm: threadid=1: thread exiting with uncaught exception (group=0x41d29700)
11-23 19:46:54.125 27465-27465/fozard.backuptestapp E/AndroidRuntime: FATAL EXCEPTION: main
                                                                      java.lang.NullPointerException
                                                                      at android.app.Activity.findViewById(Activity.java:1914)
                                                                      at fozard.backuptestapp.Play.updateCharPos(Play.java:55)
                                                                      at fozard.backuptestapp.GameThread$1.run(GameThread.java:88)
                                                                      at android.os.Handler.handleCallback(Handler.java:730)
                                                                      at android.os.Handler.dispatchMessage(Handler.java:92)
                                                                      at android.os.Looper.loop(Looper.java:176)
                                                                      at android.app.ActivityThread.main(ActivityThread.java:5419)
                                                                      at java.lang.reflect.Method.invokeNative(Native Method)
                                                                      at java.lang.reflect.Method.invoke(Method.java:525)
                                                                      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1046)
                                                                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:862)
                                                                      at dalvik.system.NativeStart.main(Native Method)
11-23 19:46:54.840 27465-27504/fozard.backuptestapp I/System.out: 30.0
11-23 19:46:55.835 27465-27504/fozard.backuptestapp I/System.out: 30.0

any help would be appreciated!

Xml:

ImageView
    android:layout_width="100dp"
    android:layout_height="100dp"
    android:layout_alignParentBottom="true"
    android:layout_centerHorizontal="true"
    android:src="@drawable/character"
    android:id="@+id/character"
    android:scaleType="fitCenter"
    android:clickable="false"
    android:longClickable="false" /
BFozard
  • 3
  • 3
  • Are you sure R.id.character is pointing to an ImageView? Can you post your activity_play layout? – mWhitley Nov 23 '16 at 20:34
  • @mWhitley . from my activity_play layout: I believe that this is correct? Also if I call the updateCharPos() method from the onTouchEvent method it will update the position but causes an issue with other tasks (above) – BFozard Nov 23 '16 at 22:07
  • @BFozard can you post your layout code (xml)? – HenriqueMS Nov 24 '16 at 12:36
  • @HenriqueMS I've added it now – BFozard Nov 24 '16 at 14:12
  • @BFozard I was hoping for the complete R.layout.activity_play file that you are inflating ;) – HenriqueMS Nov 24 '16 at 15:11
  • `Play play = new Play();` you are not allowed to create activities yourself. Use the activity that was started by android instead – njzk2 Nov 24 '16 at 15:14

2 Answers2

0

Assuming that R.id.character actually references a valid view, you may want to move the background thread initialization thread=new GameThread(); to either the onStart() or onResume() function to guarantee that the required UI components are actually present.

See this SO post for more details about the Android Lifecycle.

Community
  • 1
  • 1
Boo Radley
  • 684
  • 1
  • 13
  • 25
  • Thanks for the suggestion @Boo. I've checked that R.id.character does reference a valid view and tried the following ammendment: '@Override' protected void onStart(){ super.onStart(); thread=new GameThread(); thread.setRunning(true); thread.start(); } but the issue still occurs. The weird part is that if i call the updateCharPos from the touch event it works (although this causes the original issue with halting other processes) – BFozard Nov 23 '16 at 22:00
0

EDIT:

I had not noticed but like @njzk2 wrote in the comments you are manually instantiating a class that extends Activity which explains the strange behaviour I was talking about

private Play play = new Play();

Activity's subclasses should not be directly instantiated, right here is a good tutorial in this.

If you are doing this from inside Activity you can:

    Intent intent = new Intent(this, NAME_OF_NEW_ACTIVITY.class);
    startActivity(intent);

Your actual call flow goes like this :

Play (Activity) -> new GameThread() -> new Play() (Activity) -> new GameThread()

It's probably not what you want right?

Moreover, if you are starting an Activity from another thread, you need to have a Context so you can launch the new activity

Intent myIntent = new Intent(mContext, Play.class);

mContext.startActivity(myIntent);

The fact that you are getting the NPE directly in the findViewById() call makes me think that you may not yet have the activity fully created which is strange.

For further debugging can you substitute

character = (ImageView) findViewById(R.id.character);

for

    LayoutInflater mInflater;
    mInflater = LayoutInflater.from(this);

    //breakpoint here, check if you have the mInflater
    View layoutView = mInflater.inflate(R.layout.activity_play, null);

    //breakpoint here, check if you have the layoutView
    ImageView character = (ImageView) layoutView.findViewById(R.id.character);

    //if you get here you should be in the clear
    setContentView(layoutView);
HenriqueMS
  • 3,864
  • 2
  • 30
  • 39
  • Thank you so much for your help! You and @njzk2 were right about it being caused by creating a new instance of the activity. I ended up using 'public static Play play' within the activity and 'play=this;' within onCreate(). This allowed me to reference the existing instance of the activity from the thread by 'Play.play.updateCharPos(movementSpeed);' and it now works perfectly! Thanks again for the help! – BFozard Nov 24 '16 at 21:49
  • @BFozard just be careful with that ideia, by keeping the static reference to the Activity there is a high probability that you are keeping it from being garbage collected, therefore leaking memory. I would advise you to use Activities according to Android patterns. I'm glad I could help cheers ;) – HenriqueMS Nov 24 '16 at 22:24