-1

我想在用戶觸摸屏幕時移動ImageView。我最初嘗試了onTouchEvent方法的while循環,但是這暫停了我需要的一些後臺進程。我現在有一個線程,我用它來控制我的遊戲,其中包含一個調用,用於根據Motion事件確定的名爲'movementSpeed'的浮動更新ImageView的位置。我相信這個問題與ImageView的初始化有關,但我嘗試了許多其他問題的解決方案,而這些解決方案似乎並不適合我。 我認爲這是否會因爲我從一個線程調用方法,並嘗試在UI線程上運行該方法,但它仍然無法工作。然而,我發現如果我從觸摸事件方法調用updateCharPos,位置將被更新,但這會導致上述執行其他任務的困難。ImageView nullPointerException

活動的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); 
} 
} 

線程代碼:

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); 
     } 
    }); 

    } 
} 

和我的堆棧跟蹤:

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 

任何幫助,將不勝感激!

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"/
+0

確定R.id.character指向一個ImageView的?你可以發佈你的activity_play佈局嗎? – mWhitley

+0

@mWhitley。從我activity_play佈局:我相信這是正確的嗎?另外,如果我從onTouchEvent方法調用updateCharPos()方法,它將更新位置,但會導致其他任務出現問題(上圖) – BFozard

+0

@BFozard是否可以發佈佈局代碼(xml)? – HenriqueMS

回答

0

編輯:

我沒有注意到,但是像@ njzk2在評論中寫道,你手動實例化擴展Activity類這也解釋了奇怪的行爲,我說的是

private Play play = new Play(); 

Activity的子類不應該直接實例化,對此here是一個很好的教程。

如果從內部活動這樣做,你可以:

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

您的實際呼叫流程是這樣的:

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

這可能不是你想要的嗎?

此外,如果從另一個線程開始一個活動,你需要有一個背景,因此您可以啓動新的活動

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

mContext.startActivity(myIntent); 

,你是在直接獲得NPE的事實電話讓我覺得你可能還沒有完全創造出奇怪的活動。

爲了進一步調試可以替代

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

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); 
+0

非常感謝您的幫助!你和@ njzk2是正確的,因爲它是由創建一個新的活動實例引起的。我最終在活動中使用了'public static Play play','play = this'。在onCreate()中。這使我可以通過'Play.play.updateCharPos(movementSpeed)'引用線程中現有活動的實例;''現在它完美的工作!再次感謝您的幫助! – BFozard

+0

@BFozard只是要小心這個ideia,通過保持對Activity的靜態引用,很有可能會阻止它被垃圾回收,從而泄漏內存。我建議你根據Android模式使用活動。我很高興我能幫助歡呼;) – HenriqueMS

0

假設R.id.character實際引用了有效的觀點,你可能要移動的後臺線程初始化thread=new GameThread();要麼在onStart()onResume()功能,以保證所需的UI組件是實際存在的。

有關Android生命週期的更多詳細信息,請參閱this SO post

+0

感謝您的建議@Boo。我檢查過R.id.character引用了一個有效的視圖並嘗試了以下修正:'@Override' protected void onStart(){ super.onStart(); thread = new GameThread(); thread.setRunning(true); thread.start(); }但問題仍然存在。奇怪的部分是,如果我從touch事件調用updateCharPos,它會起作用(儘管這會導致原始問題停止其他進程) – BFozard

相關問題