2016-02-08 54 views
1

我正在嘗試編寫一個類似於啓動程序的應用程序,它可以將Widgets添加到其屏幕。父視圖無法獲得longpress事件

我正在使用Leonardo Fischer的教程(http://leonardofischer.com/hosting-android-widgets-my-appwidgethost-tutorial/),這很棒。

爲了移除一個小部件,用戶應該長按這個小部件,這就是我遇到麻煩的地方;一些小部件(例如WhatsApp Messagelist,Evernote列表)允許您滾動它們。出於某種原因,如果您滾動,Android的觸發一個LongClick事件,不當取出小部件...

我的代碼: (創建窗口小部件並設置LongClickListener)

public void createWidget(Intent data) { 
    Bundle extras = data.getExtras(); 
    int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); 
    AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId); 

    final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo); 
    hostView.setAppWidget(appWidgetId, appWidgetInfo); 

    // relative layout 
    //RelativeLayout.LayoutParams lp = new RelativeLayout() 
    //mainlayout.addView(hostView, lp); 
    mainlayout.addView(hostView); 

    // [COMMENTED OUT] hostView.setOnLongClickListener(new AppWidgetLongClickListener(hostView)); 

} 

UPDATE

無數小時後,我想我部分瞭解發生了什麼,但我仍然無法得到正確的行爲。

http://balpha.de/2013/07/android-development-what-i-wish-i-had-known-earlier/,你需要實現在父容器的onInterceptTouchEventmainlayout在我的情況),攔截和處理事件,他們到達兒童(部件在我的情況)前。

所以我用Google搜索了下面的代碼,並試圖適應我的需求:

@Override 
public boolean onInterceptTouchEvent(MotionEvent ev) { 
    // Consume any touch events for ourselves after longpress is triggered 
    //Log.i(TAG,"OnIntercept: "+ev.toString()); 
    if (mHasPerformedLongPress) { 
     Log.i(TAG,"Longpress OK!: "+ev.toString()); 
     mHasPerformedLongPress = false; 
     return true; 
    } 

    // Watch for longpress events at this level to make sure 
    // users can always pick up this widget 
    switch (ev.getAction()) { 
     case MotionEvent.ACTION_DOWN: { 
      postCheckForLongClick(); 
      break; 
     } 

     case MotionEvent.ACTION_UP: 
     case MotionEvent.ACTION_CANCEL: 
      mHasPerformedLongPress = false; 
      if (mPendingCheckForLongPress != null) { 
       removeCallbacks(mPendingCheckForLongPress); 
      } 
      break; 
    } 

    // Otherwise continue letting touch events fall through to children 
    return false; 
} 

class CheckForLongPress implements Runnable { 
    private int mOriginalWindowAttachCount; 

    public void run() { 
     Log.i(TAG,"Inside RUN"); 
     if (getParent()!= null) { 
      Log.i(TAG,"getParent:"+getParent().toString()); 
     } 
     if ((getParent() != null) && hasWindowFocus() 
       && (mOriginalWindowAttachCount == getWindowAttachCount()) 
       && !mHasPerformedLongPress) { 
      if (performLongClick()) { // <-- DOESN'T WORK :(
       mHasPerformedLongPress = true; 
      } 
     } 
    } 

    public void rememberWindowAttachCount() { 
     mOriginalWindowAttachCount = getWindowAttachCount(); 
    } 
} 

private void postCheckForLongClick() { 
    mHasPerformedLongPress = false; 

    if (mPendingCheckForLongPress == null) { 
     mPendingCheckForLongPress = new CheckForLongPress(); 
    } 
    mPendingCheckForLongPress.rememberWindowAttachCount(); 
    postDelayed(mPendingCheckForLongPress, ViewConfiguration.getLongPressTimeout()); 
} 

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

    mHasPerformedLongPress = false; 
    if (mPendingCheckForLongPress != null) { 
     removeCallbacks(mPendingCheckForLongPress); 
    } 
} 

上面的代碼不會攔截觸摸事件,當我點擊一個窗口小部件,但它的邏輯似乎旨在攔截(並直接用於進一步的治療)longclicks小部件。我真正需要的是在父視圖內截取一個longclick。

訣竅似乎在於在if (performLongClick()),其中,據我可以得到,觸發一個事件LongClick到小部件 ...

...所以我想我現在的問題是如何跟蹤父視圖內的一個長按鈕。

對不起,在處理機器人UI事件長(和看似基本的)的問題,但是從我一派這似乎是一個很令人費解的話題..

回答

0

