Запустить onTouch в диалоге из onLongНажмите во фрагменте - PullRequest
0 голосов
/ 25 января 2019

У меня есть приложение с фидом изображений (в стиле Instagram). Я пытаюсь показать предварительный просмотр изображения с помощью длинного клика по изображению.

Основная идея - показать изображение в диалоговом окне, когда пользователь сделал длинный клик, затем изменить масштаб, когда пользователь перемещает палец вниз / вверх, и закрыть предварительный просмотр, когда палец отпущен.

Для того, чтобы заархивировать, что у меня есть onLongClick в адаптере фрагмента, как это:

holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {

        @Override
        public boolean onLongClick(View view) {

            listener.onLongClick(item.getId());

            return false;
        }
    });

Затем фрагмент реализует слушатель и вызывает диалог следующим образом:

@Override
public void onLongClick(long itemId) {

    FullscreenPhotoPreviewDialog dialog = FullscreenPhotoPreviewDialog.newInstance(itemId);

    dialog.show(getActivity().getSupportFragmentManager(), "FullscreenPhotoPreviewDialog");
}

Наконец, в Dialog реализована вся логика OnTouch, позволяющая пользователю изменять масштаб изображения, не отпуская палец.

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {

    super.onViewCreated(view, savedInstanceState);

    view.setOnTouchListener(new View.OnTouchListener() {

        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {

            switch(motionEvent.getAction()) {

                case MotionEvent.ACTION_DOWN:

                    break;

                case MotionEvent.ACTION_MOVE:

                    float scale = 0;

                    if (motionEvent.getHistorySize() > 0)
                        scale = ((motionEvent.getY() > motionEvent.getHistoricalY(motionEvent.getHistorySize() - 1)) ? 0.1f : -0.1f);

                    FullscreenPhotoPreviewDialog.this.applyScale(scale);

                    break;

                case MotionEvent.ACTION_UP:

                    FullscreenPhotoPreviewDialog.this.dismiss();

                    break;
            }

            return true;
        }
    });
}

Поток открытия диалога с длинным щелчком работает нормально. Проблема в том, что с onTouch. Длинный щелчок не отправляет событие ACTION_DOWN в onTouch. Поэтому мне нужно потянуть вверх и снова потянуть вниз, чтобы запустить сенсорный ввод.

Есть ли способ сделать это? Чтобы автоматически вызвать ACTION_DOWN из длинного нажатия?

Спасибо и извините за мой английский!

1 Ответ

0 голосов
/ 26 января 2019

Используйте событие itemView onTouch, чтобы обнаружить движения пальцев перед первым выпуском после первого прикосновения.После освобождения следующее событие onTouch отправляется в диалоговое окно, если пользователь прикасается к диалоговому окну, и затем вызывается ACTION_DOWN для диалогового окна.

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

class SomeClass {
    private int touchCount;
    private ItemFunctions itemFunctions;
    private FullscreenPhotoPreviewDialog dialog;

    void someFunction(Holder holder) {
        holder.itemView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // This block is called after first touch and before releasing his finger
                int action = event.getAction();
                switch (action) {
                case MotionEvent.ACTION_DOWN: {
                    // First touch down event here.
                    // Second one depends on where user will touch again.
                    if (0 >= touchCount) {
                        itemFunctions = new ItemFunctions(item.getId());
                    }
                    touchCount++;
                    break;
                }
                case MotionEvent.ACTION_MOVE: {
                    // First touch move events here
                    float scale = 0;
                    ...
                    if (itemFunctions.displayed) {
                        itemFunctions.applyScale(scale);
                    }
                    break;
                }
                case MotionEvent.ACTION_UP: {
                    // First up event here.
                    // Second one depends on where user will touch again.
                    break;
                }
                }

                // This should be false to get long touch event
                return false;
            }
        });

        holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View view) {
                // dialog may need itemFunctions to manipulate zoom, dismiss state callback, etc.
                dialog = FullscreenPhotoPreviewDialog.newInstance(itemFunctions.itemId);
                dialog.show(getActivity().getSupportFragmentManager(), "FullscreenPhotoPreviewDialog");
                itemFunctions.displayed = true;
                return false;
            }
        });
    }
}

class ItemFunctions {
    long itemId;
    boolean displayed;

    ItemFunctions(long itemId) {
        this.itemId = itemId;
    }

    void applyScale(float scale) {
    }
}

Дополнительные примечания

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

Без обработчиков

void funcA() {
    // funcB wiil be executed inside funcA.
    funcB();
}

С обработчиками

void funcA() {
    // Send a request to call funcB after this point.
    // Then looper will fetch the request from queue.
    // And funcB will be called then.
    // handler.post() -> sendMessageDelayed(getPostMessage(), ...)
    // getPostMessage() -> Message m = Message.obtain(); ... return m;
    handler.post(new Runnable() {
        @Override
        public void run() {
            funcB();
        }
    });
}

void funcB() {
}

Чтобы понять, почему события будут заблокированы, мы должны знать, как события вводадоставлено и обработано.

Если вы знакомы с насосом сообщений Windows, сравните ViewRootHandler.handleMessage () с Оконная процедура и Looper.loop () с Message Loop .

Looper на Android и насос сообщений на Windows имеют схожие концепции.Они доставляют сообщения в очереди.События ввода, такие как касание, щелчок и т. Д., Являются одним из таких сообщений.Таким образом, если некоторые функциональные блоки или обработка сообщения занимает много времени, другие сообщения остаются в ожидании получения следующей очереди.

Обработчики на Android и оконная процедура в программах Windows предназначены для обработки таких сообщений.Декодируйте то, что отправлено, и выполняйте задачи, соответствующие ему.

ViewRootImpl.ViewRootHandler.handleMessage на Android

public void handleMessage(Message msg) {
    switch (msg.what) {
        ...
        case MSG_PROCESS_INPUT_EVENTS:
            mProcessInputEventsScheduled = false;
            doProcessInputEvents();
            break;
        ...
    }
}     

Процедура окна в программах для Windows

LRESULT CALLBACK MainWndProc(
    HWND hwnd,        // handle to window
    UINT uMsg,        // message identifier
    WPARAM wParam,    // first message parameter
    LPARAM lParam)    // second message parameter
{ 

    switch (uMsg) 
    {
    ...
        case WM_MOUSEMOVE: 
            return 0;
    ...
    }
}

Looper.цикл на Android

public static void loop() {
    final Looper me = myLooper();
    ...
    final MessageQueue queue = me.mQueue;
    ...
    for (;;) {
        Message msg = queue.next(); // might block
        ...
        try {
            msg.target.dispatchMessage(msg);
            ...
        } finally {
            ...
        }
    }
}

Цикл сообщений в программах для Windows

while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{ 
    if (bRet == -1)
    {
        // handle the error and possibly exit
    }
    else
    {
        TranslateMessage(&msg); 
        DispatchMessage(&msg); 
    }
}

Ссылки.

Что такое насос сообщений? (Windows)

Какова цель Looper и как его использовать? (Android)

...