2014-10-29 64 views
0

在運行Android 2.2的設備上,我想要檢測用戶何時按下屏幕一段時間。想象一下,發送一個莫爾斯電碼消息,短小點(點)和長按(短劃線)。我想在用戶舉起手指時立即響應短按,並且在500毫秒後(例如)持續按下她的手指後再按一次。處理可取消超時的典型Java技術是什麼?

我已經看過FutureTaskScheduledExecutorService,但這些看起來像這個實施過度殺毒。或者,也許我只是冷靜地處理線程,並查看處理它們所需的所有代碼。

這裏的簡化僞代碼因爲我怎麼做這樣的事情在其他語言:

public boolean onTouch(MotionEvent event) { 
    if (event.getAction() == MotionEvent.ACTION_DOWN) { 
    timer = createObjectToCallback(callbackMethod, 500); // milliseconds 

    } else if (event.getAction() == MotionEvent.ACTION_UP) { 
    if (timer still exists) { 
     timer.kill(); 
     // Do the short press thing 
    } else { 
     // Do nothing. It already happened when callbackMethod was triggered 
    } 
    } 
} 

public void callbackMethod() { 
    // Do the long press thing. The timer has already auto-destructed. 
} 

什麼簡單的方法是有在Java中這樣做的?響應答案

==編輯從@zapl ==

編寫代碼的工作原理是一回事。瞭解它如何工作是另一回事。

如果我理解正確,更新UI的線程已經在循環中運行。讓我們設想一個非常簡單的例子。

Main活動創建一個黑色畫布,幷包含一個onTouch方法。當它開始時,它調用setOnTouchListener。主線程現在不斷偵聽來自屏幕的輸入。如果用戶觸摸屏幕的方式已更改,則會調用onTouch方法以及有關更改的信息。

假設onTouch方法在觸摸點周圍繪製一個綠色圓圈。這個圓圈是使用屬於主線程的循環繪製的。繪圖完成後,主線程開始檢查屏幕上的新更改。如果沒有更改,則不會再調用onTouch,並且綠點不會移動。

當用戶擡起手指時,屏幕將更改的信息提供給主線程,並且onTouch方法中的代碼將擦除點。

Create interface 
Has the screen detected a change? No: loop 
Has the screen detected a change? No: loop 
... 
Has the screen detected a change? Yes: draw a green dot; loop 
Has the screen detected a change? No: loop. 
... 
Has the screen detected a change? Yes: new position => redraw the green dot; loop 
... 
Has the screen detected a change? Yes: not touching => remove dot; loop 
Has the screen detected a change? ... 

假設我希望點在用戶的手指移動至少500毫秒時變成紅色。沒有任何舉動意味着不回撥到onTouch。所以我可以設置一個Handler,它將自己添加到主線程的循環中。主線程現在在其循環中有兩個動作。

Create interface 
Has the screen detected a change? No: loop 
Has the screen detected a change? No: loop 
... 
Has the screen detected a change? Yes: a touch; draw a green dot; add Handler; loop 
Has the screen detected a change? No; 
    Is it time for Handler to trigger? No: loop. 
... 
Has the screen detected a change? No; 
    Is it time for Handler to trigger? Yes: change dot color to red; remove Handler; loop. 
Has the screen detected a change? No: loop. 
... 
Has the screen detected a change? Yes: not touching => remove dot; loop 
Has the screen detected a change? ... 

任何由Handler執行的代碼將阻塞主線程,直到它完成。
這是Handler做什麼的準確描述?

+0

看看有沒有這些幫助:http://stackoverflow.com/questions/3553163/android-long-touch-event,http://stackoverflow.com/questions/4324362/detect-touch-press-vs-長按vs運動,http://developer.android.com/training/gestures/detector.html – 2014-10-29 01:51:30

+0

謝謝。我希望能夠觸摸獨立的東西,以便在其他情況下重複使用該技術。如果我理解正確,onLongPress會在用戶將其手指從屏幕上移開時觸發,這不是我想要的。我的情況有點複雜,因爲它也需要允許用戶移動她的手指,並且具有單獨的含義。 – 2014-10-29 02:00:17

