Рисовать на панели навигации (и других приложениях) в версии Android ›= 5

Я хотел бы нарисовать значок (указатель мыши) на экране поверх других приложений из службы. Я реализовал функциональность, и я могу рисовать на экране, кроме панели навигации. Я изучил несколько other questions здесь и безуспешно пробовал TYPE_SYSTEM_OVERLAY, TYPE_TOAST, TYPE_SYSTEM_ERROR и некоторые другие типы окон.

Я не пытаюсь захватить фокус, просто рисую указатель мыши на экране в течение секунды или двух. Когда я пытаюсь нарисовать панель навигации, она просто исчезает (на самом деле RelativeLayout заканчивается на границе с панелью навигации, даже когда я указываю размер вручную для высоты). На скриншоте ниже показан указатель в виде руки в нижней правой части экрана. Это настолько низко, насколько я могу его позиционировать. Обратите внимание, что я не пытаюсь скрыть панель навигации в своем приложении — пытаюсь рисовать поверх других приложений.

overlay-pointer-on-screen

Я даже пытался установить параметры смещения xpos и ypos в экземпляре WindowManager.LayoutParams, но это просто смещает макет и все еще находится ниже панели навигации).

Параметры макета, которые я использую для отображения этого окна:

        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.MATCH_PARENT,
                WindowManager.LayoutParams.MATCH_PARENT,
                WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
                WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
                WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
                WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
                PixelFormat.TRANSPARENT);

Затем я добавляю RelativeLayout с этими параметрами в WindowManager: windowManager.addView(relativeLayout, params);


person miha    schedule 20.07.2015    source источник
comment
Вы объявили разрешение draw over other apps в файле манифеста?   -  person mr.icetea    schedule 26.08.2015
comment
@mr.icetea да, конечно. Иначе я бы вообще не умел рисовать. Я могу хорошо рисовать и над строкой состояния, но не над панелью навигации (внизу, где находятся кнопки «Назад», «Домой» и «Недавние программные клавиши»)   -  person miha    schedule 26.08.2015


Ответы (2)


После долгих возни мне удалось получить правильные флаги, чтобы заставить его работать. Это почти работает (для большинства приложений). Ниже приведен код для достижения (пример обработчика нажатия кнопки):

@Override
public void onClick(View view) {
    if (_windowManager == null) {
        _windowManager = (WindowManager) MainActivity.this.getSystemService(Context.WINDOW_SERVICE);
    }

    WindowManager.LayoutParams params;
    // create the view on the first try
    if (_overlayView == null) {
        ImageView hand;

        _overlayView = new FrameLayout(MainActivity.this);
        hand = new ImageView(MainActivity.this);
        hand.setImageResource(R.drawable.icon_hand_pointer);
        int w = hand.getDrawable().getIntrinsicWidth();
        int h = hand.getDrawable().getIntrinsicHeight();
        _overlayView.addView(hand);

        params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
                WindowManager.LayoutParams.FLAG_FULLSCREEN |
                WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
                WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
                PixelFormat.TRANSLUCENT);

        params.width = w;
        params.height = h;
        params.gravity = Gravity.LEFT | Gravity.TOP;
        params.x = _xOffset;
        params.y = _yOffset;
        _windowManager.addView(_overlayView, params);
    } else {
        // move the view
        params = (WindowManager.LayoutParams) _overlayView.getLayoutParams();
        params.x = _xOffset;
        params.y = _yOffset;
        _windowManager.removeView(_overlayView);
        _overlayView.setLayoutParams(params);
        _windowManager.addView(_overlayView, params);
    }

    _xOffset += 40;
    _yOffset += 100;
}

Вот скриншот этого кода, работающего в приложении (вы можете увидеть наложение руки; это на Nexus 5 с Android 6.0.1: введите здесь описание изображения

person miha    schedule 03.09.2015
comment
Также см. следующий комментарий о том, что не всегда можно рисовать поверх навигационной панели. - person miha; 03.09.2015
comment
Вы когда-нибудь находили способ заставить его работать во всех приложениях? Я не могу заставить свою рисовать поверх панели навигации в некоторых приложениях, таких как режим просмотра профиля Chrome и Tinder! Он просто рисует под панелью навигации. - person Flyview; 19.10.2016
comment
@Flyview, нет, не видел; В итоге мы остановились на этом решении. - person miha; 20.10.2016
comment
@miha это работает? не могли бы вы опубликовать скриншот? - person Karun Shrestha; 02.02.2017
comment
@KarunShrestha, это работает. Вы можете протестировать его в приложении ISL Light (доступно в Play). Я также постараюсь добавить скриншот в ответ выше. - person miha; 02.02.2017

Панель навигации внизу — это то, что нельзя перекрыть, потому что элементы, нарисованные выше, могут препятствовать доступу к кнопке «Домой», кнопке «Назад» и т. д. Но вы можете сделать свою деятельность полноэкранной, что означает, что она будет скрывать кнопки навигации до тех пор, пока вы проводите снизу вверх, так что вы можете свободно рисовать в любом месте экрана

РЕДАКТИРОВАТЬ: я нашел несколько похожих вопросов, и у этого есть ответ: Нарисуйте растровые изображения поверх панели навигации в Android. Ответ состоял в том, чтобы добавить смещение по оси Y, равное размеру панели навигации.

person LonelyIdiot    schedule 29.08.2015
comment
Это может сделать приложение TeamViewer QS. Идея состоит в том, чтобы рисовать поверх других приложений, а не по собственной активности. - person miha; 01.09.2015
comment
@miha похоже, что он делает быстрый снимок экрана, а затем позволяет технику рисовать на экране, а затем снова позволяет пользователю взять на себя управление. - person LonelyIdiot; 02.09.2015
comment
@user3911725 user3911725 Я только что повторно протестировал приложение TeamViewer - я не верю, что оно использует скриншоты, так как я могу взаимодействовать с панелью запуска или приложением, пока телевизор накладывает указатель. Я нашел ответ, который вы связали ранее, но, к сожалению, он не работает (или мне не хватает ключевого ингредиента). - person miha; 03.09.2015