onUserInteraction не работает в DialogPreference

Ожидается, что onUserInteraction вызывается для любого взаимодействия с пользователем. он отлично работает в PreferenceActivity, Тем не менее, когда DialogPreference это всплывающее окно, onUserInteraction больше не вызывается, даже если есть взаимодействие с пользователем, например, событие касания.

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

Но что я могу сделать, если мне это действительно нужно. Благодарю вас.

4 ответа

Решение

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

Я знаю два решения:

  • Подкласс Dialog/DialogPreference класс и переопределение dispatchTouchEvent(),

  • Воплощать в жизнь Window.Callback интерфейс и установить его как DialogОкно обратного вызова с помощью:

    dialog.getWindow().setCallback(callbackImplementation);
    

    Примечание: эта реализация должна обрабатывать все полученные события, вызывая соответствующие методы диалога или обрабатывать события по-своему (например, вручную onUserInteraction()).

редактировать

У вас есть несколько способов получить Activity из обычая PreferenceDialog пример.

  1. Вызов DialogPreference.getPreferenceManager() метод, который возвращает PreferenceManager, Оно имеет getActivity() метод, но это пакетно-частный, так что вам придется поставить свой заказ DialogPreference в android.preference пакет для доступа к нему.

  2. в PreferenceActivity.onCreate(), после раздувания предпочтений, используйте findPreference() найти свой заказ DialogPreference по ключу. Затем приведите его в свой пользовательский класс и установите активность на this через аксессор.

Я бы пошел со вторым вариантом.

Вот полное решение для DialogFragment, который вызывает активность onUserInteraction() на ощупь и сохраняет поведение обратного вызова по умолчанию:

public abstract class BaseDialogFragment extends DialogFragment {

    @Override
    public void onActivityCreated(final Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        final Window window = getDialog().getWindow();
        if (window != null) {
            window.setCallback(new UserInteractionAwareCallback(window.getCallback(), getActivity()));
        }
    }
}

А вот и сам Callback:

public class UserInteractionAwareCallback implements Window.Callback {

    private final Window.Callback originalCallback;
    private final Activity activity;

    public UserInteractionAwareCallback(final Window.Callback originalCallback, final Activity activity) {
        this.originalCallback = originalCallback;
        this.activity = activity;
    }

    @Override
    public boolean dispatchKeyEvent(final KeyEvent event) {
        return originalCallback.dispatchKeyEvent(event);
    }

    @Override
    public boolean dispatchKeyShortcutEvent(final KeyEvent event) {
        return originalCallback.dispatchKeyShortcutEvent(event);
    }