+0

看來,這就是我正在尋找的:http://stackoverflow.com/a/11679788/1927589 – 2014-10-29 02:06:53

回答

1

當你實際上不需要並行時使用線程確實是過度殺傷,因爲它增加了它自己的一套問題。你需要的是時間表將來運行的代碼,但在同一個線程上。 Android的Handler可以做到這一點。您可以安排RunnableMessage到達。還有派生的CountDownTimer用於更簡單的週期性事件調度。

但這可能不需要在這種情況下,因爲有GestureDetector

它配備了Android,可以區分幾種類型的單擊,長按和雙擊。它也與系統的其他部分保持一致。你可能會想要使用它。

更多有關http://developer.android.com/training/gestures/detector.html

如果你真的想實現自己的或只是看如何使用Handler一個例子,看看GestureDetector's source。它充滿了你所發佈的代碼(mHandler.hasMessages,mHandler.removeMessages,mHandler.sendEmptyMessageDelayed)。

注意:Handler不是標準的Java類,因爲在同一個線程中調度事件需要一個線程爲基於消息隊列,並且沒有標準的解決方案。這也取決於UI框架是如何對框架進行線程安全的。如果你嘗試從某個後臺線程修改ui,Android會嘗試拋出異常。例如,同樣的問題應該適用於Swing的Event Dispatch Thread(SwingUtilities.invokeLater)。


編輯:試圖解釋UI線程&處理程序:

由處理程序將阻止主線程,直到它已完成執行任何代碼。

正確。 Android的主線程工作非常簡化這樣的:

public BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(); 
public void loop() { 
    while (true) { 
     Runnable currentTask = queue.take(); // blocks until something happens 
     currentTask.run(); 
     // repeat. 
    } 
} 
public void enqueue(Runnable runnable) { 
    queue.put(runnable); 
} 

public static void main(String[] args) { 
    startThreadsThatReceiveSystemEvents(); 
    enqueue(new Runnable() { 
     @Override 
     public void run() { 
      Activity activity = createStartActivity(); 
      activity.onCreate(); 
      activity.onResume(); 
     } 
    }); 
    loop(); // fun fact: an android app will never return from here 
      // it's process is simply killed by the system 
} 

真實的世界等同於蹤跡經常發現很遠了:

E/AndroidRuntime(20941): at android.os.Looper.loop(Looper.java:130) 
E/AndroidRuntime(20941): at android.app.ActivityThread.main(ActivityThread.jav a:3691) 

一切,Android爲在啓動/停止Activites,繪製的條款屏幕上,..是某些東西進入隊列的結果,以便運行循環在某個時刻對其進行評估。 A Handler使用非常相同的隊列。而且你入隊的所有東西都會與其他所有已經發生的事情交織在一起。一個線程不能做與它自身平行的事情,所以它都是順序的。這就是處理程序任務阻止其他任務的原因。

你的觸摸事件的例子基本上是正確的,它只是沒有積極地看着觸摸屏。它通過與系統的連接獲得通知。基本上有另一個線程(如果你已經查看了線程列表,Binder線程)偵聽來自系統的消息,一旦它們到達,所有此線程需要做的就是將它們排隊到主循環。如果它正在等待,它可以自動喚醒環路。

主線程隊列實際上不是簡單的BlockingQueue,因爲它也需要支持預定事件。這是一個名爲MessageQueue的Android特定實現,部分用本機代碼實現。 loop方法也在它自己的類中(Looper)。隊列不直接使用Runnable,它實際上是一個Message s的隊列 - 它可以包含Runnable(在隱藏字段中),並且在發現消息中的Runnable時的運行循環就會像上面的示例代碼一樣執行它。

每個Handler綁定到一個Looper/MessageQueue組合(並因此爲1個線程)。 Handler.post/sendMessage做構建一個適當的消息& enquueing它的骯髒的工作。該消息有一個鏈接回你的處理程序,所以循環知道哪個處理程序調用的方法handleMessage

除了使用已經存在的主線程循環/隊列外,您還可以自由創建其他基於隊列的線程和處理程序。 https://stackoverflow.com/a/13369215/995891包含一個小例子。