如果你真的看到了開始滾動的事件被跟蹤通過長時間點擊的事件,您可以通過在事件處理類中設置一個標誌來處理滾動開始和結束時的標誌,並且如果正在進行滾動,則選擇忽略長按。

+0

感謝這個想法,但正如你可以在我的代碼片段中看到的,我正在實現'setOnLongClickListener',並且從我可以看到的情況來看,'AppWidgetHostView'沒有用於跟蹤滾動事件的接口。你能在這裏指出我正確的方向嗎? – Pbal

+0

從你的問題描述來看,它聽起來像你有一個滾動小部件。如果您沒有滾動窗口小部件,則主屏幕可能會將緩慢拖動解釋爲長按,並且您無法做出任何改變。 –

+0

你是對的;小部件,如WhatsApp的未讀列表和印象筆記列表**正在滾動窗口小部件**,並且每當我滾動它們時,似乎都會觸發一個LongClick。我檢查過Google Launcher的源代碼,它使用的邏輯幾乎和我的一樣(除了他們處理多個屏幕,而我的窮人的Launcher是一個「單屏幕」......),並且這種類型的小部件不會生成這種奇怪的行爲......我相信我錯過了一些東西,但我無法弄清楚。 – Pbal

1

就這樣做了!我不確定這是否是一個優雅的解決方案,但它的工作原理。

onInterceptTouchEvent允許父視圖對事件採取行動,然後將它們發送給最終發件人。請注意,如果您觸摸實際視圖它不會觸發。所以如果你有一個帶有「空白區域」和一些元素的佈局,如果你觸摸佈局的「空白區域」(在這種情況下你需要佈局的onTouchEvent),onInterceptTouchEvent不會觸發

因爲我們基本上可以只跟蹤ACTION_UPACTION_MOVEACTION_DOWN事件,我們需要時間ACTION_DOWN/ACTION_UP對事件的持續時間來確定這是否是一個longclick或沒有,所以我做了什麼如下:

public class time_counter { 
    private long begin_time; 
    private long end_time; 


    public time_counter(long i, long f) { 
     this.begin_time = i; 
     this.end_time = f; 
    } 

    public long getDuration() { 
     return (this.end_time - this.begin_time); 
    } 

} 

@Override 
public boolean onInterceptTouchEvent(MotionEvent ev) { 

    // Consume any touch events for ourselves after longpress is triggered 

    // Watch for longpress events at this level to make sure 
    // users can always pick up this widget 
    switch (ev.getAction()) { 
     case MotionEvent.ACTION_DOWN: { 
      cnt = new time_counter(ev.getEventTime(), (long)0); 
      break; 
     } 

     case MotionEvent.ACTION_MOVE: { 
      if (cnt != null) { 
       cnt.end_time = ev.getEventTime(); 
      } 
      break; 
     } 
     case MotionEvent.ACTION_UP: 
     case MotionEvent.ACTION_CANCEL: 
      if (cnt != null) { 
       cnt.end_time = ev.getEventTime(); 
      } 
      Log.i(TAG, "DURATION: " + cnt.getDuration()); 
      if (cnt.getDuration() > ViewConfiguration.getLongPressTimeout()) { 
       Log.i(TAG, "it's a longpress: " + this.toString()); 
       if (processClick) { 
        processClick = false; 
        this.doRemoveWidget(); 
       } 
       cancelLongPress(); 
       return true; 
      } 
      break; 
    } 

    // Otherwise continue letting touch events fall through to children 
    return false; 
} 

每當Android發送ACTION_DOWN事件時,代碼將使用簡單的「時間計數器」對象開始跟蹤其持續時間。計數器的結束時間戳在整個ACTION_MOVE事件中不斷更新,當Android發送ACTION_UPACTION_CANCEL時,代碼將檢查最後的持續時間。如果它超過ViewConfiguration.getLongPressTimeout()(默認= 500毫秒),則觸發該操作。

請注意,在我的情況下,我需要一個布爾變量來防止多個事件觸發,因爲我想使用LongClick來移除一個小部件。第二次意外觸發,幾乎總是會發生,會觸發空指針異常,因爲該小部件已被刪除。

我測試了幾個小部件(大,小,可配置,有和沒有滾動視圖等,等等),我還沒有發現一個小故障。

再次,不知道這是一個優雅的或「Android智能」的解決方案,但它解決了我的問題。

希望這會有所幫助!

參考文獻: 如果您需要優秀的有關觸摸事件的文章,請檢查http://balpha.de/2013/07/android-development-what-i-wish-i-had-known-earlier/。它給了我正確的「思維框架」來解決我的問題。

相關問題