【问题标题】:ImageView nullPointerExceptionImageView 空指针异常
【发布时间】:2016-11-23 19:50:02
【问题描述】:

我试图在用户触摸屏幕时移动 ImageView。我最初从 onTouchEvent 方法尝试了一个 while 循环,但这停止了我需要的一些后台进程。我现在有一个线程,用于控制我的游戏,该线程涉及基于名为“movementSpeed”的浮点数更新我的 ImageView 位置的调用,该浮点数由 Motion 事件确定。我相信这个问题与我的 ImageView 的初始化有关,但我已经尝试了许多其他问题的解决方案,这些问题似乎对我不起作用。 我考虑这是否是因为我从线程调用该方法并尝试在 UI 线程上运行该方法,但它仍然不起作用。但是,我发现如果我从我的触摸事件方法中调用 updateCharPos 会更新位置,尽管这会导致上述执行其他任务的困难。

活动 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" /

【问题讨论】:

  • 你确定 R.id.character 指向一个 ImageView 吗?你能发布你的 activity_play 布局吗?
  • @mWhitley 。从我的 activity_play 布局: 我相信这是正确的?此外,如果我从 onTouchEvent 方法调用 updateCharPos() 方法,它将更新位置,但会导致其他任务出现问题(上图)
  • @BFozard 你能发布你的布局代码(xml)吗?
  • @HenriqueMS 我现在已经添加了
  • @BFozard 我希望你能得到完整的 R.layout.activity_play 文件;)

标签: java android android-layout android-studio nullpointerexception


【解决方案1】:

编辑:

我没有注意到,但就像 @njzk2 在 cmets 中写的那样,您正在手动实例化一个扩展 Activity 的类,这解释了我在谈论的奇怪行为

private Play play = new Play();

Activity的子类不应该直接实例化,对here这个教程很好。

如果您在 Activity 中执行此操作,您可以:

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

你的实际通话流程是这样的:

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

这可能不是你想要的吧?

此外,如果您从另一个线程启动 Activity,则需要有一个 Context 以便启动新 Activity

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

mContext.startActivity(myIntent);

您在findViewById() 调用中直接获取 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);

【讨论】:

  • 非常感谢您的帮助!您和@njzk2 是正确的,它是由创建活动的新实例引起的。我最终在活动中使用了“public static Play play”和“play=this;”在 onCreate() 中。这使我可以通过“Play.play.updateCharPos(movementSpeed);”从线程中引用活动的现有实例。现在它可以完美运行了!再次感谢您的帮助!
  • @BFozard 只是要小心这个想法,通过保持对 Activity 的静态引用,您很可能会阻止它被垃圾收集,从而导致内存泄漏。我建议您根据 Android 模式使用活动。我很高兴能帮上忙;)
【解决方案2】:

假设R.id.character 实际上引用了一个有效视图,您可能希望将后台线程初始化thread=new GameThread(); 移动到onStart()onResume() 函数,以保证所需的UI 组件确实存在。

有关 Android 生命周期的更多详细信息,请参阅 this SO post

【讨论】:

  • 感谢@Boo 的建议。我检查了 R.id.character 确实引用了一个有效的视图并尝试了以下修正:'@Override' protected void onStart(){ super.onStart();线程=新游戏线程();线程.setRunning(true);线程.start(); } 但问题仍然存在。奇怪的是,如果我从触摸事件中调用 updateCharPos 它可以工作(尽管这会导致停止其他进程的原始问题)
猜你喜欢
  • 2015-07-12
相关资源
最近更新 更多