Addressing layout discrepancies across devices

Per the Google Play pre-launch report, a number of display items on my screens are shifted up ~20-50 px on various devices. The respective devices are running the same Android versions and screen resolutions as other devices that display fine; the only difference I see is the dpi.

Is there a way to address this programmatically, or is it something I have to live with?

Which devices are you speaking of? You’re saying they’re the  exact same aspect ratios? Or do they differ just very slightly?

There are almost always ways to deal with this programmatically. Can you post your “config.lua” so we can inspect?

Thanks,

Brent

I stand corrected, there isn’t a 1-to-1 match with version, resolution, and dpi.  Here are some examples of good and bad, along with my config.lua.  I’m currently not targeting IOS and I’m not completely sure that I ever will, so my config is overkill in some respects (copied from the Ultimate config.lua)

I guess a good question would be “which devices should I really care about?”.  

Examples of BAD Displays


Pixel - Android 7.1 | English (United States)                       - 1080×1920 | 420 dpi

Pixel - Android 8.0 | English (United States)                        - 1080×1920 | 420 dpi

Moto X (2nd Gen) - Android 4.4 | English (United States)   - 1080×1920 | 480 dpi

Galaxy J1 Ace - Android 5.1 | English (United States)        - 480×800     |  240 dpi

Nexus 5 - Android 5.1 | English (United States)              - 1080×1920 | 480 dpi

Nexus 9 - Android 5.0 | English (United States)                  - 1536×2048 | 320 dpi

Nexus 7 (2013) - Android 5.0 | English (United States)       - 1200×1920 | 320 dpi

Examples of GOOD Displays


Galaxy S7 Edge - Android 6.0 | English (United States)       - 1440×2560 | 640 dpi

