2013-12-22 27 views
-1

我想創建一個應用程序,當你觸摸屏幕時,一個雪花出現在屏幕的頂部,並慢慢下降。我將每個雪花添加到數組列表中,以便我可以使每個雪花落下。這裏是我的代碼:從錯誤的線程異常調用Android

Runnable runable = new Runnable(){ 
    @Override 
    public void run(){ 
     while(true){ 
      letTheSnowFall(); 
     } 
    } 
}; 

public void letTheSnowFall(){ 
    for(int i = 0; i < snowArray.size(); i++){ 
     snowArray.get(i).setY(snowArray.get(i).getY() + 0.01f); 
    } 
} 

和我啓動線程在onCreate()方法:

protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 

    layout = (RelativeLayout) findViewById(R.id.activity_main); 

    setContentView(layout); 
    layout.setOnClickListener(listener); 

    Thread myThread = new Thread(runable); 
    myThread.start(); 
} 
+0

如何初始化'snowArray'?你確定它可以從'myThread'中訪問嗎? – NitroNbg

+2

你在一個線程上調用你的繪圖(?)代碼,這不是UI線程,並且可能試圖將雪花添加到窗口右側?嘗試將'letTheSnowFall()'發佈到'Activity's'' Handler'或者使用'runOnUiThread'方法。 – Darwind

回答

1

首先,你不能從後臺線程更新UI。

其次,一個沒有延遲的無限後臺線程是非常糟糕的代碼。

三,請不要在onCreate()內撥打setContentView()兩次。你不需要第二個(setContentView(layout))。

最輕的方法是使用postDelayed()(可用於任何View(如layout))來安排在延遲後取得控制權。你可以通過調用removeCallbacks()取消postDelayed()工作,通過在同一Runnable爲您postDelayed()使用:

/*** 
    Copyright (c) 2012 CommonsWare, LLC 
    Licensed under the Apache License, Version 2.0 (the "License"); you may not 
    use this file except in compliance with the License. You may obtain a copy 
    of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required 
    by applicable law or agreed to in writing, software distributed under the 
    License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 
    OF ANY KIND, either express or implied. See the License for the specific 
    language governing permissions and limitations under the License. 

    From _The Busy Coder's Guide to Android Development_ 
    http://commonsware.com/Android 
*/ 

package com.commonsware.android.post; 

import android.app.Activity; 
import android.os.Bundle; 
import android.view.View; 
import android.widget.Toast; 

public class PostDelayedDemo extends Activity implements Runnable { 
    private static final int PERIOD=5000; 
    private View root=null; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.main); 
    root=findViewById(android.R.id.content); 
    } 

    @Override 
    public void onResume() { 
    super.onResume(); 

    run(); 
    } 

    @Override 
    public void onPause() { 
    root.removeCallbacks(this); 

    super.onPause(); 
    } 

    @Override 
    public void run() { 
    Toast.makeText(PostDelayedDemo.this, "Who-hoo!", Toast.LENGTH_SHORT) 
     .show(); 
    root.postDelayed(this, PERIOD); 
    } 
} 

(從this sample project代碼)

在你的情況,run()將更新您的雪花,而不是呈現出的Toast

+0

那麼在我的情況下,根源會是什麼,它會一樣嗎? – Cj1m

+0

@ Cj1m:它可以是一樣的,或者其他'View',比如你的'layout'。 – CommonsWare

0

如果我理解正確的雪花是意見。所以snowArray的類型爲ArrayList<SubtypeOfView>。如果是這樣,那麼拋出的異常是因爲你從主線以外的線程調用View.setY()方法。幾乎所有與Android GUI框架相關的方法都必須從主線程(執行回調如Activity.onCreate())的線程中調用。

+0

他們實際上是ImageViews,但是是的 – Cj1m

+0

在任何情況下,你的方法不是解決這個問題的最佳方法。如果你只想使用Android庫,那麼我會建議實現一個自定義視圖(View的子類),它將包含渲染代碼。否則,你應該使用更高級的圖形庫。 Android庫太有限了。這種庫的一個例子是LibGDX,它實際上是一個遊戲引擎。 – GareginSargsyan

0

我猜你的snowArray包含一堆View(或它的許多子類之一)的實例嗎?在這種情況下,您試圖從非GUI線程修改GUI組件,這是不允許的,因此拋出一個CalledFromWrongThreadException

無論您需要修改UI,「簡單」解決方案都會致電Activity.runOnUiThread。如果您已經在單獨的線程上完成了一些處理並且現在已準備好在GUI上顯示結果,那麼這很有用。

但是,您想要做很多GUI更新。更糟糕的是,因爲你在無限循環中進行更新:while(true) { letTheSnowFall(); }。這東西真的是無限:它永遠不會停止,你的積雪將永遠不斷下降,Y座標迅速遞增並最終溢出。你不會看到這種情況發生,因爲一旦GUI更新,你立即再次更新它:有沒有延遲

雖然你想做的事情要簡單得多:你只是想讓雪落下來。你想動畫你的雪花意見從他們的當前位置開始,並向下結束一段距離。 Android有a whole package for animations以及一些very good guides

+0

我計劃在移動屏幕時除去積雪,並稍加延遲以使其「無限」。 – Cj1m

+0

不過,你應該看看動畫。它可以讓你的生活更輕鬆,因爲你可以依靠框架來管理時間和補間。您還可以使用更高級的功能,如加速器或動畫集。此外,Android中的大部分動畫都可以通過GPU完成,從而獲得更流暢的體驗。 –

+0

它會導致我不得不重寫大量的應用程序嗎? – Cj1m

1

要更新視圖,您需要從主線程更新視圖。顯然,你做錯了。您可以將信息發送到主線程並進行更新。也許,你可以嘗試使用Android構建線程(AsyncTasks)更新視圖,在doInBackGround()上計算結果,並在onPostExecute()方法上發佈結果。

相關問題