    @Override
    public boolean dispatchTouchEvent(final MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
            case MotionEvent.ACTION_UP:
                if (activity != null) {
                    activity.onUserInteraction();
                }
                break;
            default:
        }
        return originalCallback.dispatchTouchEvent(event);
    }

    @Override
    public boolean dispatchTrackballEvent(final MotionEvent event) {
        return originalCallback.dispatchTrackballEvent(event);
    }

    @Override
    public boolean dispatchGenericMotionEvent(final MotionEvent event) {
        return originalCallback.dispatchGenericMotionEvent(event);
    }

    @Override
    public boolean dispatchPopulateAccessibilityEvent(final AccessibilityEvent event) {
        return originalCallback.dispatchPopulateAccessibilityEvent(event);
    }

    @Nullable
    @Override
    public View onCreatePanelView(final int featureId) {
        return originalCallback.onCreatePanelView(featureId);
    }

    @Override
    public boolean onCreatePanelMenu(final int featureId, final Menu menu) {
        return originalCallback.onCreatePanelMenu(featureId, menu);
    }

    @Override
    public boolean onPreparePanel(final int featureId, final View view, final Menu menu) {
        return originalCallback.onPreparePanel(featureId, view, menu);
    }

    @Override
    public boolean onMenuOpened(final int featureId, final Menu menu) {
        return originalCallback.onMenuOpened(featureId, menu);
    }

    @Override
    public boolean onMenuItemSelected(final int featureId, final MenuItem item) {
        return originalCallback.onMenuItemSelected(featureId, item);
    }

    @Override
    public void onWindowAttributesChanged(final WindowManager.LayoutParams attrs) {
        originalCallback.onWindowAttributesChanged(attrs);
    }

    @Override
    public void onContentChanged() {
        originalCallback.onContentChanged();
    }

    @Override
    public void onWindowFocusChanged(final boolean hasFocus) {
        originalCallback.onWindowFocusChanged(hasFocus);
    }

    @Override
    public void onAttachedToWindow() {
        originalCallback.onAttachedToWindow();
    }

    @Override
    public void onDetachedFromWindow() {
        originalCallback.onDetachedFromWindow();
    }

    @Override
    public void onPanelClosed(final int featureId, final Menu menu) {
        originalCallback.onPanelClosed(featureId, menu);
    }

    @Override
    public boolean onSearchRequested() {
        return originalCallback.onSearchRequested();
    }

    @TargetApi(Build.VERSION_CODES.M)
    @Override
    public boolean onSearchRequested(final SearchEvent searchEvent) {
        return originalCallback.onSearchRequested(searchEvent);
    }

    @Nullable
    @Override
    public ActionMode onWindowStartingActionMode(final ActionMode.Callback callback) {
        return originalCallback.onWindowStartingActionMode(callback);
    }

    @TargetApi(Build.VERSION_CODES.M)
    @Nullable
    @Override
    public ActionMode onWindowStartingActionMode(final ActionMode.Callback callback, final int type) {
        return originalCallback.onWindowStartingActionMode(callback, type);
    }

    @Override
    public void onActionModeStarted(final ActionMode mode) {
        originalCallback.onActionModeStarted(mode);
    }

    @Override
    public void onActionModeFinished(final ActionMode mode) {
        originalCallback.onActionModeFinished(mode);
    }
}

Вот более автономная и более полная реализация Kotlin:

      /**
 * Sets up the receiver's [window][Dialog.getWindow] to call [Activity.onUserInteraction]
 * at appropriate times, mirroring the calls made in [Activity] itself.
 * This method should be called immediately after [Dialog.show].
 */
fun Dialog.reportUserInteraction() {
  window?.let { window ->
    val activity = window.decorView.activity

    val wrappedCallback = window.callback
    window.callback = object : Window.Callback by wrappedCallback {
      override fun dispatchGenericMotionEvent(event: MotionEvent): Boolean {
        activity.onUserInteraction()
        return wrappedCallback.dispatchGenericMotionEvent(event)
      }

      override fun dispatchKeyEvent(event: KeyEvent): Boolean {
        activity.onUserInteraction()
        return wrappedCallback.dispatchKeyEvent(event)
      }

      override fun dispatchKeyShortcutEvent(event: KeyEvent): Boolean {
        activity.onUserInteraction()
        return wrappedCallback.dispatchKeyShortcutEvent(event)
      }

      override fun dispatchTouchEvent(event: MotionEvent): Boolean {
        if (event.action == ACTION_DOWN) activity.onUserInteraction()
        return wrappedCallback.dispatchTouchEvent(event)
      }

      override fun dispatchTrackballEvent(event: MotionEvent): Boolean {
        activity.onUserInteraction()
        return wrappedCallback.dispatchTrackballEvent(event)
      }
    }
  }
}

Основывается на:

      val View.activity: Activity
  get() = context.activityOrNull!!

val Context.activityOrNull: Activity?
  get() {
    var context = this
    while (true) {
      if (context is Application) {
        return null
      }
      if (context is Activity) {
        return context
      }
      if (context is ContextWrapper) {
        val baseContext = context.baseContext
        // Prevent Stack Overflow.
        if (baseContext === this) {
          return null
        }
        context = baseContext
      } else {
        return null
      }
    }
  }

Это сработало для меня, версия Kotlin:

В вашем диалоге:

      override fun onStart() {
    super.onStart()
    UserInteractionInterceptor.wrapWindowCallback(dialog.window, activity)
}

и класс UserInteractionInterceptor:

      object UserInteractionInterceptor {
fun wrapWindowCallback(window: Window, activity: FragmentActivity?) {
    val originalCallback: Window.Callback = window.callback

    window.callback = object : Window.Callback {
        override fun dispatchKeyEvent(event: KeyEvent?): Boolean {
            return originalCallback.dispatchKeyEvent(event)
        }

        override fun dispatchKeyShortcutEvent(event: KeyEvent?): Boolean {
            return originalCallback.dispatchKeyShortcutEvent(event)
        }

        override fun dispatchTouchEvent(event: MotionEvent): Boolean {
            if (event.action == MotionEvent.ACTION_DOWN) {
                activity?.onUserInteraction()
            }
            return originalCallback.dispatchTouchEvent(event)
        }

        override fun dispatchTrackballEvent(event: MotionEvent?): Boolean {
            return originalCallback.dispatchTrackballEvent(event)
        }

        override fun dispatchGenericMotionEvent(event: MotionEvent?): Boolean {
            return originalCallback.dispatchGenericMotionEvent(event)
        }

        override fun dispatchPopulateAccessibilityEvent(event: AccessibilityEvent?): Boolean {
            return originalCallback.dispatchPopulateAccessibilityEvent(event)
        }

        @Nullable
        override fun onCreatePanelView(featureId: Int): View? {
            return originalCallback.onCreatePanelView(featureId)
        }

        override fun onCreatePanelMenu(featureId: Int, p1: Menu): Boolean {
            return originalCallback.onCreatePanelMenu(featureId, p1)
        }

        override fun onPreparePanel(featureId: Int, view: View?, p2: Menu): Boolean {
            return originalCallback.onPreparePanel(featureId, view, p2)
        }

        override fun onMenuOpened(featureId: Int, p1: Menu): Boolean {
            return originalCallback.onMenuOpened(featureId, p1)
        }

        override fun onMenuItemSelected(featureId: Int, p1: MenuItem): Boolean {
            return originalCallback.onMenuItemSelected(featureId, p1)
        }

        override fun onWindowAttributesChanged(attrs: WindowManager.LayoutParams?) {
            originalCallback.onWindowAttributesChanged(attrs)
        }

        override fun onContentChanged() {
            originalCallback.onContentChanged()
        }

        override fun onWindowFocusChanged(hasFocus: Boolean) {
            originalCallback.onWindowFocusChanged(hasFocus)
        }

        override fun onAttachedToWindow() {
            originalCallback.onAttachedToWindow()
        }

        override fun onDetachedFromWindow() {
            originalCallback.onDetachedFromWindow()
        }

        override fun onPanelClosed(featureId: Int, p1: Menu) {
            originalCallback.onPanelClosed(featureId, p1)
        }

        override fun onSearchRequested(): Boolean {
            return originalCallback.onSearchRequested()
        }

        @TargetApi(Build.VERSION_CODES.M)
        override fun onSearchRequested(searchEvent: SearchEvent?): Boolean {
            return originalCallback.onSearchRequested(searchEvent)
        }

        @Nullable
        override fun onWindowStartingActionMode(callback: ActionMode.Callback?): ActionMode? {
            return originalCallback.onWindowStartingActionMode(callback)
        }

        @TargetApi(Build.VERSION_CODES.M)
        @Nullable
        override fun onWindowStartingActionMode(callback: ActionMode.Callback?, type: Int): ActionMode? {
            return originalCallback.onWindowStartingActionMode(callback, type)
        }

        override fun onActionModeStarted(mode: ActionMode?) {
            originalCallback.onActionModeStarted(mode)
        }

        override fun onActionModeFinished(mode: ActionMode?) {
            originalCallback.onActionModeFinished(mode)
        }
    }
}

}

Другие вопросы по тегам