Galaxy J7(2016) - Android 6.0 | English (United States        - 720×1280  | 320 dpi

Galaxy Note3 - Android 4.4 | English (United States)       - 1080×1920 | 480 dpi

[lua]

if string.sub(system.getInfo(“model”),1,4) == “iPad” then

application =

{

content =

{

fps = 60,

width = 360,

height = 480,

scale = “letterBox”,

xAlign = “center”,

yAlign = “center”,

audioPlayFrequency = 22050,

imageSuffix =

{

["@2x"] = 1.5,

["@4x"] = 3.0,

},

},

notification =

{

iphone = {

types = {

“badge”, “sound”, “alert”

}

},

},

license =

{

google =

{

key = “removed”

},

},

google =

{

projectNumber = “removed”

}

}

elseif string.sub(system.getInfo(“model”),1,2) == “iP” and display.pixelHeight > 960 then

application =

{

content =

{

antialias = true,

fps = 60,

width = 320,

height = 568,

scale = “letterBox”,

xAlign = “center”,

yAlign = “center”,

audioPlayFrequency = 22050,

imageSuffix =

{

["@2x"] = 1.5,

["@4x"] = 3.0

},

},

notification =

{

iphone = {

types = {

“badge”, “sound”, “alert”

}

},

},

license =

{

google =

{

key = “removed”

},

},

google =

{

projectNumber = “removed”

}

}

elseif string.sub(system.getInfo(“model”),1,2) == “iP” then

application =

{

content =

{

antialias = true,

fps = 60,

width = 320,

height = 480,

scale = “letterBox”,

xAlign = “center”,

yAlign = “center”,

audioPlayFrequency = 22050,

imageSuffix =

{

["@2x"] = 1.5,

["@4x"] = 3.0,

},

},

notification =

{

iphone = {

types = {

“badge”, “sound”, “alert”

}

},

},

license =

{

google =

{

key = “removed”

},

},

google =

{

projectNumber = “removed”

}

}

elseif display.pixelHeight / display.pixelWidth > 1.72 then

application =

{

content =

{

antialias = true, --THIS ONE FOR S6

fps = 60,

width = 320,

height = 570,

scale = “letterBox”,

xAlign = “center”,

yAlign = “center”,

audioPlayFrequency = 22050,

imageSuffix =

{

["@2x"] = 1.5,

["@4x"] = 3.0,

},

},

license =

{

google =

{

key = “removed”

},

},

google =

{

projectNumber = “removed”

}

}

else

application =

{

content =

{

antialias = true,

fps = 60,

width = 320,

height = 512,

scale = “letterBox”,

xAlign = “center”,

yAlign = “center”,

audioPlayFrequency = 22050,

imageSuffix =

{

["@2x"] = 1.5,

["@4x"] = 3.0,

},

},

notification =

{

iphone = {

types = {

“badge”, “sound”, “alert”

}

},

},

license =

{

google =

{

key = “removed”

},

},

google =

{

projectNumber = “removed”

}

}

end

[/lua]

(sigh)… You appear to be another victim of the “ultimate config.lua”… and actually a much worse and older variation of it, where actual device model codes and parsed string bits are checked. This approach has caused so many issues and problems over the years, I personally took down the past tutorials and I actively advise against going this route.

Sincerely… use a reliable “config.lua” with a static content area size, along with “letterbox” or “zoomEven”, and then adjust in-app UI/objects using “display.screenOriginX” and “display.screenOriginY” if you need to push them closer to a screen edge or similar. You’ll save yourself so many headaches caused by this “ultimate” config setup (biggest misnomer in a long time IMHO).

Best regards,

Brent

Well that’s embarrassing.  To save face, I’m blaming it on my newbieness :slight_smile:  I really appreciate the insight.  I’ll clean up my ignorance and upload a new version.  

Don’t be embarrassed :slight_smile: … that thing has bitten countless people in ways they didn’t anticipate. Some might argue that its “intention” was noble, and maybe they have a case, but the beast got out of control and people just started copying it over from project to project (and from other people’s projects to their own) without knowing the ramifications of doing so.

Brent

You could also be seeing the effect of soft button bars/status bars. On Android, over all the different versions, and brand additions, your screen height may count the status bar on one brand of device and not on the other.  Case in point, you have a 1920x1080 device in both the good and bad section. So something else is effecting your available pixels.

I agree, the original “ultimate config.lua” was designed at a time when there were few screen sizes and it was designed to solve a particular problem. Today there are simply too many screen variations to try and device test your way.  

What are you trying to make? It impacts the best way to setup your config.lua. For instance the advice for a portrait business app will likely be different than a landscape tower defense game.

Rob

Thanks guys. What you are saying make sense Rob, I hadn’t considered that.  My app is portrait-only.  Here are some examples.

GOOD


http://demo.sirentec.net/Good_Galaxy_S7.png

BAD


http://demo.sirentec.net/Bad_Galaxy%20J1%20Ace.png

WORSE


http://demo.sirentec.net/Bad_Nexus9.png

I’m going to try a build with native.setProperty( “androidSystemUiVisibility”, “immersiveSticky” )

Could you humor me and make a test app using this config.lua:

application = { content = { width = 320, height = 480, scale = "letterbox", imageSuffix = { ["@2x"] = 2.0, ["@4x"] = 4.0, }, }, }

Then in your main.lua do:

local fullScreen = display.newRect(display.contentCenterX, display.contentCenterY, display.actualContentWidth, display.actualContentHeight) fullScreen:setFillColor(0.5, 0.9, 0.9) local contentArea = display.newRect(display.contentCenterX, display.contentCenterY, display.contentWidth, display.contentHeight) contentArea:setFillColor( 0.9, 0.5, 0.9)

and snap screen shots on the various devices.

It might be helpful to also use display.newText() and output various values you’re actually getting:

display.actualContentWidth

display.actualContentHeight

display.pixelWidth

display.pixelHeight

display.topStatusBarContentHeight

display.screenOriginX

display.screenOriginY

Rob

Tiny clarification… there isn’t a “display.contentOriginX” (or Y). It’s “display. screen OriginX” and “display. screen OriginY”.

https://docs.coronalabs.com/api/library/display/screenOriginX.html

https://docs.coronalabs.com/api/library/display/screenOriginY.html

The reason I mention these specifically is because those are the super-useful-ultra-valuable properties which I use frequently in “letterbox” mode. Basically, if you have a standard 2:3 ratio content area like 320x480 and you run that app on an iPad (let’s assume portrait orientation), there will be letterbox regions on both the left and right sides of the content area… that’s obviously because the iPad aspect ratio is 3:4, not 2:3.

This is where the above APIs save you… if you test “screenOriginX” on the iPad, you’ll get some negative value like -20. That is basically the exact amount of pixels you need to offset some item to push it exactly up against the left edge of the screen on iPad (adding it to the object X position) or against the right side (subtracting it from the object X position).

Conveniently, it won’t have any effect on devices without left/right letterbox regions… in that case it will be -0 so you can still add/subtract it safely in your code without worrying about any device detection, varying content area sizes, or other silliness.

In any app I work on using “letterbox”, I cache these variables near the top of the module so they’re far shorter and easier to use. For instance:

[lua]

local ox = display.screenOriginX

local oy = display.screenOriginY

[/lua]

Then I just add/subtract them whenever needed, for example:

[lua]

local myImage = display.newImageRect( “ball.png”, 50, 50 )

myImage.x = 200 - ox

myImage.y = 400 + oy

[/lua]

Brent

Good catch Brent. I’ve updated my post…

Rob

Here you go Rob.  For whatever reason,  display.contentOriginX and display.contentOriginY are nil values so I removed them.

http://demo.sirentec.net/Galaxy%20J1%20Ace.png

http://demo.sirentec.net/Galaxy%20Note3.png

http://demo.sirentec.net/LG%20G3.png
 

http://demo.sirentec.net/Nexus%205%20Arabic.png

http://demo.sirentec.net/Nexus%205.png

http://demo.sirentec.net/Nexus%207%20(2013).png

http://demo.sirentec.net/Nexus%209.png

http://demo.sirentec.net/One%20(m8).png

Edit : I just saw Brent’s post.  I can go back and add those if needed.

Edit 2: These are global values that I use.  Could this possibly be some of my problem?

m.centerX = display.contentCenterX m.centerY = display.contentCenterY m.screenLeft = display.screenOriginX m.screenWidth = display.contentWidth - m.screenLeft \* 2 m.screenRight = m.screenLeft + m.screenWidth m.screenTop = display.screenOriginY m.screenHeight = display.contentHeight - m.screenTop \* 2 m.screenBottom = m.screenTop + m.screenHeight m.screenTopSB = m.screenTop + display.topStatusBarContentHeight m.screenHeightSB = display.contentHeight - m.screenTopSB m.screenBottomSB = m.screenTopSB + m.screenHeightSB

I think the important takeaway is that the teal background fills the screen correctly. The content area seems to also be correct. But the actualContentHeight is different on all the devices due to loosing screen real estate to the button bars.

Rob

At first glance, adding native.setProperty( “androidSystemUiVisibility”, “immersiveSticky” ****) to my main.lua seems to have addressed the issue for all but 2 of the devices from the test set.  (The Nexus 9 and Nexus 7 (2013)).  I can definitely live with that. 

Here’s the full main.lua in case future visitors want to utilize it.

<lua>

native.setProperty( “androidSystemUiVisibility”, “immersiveSticky” )

local fullScreen = display.newRect(display.contentCenterX, display.contentCenterY, display.actualContentWidth, display.actualContentHeight)

fullScreen:setFillColor(0.5, 0.9, 0.9)

local contentArea = display.newRect(display.contentCenterX, display.contentCenterY, display.contentWidth, display.contentHeight)

contentArea:setFillColor( 0.9, 0.5, 0.9)

local startY = 20

local text1 = display.newText("Content Width " … display.actualContentWidth, 150, startY, native.systemFont, 16 )

local text2 = display.newText("Content Height " … display.actualContentHeight, 150, startY + 25, native.systemFont, 16 )

local text3 = display.newText("Pixel Width " … display.pixelWidth, 150, startY + 50, native.systemFont, 16 )

local text4 = display.newText("Pixel Height " … display.pixelHeight, 150, startY + 75, native.systemFont, 16 )

local text5 = display.newText("SB Content Height " … display.topStatusBarContentHeight, 150, startY + 100, native.systemFont, 16 )

local text6 = display.newText("Screen OriginX " … display.screenOriginX, 100, startY + 125, native.systemFont, 16 )

local text7 = display.newText("Screen OriginY " … display.screenOriginY, 100, startY + 150, native.systemFont, 16 )

</lua>

Sorry to jump in on an outdated topic here but I’m just starting on my project and I’ve been researching resolutions.  Is there a tutorial out there demonstrating (for dummies) how to do what you’re describing with some examples?  It seems like I would benefit from understanding this concept early on in my project.  I tried searching for something but couldn’t seem to find anything.

Which devices are you speaking of? You’re saying they’re the  exact same aspect ratios? Or do they differ just very slightly?

There are almost always ways to deal with this programmatically. Can you post your “config.lua” so we can inspect?

Thanks,

Brent

I stand corrected, there isn’t a 1-to-1 match with version, resolution, and dpi.  Here are some examples of good and bad, along with my config.lua.  I’m currently not targeting IOS and I’m not completely sure that I ever will, so my config is overkill in some respects (copied from the Ultimate config.lua)

I guess a good question would be “which devices should I really care about?”.  

Examples of BAD Displays


Pixel - Android 7.1 | English (United States)                       - 1080×1920 | 420 dpi

Pixel - Android 8.0 | English (United States)                        - 1080×1920 | 420 dpi

Moto X (2nd Gen) - Android 4.4 | English (United States)   - 1080×1920 | 480 dpi

Galaxy J1 Ace - Android 5.1 | English (United States)        - 480×800     |  240 dpi

Nexus 5 - Android 5.1 | English (United States)              - 1080×1920 | 480 dpi

Nexus 9 - Android 5.0 | English (United States)                  - 1536×2048 | 320 dpi

Nexus 7 (2013) - Android 5.0 | English (United States)       - 1200×1920 | 320 dpi

Examples of GOOD Displays


Galaxy S7 Edge - Android 6.0 | English (United States)       - 1440×2560 | 640 dpi

Galaxy J7(2016) - Android 6.0 | English (United States        - 720×1280  | 320 dpi

Galaxy Note3 - Android 4.4 | English (United States)       - 1080×1920 | 480 dpi

[lua]

if string.sub(system.getInfo(“model”),1,4) == “iPad” then

application =

{

content =

{

fps = 60,

width = 360,

height = 480,

scale = “letterBox”,

xAlign = “center”,

yAlign = “center”,

audioPlayFrequency = 22050,

imageSuffix =

{

["@2x"] = 1.5,

["@4x"] = 3.0,

},

},

notification =

{

iphone = {

types = {

“badge”, “sound”, “alert”

}

},

},

license =

{

google =

{

key = “removed”

},

},

google =

{

projectNumber = “removed”

}

}

elseif string.sub(system.getInfo(“model”),1,2) == “iP” and display.pixelHeight > 960 then

application =

{

content =

{

antialias = true,

fps = 60,

width = 320,

height = 568,

scale = “letterBox”,

xAlign = “center”,

yAlign = “center”,

audioPlayFrequency = 22050,

imageSuffix =

{

["@2x"] = 1.5,

["@4x"] = 3.0

},

},

notification =

{

iphone = {

types = {

“badge”, “sound”, “alert”

}

},

},

license =

{

google =

{

key = “removed”

},

},

google =

{

projectNumber = “removed”

}

}

elseif string.sub(system.getInfo(“model”),1,2) == “iP” then

application =

{

content =

{

antialias = true,

fps = 60,

width = 320,

height = 480,

scale = “letterBox”,

xAlign = “center”,

yAlign = “center”,

audioPlayFrequency = 22050,

imageSuffix =

{

["@2x"] = 1.5,

["@4x"] = 3.0,

},

},

notification =

{

iphone = {

types = {

“badge”, “sound”, “alert”

}

},

},

license =

{

google =

{

key = “removed”

},

},

google =

{

projectNumber = “removed”

}

}

elseif display.pixelHeight / display.pixelWidth > 1.72 then

application =

{

content =

{

antialias = true, --THIS ONE FOR S6

fps = 60,

width = 320,

height = 570,

scale = “letterBox”,

xAlign = “center”,

yAlign = “center”,

audioPlayFrequency = 22050,

imageSuffix =

{

["@2x"] = 1.5,

["@4x"] = 3.0,

},

},

license =

{

google =

{

key = “removed”

},

},

google =

{

projectNumber = “removed”

}

}

else

application =

{

content =

{

antialias = true,

fps = 60,

width = 320,

height = 512,

scale = “letterBox”,

xAlign = “center”,

yAlign = “center”,

audioPlayFrequency = 22050,

imageSuffix =

{

["@2x"] = 1.5,

["@4x"] = 3.0,

},

},

notification =

{

iphone = {

types = {

“badge”, “sound”, “alert”

}

},

},

license =

{

google =

{

key = “removed”

},

},

google =

{

projectNumber = “removed”

}

}

end

[/lua]

(sigh)… You appear to be another victim of the “ultimate config.lua”… and actually a much worse and older variation of it, where actual device model codes and parsed string bits are checked. This approach has caused so many issues and problems over the years, I personally took down the past tutorials and I actively advise against going this route.

Sincerely… use a reliable “config.lua” with a static content area size, along with “letterbox” or “zoomEven”, and then adjust in-app UI/objects using “display.screenOriginX” and “display.screenOriginY” if you need to push them closer to a screen edge or similar. You’ll save yourself so many headaches caused by this “ultimate” config setup (biggest misnomer in a long time IMHO).

Best regards,

Brent

Well that’s embarrassing.  To save face, I’m blaming it on my newbieness :slight_smile:  I really appreciate the insight.  I’ll clean up my ignorance and upload a new version.  

Don’t be embarrassed :slight_smile: … that thing has bitten countless people in ways they didn’t anticipate. Some might argue that its “intention” was noble, and maybe they have a case, but the beast got out of control and people just started copying it over from project to project (and from other people’s projects to their own) without knowing the ramifications of doing so.

Brent