2016-02-08 2 views
1

Я пытаюсь написать приложение, напоминающее программу, которое может добавить виджеты на свой экран.Родительский взгляд не получает событие longpress

Я использую учебник Леонардо Фишера (http://leonardofischer.com/hosting-android-widgets-my-appwidgethost-tutorial/), который является большим.

Для того, чтобы удалить виджет, пользователь должен пропустить Widget, и в этом я столкнулся с некоторыми проблемами; некоторые Виджеты (WhatsApp Messagelist, Evernote List, например) позволяют прокручивать их. По какой-то причине, если вы перечисляете, 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/, вам нужно реализовать в родительском контейнере в onInterceptTouchEvent (mainlayout в моем случае), чтобы перехватывать и обрабатывать события до того, как они достигнут детей (виджеты в моем случае).

Я гугле вверх следующий код и пытался приспособиться к моим потребностям:

@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 виджету ...

... так что я думаю, мой вопрос в том, как отслеживать длинный клик внутри родительского представления.

Извините за длинный (и, казалось бы, основным) вопрос об обработке событий Android UI, но от того, что я гугла это кажется очень запутанная тема ..

ответ

0

Если вы действительно видите событие для запуска прокрутки соблюдается по событию для долгого щелчка, вы можете справиться с этим, установив флаг в классе обработки событий, который отслеживает, когда начинается и заканчивается прокрутка, и выберите игнорировать длительный щелчок, если выполняется прокрутка.

+0

Благодарим за идею, но, как вы можете видеть в моем фрагменте кода, я реализую 'setOnLongClickListener' и из того, что я мог бы изучить,' AppWidgetHostView' не имеет интерфейса для отслеживания событий прокрутки. Можете ли вы указать мне в правильном направлении? – Pbal

+0

Из вашего описания проблемы это звучало так, будто у вас есть прокручиваемый виджет. Если у вас нет прокручиваемого виджета, главный экран может интерпретировать медленное перетаскивание как долгое нажатие, и вы ничего не можете сделать, чтобы его изменить. –

+0

Вы правы; виджеты, такие как «Непрочитанный список WhatsApp» и список Evernote **, прокручивают виджеты **, и всякий раз, когда я просматриваю их, кажется, что «LongClick» уволен. Я проверил исходный код Google Launcher, и он использует почти ту же логику, что и моя (за исключением того, что они обрабатывают несколько экранов, а Launcher моего бедняка - это «только один экран» ...), и этот вид виджетов не генерирует это странное поведение ... Я уверен, что чего-то не хватает, но я не могу понять это. – Pbal

1

Так что все сделано ...! Я не уверен, что это изящное решение, но оно работает.

onInterceptTouchEvent позволяет родительскому представлению действовать на события до, они отправляются конечному отправителю. Обратите внимание, что он не срабатывает, если вы коснетесь фактического вида . Поэтому, если у вас есть макет с некоторым «пробелом» и некоторыми элементами, onInterceptTouchEventне срабатывает, если вы коснетесь «пробела» макета (в этом случае вам понадобится onTouchEvent макета).

Потому что мы можем по существу только отслеживать ACTION_UP, ACTION_MOVE и ACTION_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_UP или ACTION_CANCEL, код проверяет окончательную продолжительность. Если он превышает ViewConfiguration.getLongPressTimeout() (по умолчанию = 500 мс), он запускает действие.

Обратите внимание, что в моем случае мне нужна логическая переменная, чтобы предотвратить многократное срабатывание событий, так как я хотел использовать LongClick для удаления виджета. Вторая случайная стрельба, которая почти всегда произойдет, вызовет исключение нулевого указателя, поскольку виджет уже удален.

Я проверил его с несколькими виджетами (большими, маленькими, настраиваемыми, с прокруткой и без прокрутки и т. Д. И т. Д.) И я не нашел сбой.

Опять же, не уверен, что это изящное или «андроид-разумное» решение, но оно решило мою проблему.

Надеюсь, это поможет!

ССЫЛКА: Если вам нужна отличного статья на событии касания, пожалуйста, проверьте http://balpha.de/2013/07/android-development-what-i-wish-i-had-known-earlier/. Это дало мне правильное «настроение» для решения моей проблемы.

Смежные вопросы