2

Я создаю приложение, которое создает крошечные анимации спрайтов, которые ходят по экрану.Как сделать полноэкранный оверлей оставшимся на экране после изменения ориентации?

У меня есть основная деятельность, с кнопкой «начать сервис». Это запустит службу, которая (в onCreate()) создает полноэкранное представление и прикрепляет его к диспетчеру корневого окна.

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

initial render is fine

Проблема возникает при повороте устройства.

view does not rotate with device

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

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


Часть проблемы связана с тем, как я определил размеры вида. Я использовал флажки, чтобы указать «полный экран», но это не установить вид width и height в соответствии с размерами экрана. Таким образом, я должен был установить их вручную при запуске.

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

Моя служба создает вид, как так:

public class WalkerService extends Service { 
    private WalkerView view; 

    @Override 
    public void onCreate() { 
     super.onCreate(); 

     final WindowManager wm = (WindowManager)getSystemService(WINDOW_SERVICE); 

     final DisplayMetrics metrics = new DisplayMetrics(); 
     wm.getDefaultDisplay().getMetrics(metrics); 

     view = new WalkerView(this); // extends SurfaceView 
     final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
       WindowManager.LayoutParams.FLAG_FULLSCREEN, 
       WindowManager.LayoutParams.FLAG_FULLSCREEN, 
       WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, // TYPE_SYSTEM_ALERT is denied in apiLevel >=19 
       WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, 
       PixelFormat.TRANSLUCENT 
     ); 
     view.setFitsSystemWindows(false); // allow us to draw over status bar, navigation bar 

     // here I _manually_ set the dimensions, because otherwise it defaults to a square (something like 1024x1024) 
     params.width = metrics.widthPixels; 
     params.height = metrics.heightPixels; 

     wm.addView(view, params); 
    } 
} 

Я пытался слушать изменения ориентации и поворота вид, когда это произойдет:

@Override 
public void onConfigurationChanged(Configuration newConfig) { 
    super.onConfigurationChanged(newConfig); 

    view.setRotation(
      newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE 
        ? 90.0f 
        : 0.0f 
    ); 
} 

Но setRotation(), похоже, не влияет width и height, и они не меняли угол, на котором был сделан холст.


Я в основном неправильно понимаю, как создать полноэкранный накладной? Как я могу поддерживать способность рисовать по всему экрану, независимо от ориентации (и даже когда мое приложение не является активным приложением)?

Возможно, это связано с тем, что я привязываю свое представление к оконному менеджеру Window Service - возможно, это означает, что мой взгляд имеет нечетного родителя (я мог представить, что, возможно, на корневом представлении не повлияет ориентация, или нет определенных границ для того, чтобы дети могли растягиваться, чтобы заполнить).

Полный исходный код public on GitHub в случае, если я опущен какой-либо полезной информацией здесь.

WalkerView.java может быть особенно актуальным, поэтому here it is.

+0

Я думаю, что это дублирующая проблема для этого http://stackoverflow.com/a/10107525/5870896 –

+0

Как насчет наличия 2 просмотров.Один для портрета и один для пейзажа с правильной шириной и высотой для каждого. Когда происходит изменение ориентации, просто удалите ненужное представление и добавьте действительный вид в windowmanager. –

+0

@SecretCoder Я считаю, что мой код уже похож на ответ на этот вопрос, так как это одна из ссылок, которые я использовал для создания моего кода. Единственное различие заключается в том, что связанный ответ захватывает ссылку на представление с помощью инфлятора, тогда как мой код создает экземпляр представления inline в службе. Учитывая, что у меня есть сопоставимый код с этим ответом, и _still_ испытывают проблему: я бы сказал, что связанная проблема не отвечает на мой вопрос и поэтому не является той же проблемой. – Birchlabs

ответ

1

Я был введен в заблуждение от ответа на предыдущий вопрос "How to create always-top fullscreen overlay activity in Android". Может быть, он работал на более высоком уровне API? Я использую уровень API 24.

Это рекомендовал частности ответ:

final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
    WindowManager.LayoutParams.FLAG_FULLSCREEN, 
    WindowManager.LayoutParams.FLAG_FULLSCREEN, 
    WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, 
    WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, 
    PixelFormat.TRANSLUCENT 
); 

Существует проблема с этим. Конструкторы, которые существуют для WindowManager.LayoutParams следующим образом:

enter image description here

Так WindowManager.LayoutParams.FLAG_FULLSCREEN флаг привыкает как явное значение для int w и int h. Это не годится!


Я обнаружил, что правильно конструкция для наложения полноэкранного, как так:

final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
    WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, // TYPE_SYSTEM_ALERT is denied in apiLevel >=19 
    WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_FULLSCREEN, 
    PixelFormat.TRANSLUCENT 
); 

Это не означает, что мы больше не явно указать ширину и высоту. Вместо этого макет опирается на наши флаги.

Да, WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - обязательный флаг; это необходимо, если вы хотите нарисовать украшения, такие как строка состояния.

TYPE_SYSTEM_ALERT следует использовать вместо уровня TYPE_SYSTEM_ALERT в уровне API> = 19.


Бонусные ноты (если вы читаете это, вы, вероятно, пытается сделать накладку полноэкранный):

Ваш манифест потребуются следующие разрешения (объяснение here):

<uses-permission android:name="android.permission.ACTION_MANAGE_OVERLAY_PERMISSION"/> 
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> 

Я понимаю, что SYSTEM_ALERT_WINDOW фактического разрешения требуется, но ACTION_MANAGE_OVERLAY_PERMISSION необходима также: она позволяет запрашивать во время выполнения, чтобы пользователь выдачи SYSTEM_ALERT_WINDOW привилегию.

0

Я думаю, что ваша ситуация может быть решена с помощью альтернативного подхода.

  1. Создание полноэкранного пользовательского вида (вы можете установить соответствующий флаг в конструкторе пользовательского вида)
  2. Override onSizeChanged() в пользовательском представлении. Это даст вам высоту и ширину холста. Это необязательно. Если вы хотите заполнить весь пользовательский вид полупрозрачным цветом, это не будет необходимо.
  3. Используйте ширину и высоту сверху или просто используйте canvas.drawColor() в onDraw() пользовательского вида.

Это гарантирует, что всякий раз, когда представление воссоздается, оно будет перерисовано, чтобы заполнить весь экран (холст).

+0

Моя цель ** не ** рисовать цвет по всем моим границам холста - я рисовал это окно только для иллюстративные цели (чтобы продемонстрировать, что мое представление игнорирует вращение и что его размеры не обновляются по сравнению с их начальным значением). Но шаг 2 звучит точно так, как я хочу, поэтому я попробовал. К сожалению, мой взгляд 'onSizeChanged()' запускается только один раз (при запуске, он изменяется от 0x0 до 1080x1920). Вращение устройства не ** ** вызывает это событие. (И да, моя служба подписалась на 'android: configChanges =" keyboardHidden | orientation | screenSize "в моем манифесте). – Birchlabs

+0

Поправка: onSizeChanged() '_does_ запускается при изменении ориентации, но _only_, если ваш макет имеет динамические размеры. Конструкция 'WindowManager.LayoutParams(), которую я использовал первоначально, имела _explicitly_-set dimensions -WindowManager.LayoutParams.FLAG_FULLSCREEN', была интерпретирована как целочисленное значение 1024 для ширины и высоты - и, как результат, размер представления никогда не изменялся. Но 'onSizeChanged()' _does_ запускается, если используется в сочетании с конструкцией WindowManager.LayoutParams() в [мой ответ] (http://stackoverflow.com/a/41474994/5257399). – Birchlabs