onUserInteraction не работает в DialogPreference
Ожидается, что onUserInteraction
вызывается для любого взаимодействия с пользователем. он отлично работает в PreferenceActivity
, Тем не менее, когда DialogPreference
это всплывающее окно, onUserInteraction
больше не вызывается, даже если есть взаимодействие с пользователем, например, событие касания.
Кажется, что DialogPreference
это не единственный случай. Всякий раз, когда Dialog
показано, он не сообщает о взаимодействии пользователя с деятельностью.
Но что я могу сделать, если мне это действительно нужно. Благодарю вас.
4 ответа
Насколько я знаю, onUserInteraction()
просто не вызывается, когда пользователь взаимодействует с диалоговым окном (даже началось с Activity
в котором вы отслеживаете взаимодействия).
Я знаю два решения:
Подкласс
Dialog
/DialogPreference
класс и переопределениеdispatchTouchEvent()
,Воплощать в жизнь
Window.Callback
интерфейс и установить его какDialog
Окно обратного вызова с помощью:dialog.getWindow().setCallback(callbackImplementation);
Примечание: эта реализация должна обрабатывать все полученные события, вызывая соответствующие методы диалога или обрабатывать события по-своему (например, вручную
onUserInteraction()
).
редактировать
У вас есть несколько способов получить Activity
из обычая PreferenceDialog
пример.
Вызов
DialogPreference.getPreferenceManager()
метод, который возвращаетPreferenceManager
, Оно имеетgetActivity()
метод, но это пакетно-частный, так что вам придется поставить свой заказDialogPreference
вandroid.preference
пакет для доступа к нему.в
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)
}
}
}
}