I am developing with Nexus 4 KitKat 4.4 and
trying to add IMMERSIVE MODE to my game.
I need screen height to set glViewport correctly.
Previously I used
#SuppressWarnings("deprecation")
#TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
public static int getScreenHeight() {
if (Main.m_activity == null)
return -1;
Display display = Main.m_activity.getWindowManager()
.getDefaultDisplay();
int height = -1;
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB){
//width = display.getWidth(); // deprecated
height = display.getHeight(); // deprecated
}else{
Point size = new Point();
display.getSize(size);
height = size.y;
}
return height;
}
It does not return the real height of screen in IMMERSIVE MODE.
So I started to use values from
private static class Renderer implements GLSurfaceView.Renderer {
public void onSurfaceChanged(GL10 gl, int width, int height) {
and it worked fine when app starts. If I press home button and return to home screen and then back to game onSurfaceChanged get called again but with old wrong values (non-immersive mode screen size, smaller, regular)
IMMERSIVE SCREEN size is 800x1280
REGULAR size is 800x1184
When I get regular size and set it in glViewport then I get black line in top of screen.
PS Also IMMERSIVE MODE is lost when I press volume buttons.
PS2
I have following method impl. It does not help to handle screen/window resize.
#Override
public void onWindowFocusChanged(boolean hasFocus) {
Good article but still not enough.
http://developer.android.com/training/system-ui/immersive.html
Right now we get screen size with
public void onSurfaceChanged(GL10 gl, int width, int height) {
and if height decreased then we enable immersive mode again with 1 second delay.
This works but hacky and I look for canonical solution myself.
I am afraid it is just buggy in KitKat.
I do not any famous title that implemented immersive mode. (except our app of course )))
Use Display.getRealSize to return the screen bounds without system windows.
You can use View.OnSystemUiVisibilityChangeListener to get a callback when immersive mode is disabled/enabled. Using this, you can figure out the actual height of the screen.
Related
I have a game with full screen SurfaceView (Portrait) and thread that renders continously on the surface.
The SurfaceView is initialized onCreate and its size is determined by the width and height of the screen in pixels.
After it's created, it is set as the content view of the Game Activity (no XML definitions):
Bitmap frameBuffer = Bitmap.createBitmap(screenWidth, screenHeight, Config.ARGB_8888);
MySurfaceView renderView = new MySurfaceView(this, frameBuffer);
this.setContentView(renderView);
The problem is, height of the screen doesn't include Display Cutouts (for devices that have them) and is taller than the allowed drawing screen, because the renderView is placed below the cutout area and does not use it for rendering.
I seek to find the real screen height - display cutout height so that I can make the frame buffer have that height.
Do you know if there is way to get display cutout area height onCreate, the same way we're able to get navigation/status bar height. Below is a snippet that returns navigation bar height:
Resources resources = getResources();
int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
int navigationBarHeight = resources.getDimensionPixelSize(resourceId);
}
Thank you for your help!
--- UPDATE ---
I will try to clarify the question more:
1) onCreate a full screen portrait SurfaceView is created with the dimensions of the screen width and height (no XML)
2) Immersive mode is enabled for API level > 19 (KitKat)
3) Screen width and height is taken with Display.getSize() or Display.getRealSize() depending on API level
4) On devices with Top Display Cutout the view is automatically placed below the cutout and bottom content is cut out, and this is the issue
5) I would like to get the height of the Display Cutout onCreate, so that I can create the SurfaceView height to be screenHeight - displayCutoutHeight.
6) The problem boils down to finding the height of the Display Cutout.
There seems to be no way to get WindowInsets object in onCreate. Your Activity has to be attached to window first. So your options are:
1.
override fun onAttachedToWindow() {
val cutout = window.decorView.rootWindowInsets.displayCutout
}
2.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
contentView?.setOnApplyWindowInsetsListener { _, insets ->
val cutout = insets.displayCutout
return#setOnApplyWindowInsetsListener insets
}
}
WindowInsets.getDisplayCutout() returns the DisplayCutout object if present.
To get the bounding rectangle of display cutout you can make use of
DisplayCutout.getBoundingRects()
It will give you a list of Rect of all the display cutouts(non-functional area) on the screen.
In order to get the height of each display cutout,
Rect.height()
This might return negative values also so make sure to take the modulus of height.
For more information about display cutout support, see the following links:
android:windowLayoutInDisplayCutoutMode
DisplayCutout
layoutInDisplayCutoutMode
WindowInsets
WindowInsets.getDisplayCutout()
This modification of Roman's answer (above) worked for me:
#Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
Log.i("Chicken", "cutout: " + getWindow().getDecorView().getRootWindowInsets().getDisplayCutout());
}
Here's how in onCreate():
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
drumRoll1Button.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
DisplayCutout displayCutout = drumRoll1Button.getRootWindowInsets().getDisplayCutout();
if (displayCutout!=null) {
displayCutoutLengthPx = displayCutout.getSafeInsetLeft();
}
Log.v(LOG_TAG,"displayCutoutLengthPx:"+displayCutoutLengthPx);
}
});
}
Please swap in any view on screen (for drumRoll1Button) and getSafeInsetTop() (for getSafeInsetLeft()) if you are in Portrait mode, which the original question is asking. This sample works for Landscape.
I'm making a game for android using libgdx and I want the game look good (with the same proportion) on different screens of smartphones, but not achievement. In a device the picture looks normal and another is very small.
I used viewports and OrthographicCamera but I don't see good results. Maybe what I'm doing wrong.
Currently I have this code (excerpt):
public class PlayScreen extends BaseScreen {
private Stage stage;
private FaceActor faceActor64;
private Texture faceTexture64;
private int sw;
private int sh;
public PlayScreen(MainGame game) {
super(game);
faceTexture64 = new Texture("images/all64.png");
}
#Override
public void show() {
sw = Gdx.app.getGraphics().getWidth();
sh = Gdx.app.getGraphics().getHeight();
stage = new Stage(new FitViewport(sw, sh));
faceActor64 = new FaceActor(faceTexture64);
faceActor64.setBounds(150, 150, 64, 64);
stage.addActor(faceActor64);
Gdx.input.setInputProcessor(stage);
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0,0,0,1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act(delta);
stage.draw();
}
#Override
public void resize(int width, int height) {
stage.getViewport().update(width, height, true);
}
#Override
public void hide() {
stage.dispose();
}
#Override
public void dispose() {
faceTexture64.dispose();
}
}
I'm using an image of 64px.
Result in a smartphone of 480x800.
Result in a smartphone of 1080x1920.
Any idea how to fix it?
What you are doing there is this:
For a 720x1280 device you are setting the view of your game world to 720x1280 and for a 1080x1920 device you are setting it to 1080x1920. You assets most likely do not change in size so the more pixels a device has the more it will show of your game world. I always tell people to forget about pixels, unless they want a pixel perfect game and that is what you are creating if you do not resize your assets, a pixel perfect game world.
If you think about it, your game does not even need to know about the device. It just needs to know how much of your game world to render in that FitViewport. So let's say I have a tile game and my tiles have a size of 1x1 units. If I would want to show 16 tiles vertically and 9 horizontally I would setup my FitViewport as new FitViewport(9, 16). This would fill up the screen on most devices since they often have a aspect ratio of 16:9.
The short explanation is, pass only constants into the Viewport constructor, not the actual screen dimensions. Pick dimensions that you want to work with, and the viewport will stretch them to fit the actual screen.
Also, if you don't want black bars ("letterboxing / pillarboxing"), use ExtendViewport instead of FitViewport.
When I determine the screen size in an Activity with this attempt, I get 800x480 as result:
#SuppressWarnings("deprecation")
private void initSpecs()
{
WindowManager windowManager = (WindowManager)this.getSystemService(Context.WINDOW_SERVICE);
Point size = new Point();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2)
{
windowManager.getDefaultDisplay().getSize(size);
effectiveWidth = size.x;
effectiveHeight = size.y;
}
else
{
Display display = windowManager.getDefaultDisplay();
effectiveWidth = display.getWidth();
effectiveHeight = display.getHeight();
}
}
When I determine the screen size in my game view (a class extending view) with the following attempt, I get 800x442.
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
}
There is NO title bar. How does this discrepancy emerge?
Well the problem is that in the first way you are actually getting the "screen/display" size, however, In the second way what you are getting is the width/height measured values of the View where you overrode the "onMeasure", and that's not necessarily equal to the screen size, specially if another Views affect the size of your View during it's life cycle. This is kind of the life cycle of a View.
Attached
Measured*
Layout
Drawn
Notice that there's a chance this life cycle repeats if another View changes it's size and the OS thinks is a good idea to refresh the views.
Hope it Helps!
Regards!
Sorry, I can't describe my problem clearly. I re-describe it:
I am making an android game with libgdx, and for now I finish a demo:
It has only one game view which is implemented by GameScreen class which is a subclass of Screen in libgdx:
public class LaohuGame extends Game {
public static final int FRUIT_NUM = 24;
public static final String WORK_DIR = "Desktop/assets/";
#Override
public void create() {
GameScreen gameScreen = new GameScreen(this);
setScreen(gameScreen);
}
public class GameScreen extends LaohuScreen {
private final Stage stage;
public static int START_X = 11;
public static int START_Y = 199;
public GameScreen(Game game) {
super(game);
stage = new Stage(GAME_WIDTH,GAME_HEIGHT,true);
//add actors to the stage
}
public class LaohuScreen implements Screen{
int GAME_WIDTH;
int GAME_HEIGHT;
protected Game game;
public LaohuScreen(Game game) {
this.game = game;
GAME_WIDTH = Gdx.graphics.getWidth();
GAME_HEIGHT = Gdx.graphics.getHeight();
}
The GAME_WIDTH,GAME_HEIGHT is equal to the width,height of phone screen, so the game view fill the whole phone screen.
Now I go on developing my game. I wanna have 4 game view in my game. Each game view implemented by a subclass of Screen in libgdx. I think the completed game should look like this pic:
A button bar is added to the bottom of the phone screen. When I click one of the 4 buttons, the game view switched, but the button bar didn't switch. For example, if player clicks the "Rank" button, game set the view to Rank View(implemented by Rank screen) which shows players' scores, and the buttons still visible:
I can think of 2 ways to keep the button bar visible:
Combine button bar and game view into a libgdx's Screen. I think it is a waste because I need to draw 4 buttons in every screen, totally 16 buttons in 4 Screens.
Split phone screen into 2 areas; game view area and button bar area. Game view area implemented by libgdx's Screen and I can switch game view by setScreen method. The problem is that libgdx's Screen seems always fills the whole phone screen; I can't set the screen's width and height. I tried using resize(int width, int height) method to cut down the height of screen to leave a blank space for button bar, but it doesn't work.
Code:
public class LaohuGame extends Game {
public static final int FRUIT_NUM = 24;
public static final String WORK_DIR = "Desktop/assets/";
#Override
public void create() {
GameScreen gameScreen = new GameScreen(this);
setScreen(gameScreen);
}
#Override
public void setScreen(Screen screen) {
super.setScreen(screen);
this.getScreen().resize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight() * 5 / 10);
}
#Override
public void resize(int width, int height) {
if (getScreen() != null) getScreen().resize(width, height * 5 / 10);
}
}
Any suggestions are appreciated.
All libgdx things assume that the width and height always are equal to the "window" size. On a desktop machine this might be less than the whole screensize, but it will still fill the whole render area. On a phone it will most likely always be the phones display size.
Setting it to something smaller will not help you, because it will not use only a part of rendering area. Instead you will get weirdly stretched results.
Don't alter the values you get from the resize method. Instead go with your first solution and render the buttons on the bottom part of your game screens.
You don't need to duplicate any code here. Implement something like a "ButtonBarGUI" which uses a Stage and the scene2d.ui stuff to render four buttons on the bottom of the screen and then just reuse this helper class to render the button bar on any screen via Stage.draw().
I'm trying to make a app that allows you to drag shapes around. It works fine on smart phones, but not on my Acer a500 tablet
When I get the height by calling
ih=getWindowManager().getDefaultDisplay().getHeight()-25;
I get a value thats only 1/3 of what it should be, thus I can only drag the sahpes 1/3 the way down. If the tablet is horizonatl it goes 1/2 way down.
Why is this methed returning the wrong values for the height on my tablet??
public class cPlay extends cBase implements OnClickListener {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.play);
int w=getWindowManager().getDefaultDisplay().getWidth()-25;
int h=getWindowManager().getDefaultDisplay().getHeight()-25;
BallView ballView=new BallView(this,w,h);
setContentView(ballView);
} // end function
public void onClick(View v) {
finish();
} // end function
} // end class
Try this:
DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
int height = displaymetrics.heightPixels;
int Width = displaymetrics.widthPixels;
Methods getWidth() and getHeight() are deprecated in Display.
Try to use DisplayMetrics.
Also you might want to get the size of your view's container by calling getMeasuredWidth() and getMeasuredHeight() after onMeasure() was called to get more precise size of your view.