My Android application embeds a webview, the webview is written in Angular10. The webview displays a list of items using infinite scroll. A chunk of the data is displayed and when IntersectionObserver is triggered a new chunk of data is displayed.
It works fine on some of my devices (for example Google Pixel 4, Samsung S10e) but on some device (for example Sony Xperia XZ, Samsung TabS2) only the first chunk is displayed, the IntersectionObserver is not triggered and other chunks are not displayed.
The Android application computes the available space for the webview, if I slightly change the available space (for example by substracting 5 pixels) then the IntersectionObserver is triggered. This does not work for all devices.
The place available on the screen for the webview is computed as follow:
private fun computeWebViewHeight() {
val displaymetrics = DisplayMetrics()
activity.windowManager.defaultDisplay.getMetrics(displaymetrics)
val deviceHeight = displaymetrics.heightPixels
webView.viewTreeObserver.addOnGlobalLayoutListener(object : OnGlobalLayoutListener {
override fun onGlobalLayout() {
webView.viewTreeObserver.removeOnGlobalLayoutListener(this)
val actionBar = activity.findViewById<RelativeLayout>(R.id.action_bar)
webView.layoutParams = LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
deviceHeight - actionBar.height - statusBarHeight // - 5
)
}
})
}
I first thought that the available space computed was erroneous but if I compare with the effective height of the webview then both measurement matches, that is the height of the webview corresponds to the available space.
Have you already experienced such behaviour ? Do you have any idea where the problem comes from ?
Related
I was creating a new android app that has a particular feature in a one of the screens, which is generating an image to a random spot in the screen, I used a class as a helper to return screen dimensions:
private val displayMetrics: DisplayMetrics by lazy {
Resources.getSystem().displayMetrics
}
// Returns boundary of the screen in pixels (px).
val screenRectPx: Rect get() = displayMetrics.run { Rect(0, 0, widthPixels, heightPixels) }
// Returns boundary of the screen in density independent pixels (dp).
val screenRectDp: RectF get() = screenRectPx.run { RectF(0f, 0f, right.px2dp, bottom.px2dp) }
// Converts any given number from pixels (px) into density independent pixels (dp).
val Number.px2dp: Float get() = this.toFloat() / displayMetrics.density
Then I have created a function in the fragment (I call this function 30 times every time I click the image):
private fun generateNewPosition(){
val startMarginLimit: Int = widthDp.toInt() - 100
val topMarginLimit: Int = heightDp.toInt() - 340
val startMarginRandom = (0 until startMarginLimit).random()
val topMarginRandom = (0 until topMarginLimit).random()
val startMarginInDp = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
startMarginRandom.toFloat(),
resources.displayMetrics
).toInt()
val topMarginInDp = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
topMarginRandom.toFloat(),
resources.displayMetrics
).toInt()
val linearLayoutParams = target.getLayoutParams() as LinearLayout.LayoutParams
linearLayoutParams.setMargins(startMarginInDp, topMarginInDp, 0, 0)
target.setLayoutParams(linearLayoutParams)
}
The width and height of the image itself inside xml is 90dp, anyway it worked as expected while I was debugging on two phones and an emulator, all works fine, but I published the app on google play store and I downloaded it from two other phones one of them exactly like the one I used to debug and the function didn't work as expected, the image kept generating a random spots vertically, but horizontally is the same, it always at the edge of the screen and even after clicking it for the 30th time (which should finally set visibility to this image to gone) the app crashes, I also tried to uninstall from the phone I was using to debug the app (uninstalled the debug version) and install it from google play store and it works just fine and no crashes, what could cause these behaviors?
Actually the problem was related to phone's language, if the language is rtl the problem shows, as the layout reversed, so the startMargin keep applying it self to the edge (where the image initially generated in right), and I fixed the problem by setting the layout direction to ltr by using:
android:layoutDirection="ltr"
This is a very simple example to achieve the android multi screen wallpaper effect on iOS, written in Swift. Sorry for the poor and redundant English as I am a non-native speaker and new to coding.
Many android phones have a multi screen desktop with the feature that when you swipe between screens, the wallpaper moves horizontally according to your gesture. Usually the wallpaper will move in a smaller scale comparing to the icons or search boxes to convey a sense of depth.
To do this, I use a UIScrollView instead of a UIPageViewController.I tried the latter at first, which makes the viewController hierarchy become very complex, and I couldn't use the touchesMove method correctly, as when you pan around pages, a new child viewController will come in and interrupt the method.
Instead, I tried UIScrollView and here is what I have reached.
drag a wallpaper imageView into viewController, set the image, set the frame to (0,0,width of image, height of screen)
set the viewController size to freedom and set the width to theNumberOfPagesYouWantToDisplay * screenWidth (only to make the next steps easier, the width of the viewController will not affect the final result.)
drag a scrollView that fits in the viewController
add the "content UIs" into the scrollView to become a childView of it. Or you can do it using codes.
add a pageControl if you would like.
in your viewController.swift add the following code
import UIKit
class ViewController: UIViewController, UIScrollViewDelegate{
let pagesToDisplay = 10 // You can modify this to test the result.
let scrollViewWidth = CGFloat(320)
let scrollViewHeight = CGFloat(568) // You can also modify these based on the screen size of your device, this is an example for iPhone 5/5s
#IBOutlet weak var backgroundView: UIImageView!
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var pageControl: UIPageControl!
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
scrollView.pagingEnabled = true
// Makes the scrollView feel like pageViewController.
scrollView.frame = CGRectMake(0,0,scrollViewWidth,scrollViewHeight)
//Remember to set the frame back to the screen size.
scrollView.contentSize = CGSizeMake(CGFloat(pagesToDisplay) * scrollViewWidth, scrollViewHeight)
pageControl.numberOfPages = pagesToDisplay
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
let pageIndex = Int(scrollView.contentOffset.x / scrollView.frame.size.width)
pageControl.currentPage = pageIndex
}
func scrollViewDidScroll(scrollView: UIScrollView) {
let bgvMaxOffset = scrollView.frame.width - backgroundView.frame.width
let svMaxOffset = scrollView.frame.width * CGFloat(pagesToDisplay - 1)
if scrollView.contentOffset.x >= 0 &&
scrollView.contentOffset.x <= svMaxOffset {
backgroundView.frame.origin.x = scrollView.contentOffset.x * (bgvMaxOffset / svMaxOffset)
}
}
}
Feel free to try this on your device, and it will be really appreciated if someone could give some advice on this. Thank you.
I have worked so hard on an app that displays perfectly on my Galaxy Note 3. However, it does not display right on iPhone and also one other Droid I tested on. My issue is with addChild() and then resizing it to fit the screen. For some reason when I add the Background (addBG(); The screen size works but if I load addChild to the BG, this works great on my Note 3 but not on the iPhone or another android device.
My issue is with the screenX, screenY var I created. They seem to be different for devices. Or it is a "rendering order issue" I am not sure. Would be so greatful for the help to fix this issue so it looks great on each phone. I have read some tuts on this subject but they are confusing. I think my code is close, I hope, and maybe it just needs a tweak. !
Here is a shot of the about screen on an IPhone. See the white does not fit the whole screen.
and Here is a shot from my Droid Note 3.
Declared Vars in a package:
This is not my full code, of course but only that which I believe is relevant.
public var screenX:int;
public var screenY:int;
public function Main()
{
if (stage)
{
setStage();
addBG();
}
}
public function setStage()
{
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
if (flash.system.Capabilities.screenResolutionX > stage.stageWidth)
{
screenX = stage.stageWidth;
screenY = stage.stageHeight;
}
else
{
screenX = flash.system.Capabilities.screenResolutionX;
screenY = flash.system.Capabilities.screenResolutionY;
}
}
This works: addBG();
public function addBG()
{
theBG = new BG();
addChild(theBG);
theBG.width = screenX;
theBG.height = screenY;
}
This does not: addAbout();
public function addAbout()
{
About = new viewAbout();
About.width = screenX;
About.height = screenY;
theBG.addChild(About);
TweenMax.fromTo(About,1, {alpha:0}, {alpha:1, ease:Expo.easeOut} );
}
UPDATE: And yet another more complex load is also called from a button and having the same issue. I hope you see the logic of what I am trying to do. First set the BG to the device then load and resize content to fit proportionally into the BG I loaded. The reason being, the BG will be distorted to different proportions and that's ok, but the content can not. So here is the example that loads content into the BG and then content into that container.
public function addRosaryApp()
{
Rosary = new viewRosaryApp();
Rosary.width = screenX;
Rosary.height = screenY;
theBG.addChild(Rosary);
TweenMax.fromTo(Rosary,1, {alpha:0}, {alpha:1, ease:Expo.easeOut} );
contentRosary = new contentRosaryApp();
theBG.addChild(contentRosary);
contentRosary.width = screenX;
contentRosary.scaleY = contentRosary.scaleX;
contentRosary.x = screenX/2 - contentRosary.width/2;
contentRosary.y = menuBanner.height;
}
Have you tried adding the child to stage first, and then setting the size? That's the only difference I can see between addBG and addAbout
About = new viewAbout();
theBG.addChild(About); // do this first
About.width = screenX; // then set width
About.height = screenY; // and height
I think your problem may have to do with either of several things.
My findings so far from my own devices (an Ipad Air, an iphone 4S, an LG G2 and an Acer Iconia A500) is that the only device size that's being reported correctly at all times is the stage.fullScreenWidth and the stage.fullScreenHeight
1st, Android reports Capabilities.screenResolutionX as the LONG side of your Android device.
IOS devices however report the SHORT side of your device to Capabilities.screenResolutionX.
2nd, stage.stageWidth and stage.stageHeight seem to report the wrong size on both Android and IOS.
And in fact if you check these before and after setting stage.scaleMode and stage.align then the values will differ completly (although Android will show it almost correctly).
My suggestion is that you set a base size (width and height) for your app and then compare it with the actual stage.fullScreenWidth and stage.fullScreenHeight reported by the device. That way you can get some nice scaleratios to use to scale your displayobjects. That's what I'm currently on my apps and it scales just fine :-)
How can I restrict my GoogleTV app to display in a specific resolution: i.e. 720P, 1080p, etc? For some reason my app is stuck at displaying at a resolution around 960x540 even though my GoogleTV and monitor can handle 1080p.
I'm not sure if it's just my GoogleTV that is displaying only in 960x540 or if other GoogleTVs are also seeing the same thing. In any case, I want to make sure that my app can only be viewed in the resolutions: 960x540 or 1280x720
Each display on a Google TV is different. (With the exception of built in TV's like the LG) There is a step when the Google TV is just turned on where you establish the resolution of the TV.
Most current Google TV (ARM based) are 1080p. Scaling down to 720 is accomplished by your TV.
All that said, 960x540 are android Device Pixels for either 720p or 1080p.
So, to summarize, you can't / shouldn't do what your asking.
I figured it out. I had to set the initial scale of the webview, and that allows me to view the WebView in 720p even though the screen resolution is 1080p.
This is the code I used:
boolean _bReloaded = false;
boolean _bLoaded = false;
int _nWebScale = 100;
DisplayMetrics displaymetrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
int nScreenHeight = displaymetrics.heightPixels;
if(nScreenHeight >= 1000){
_nWebScale = (int) Math.round((nScreenHeight/720.0) * 100.0);
_bReloaded = false;
mWebView.setVisibility(View.INVISIBLE);
}
mWebView.setInitialScale(_nWebScale);
But I wasn't done there.
At this point, the webView is displayed in the proper resolution; however, there is a noticeable resizing of the webview window when the webView loads. This is why in the above code I hide the webview. Another problem is that in my particular case, my HTML wasn't listening to a window resize event and so it didn't readjust its UI after the webview was noticeably seen changing size. To fix this I could change my HTML and have it react to a JavaScript window resize event, or I could do what I ultimately went with - reloading the webview after it loads with the incorrect sized UI. With this solution I didn't have to react to a window resize event:
mWebView.setWebChromeClient(new WebChromeClient() {
public void onProgressChanged(WebView view, int progress) {
if (progress == 100) {
if(_nWebScale != 100 && (mWebView.getVisibility() != View.VISIBLE) && !_bReloaded){
_bReloaded = true;
mWebView.reload();
} else {
_bLoaded = true;
displayLoadingSpinner(false);
mWebView.setVisibility(View.VISIBLE);
}
} else if (mProgressBar.getVisibility() != View.VISIBLE) {
displayLoadingSpinner(true);
_bLoaded = false;
}
}
});
Another piece of info that isn't directly related to the original question, but may be useful is the following. Within my code, I had to resize and reposition a VideoView based on parameters that were sent from the webview. But because the webview may be in a different resolution than the actual display resolution, I had to adjust the parameters to make them in terms of the screen resolution.
For example:
//this parameter is equal to the width of the webview screen: 720
nHeightFromWebView;
//This is the x position of where the VideoView should be placed.
nXFromWebView;
float nScale = (float)_nWebScale/100;
int nAdjustedHeight = (int) Math.round(((float)nHeightFromWebView * nScale));
//The adjusted height computes to 1080 - the actual resolution of the screen
int nNewX = (int)Math.round((float)nXFromWebView * nScale);
Actually, I am bothering the problem of screen size of phonegap apps.
I have found a solution from this site (http://ryangillespie.com/phonegap.php).
By using javascript and the feature of css3, it works.
The code is following:
var designWidth = 480; // zoom to fit this ratio
var designHeight = 762; // not 800 b/c top bar is 38 pixels tall
var scaleChange = 1; // % change in scale from above #s
function zoomScreen() {
var docWidth = window.outerWidth;
var docHeight = window.outerHeight;
if (docWidth != designWidth) {
var scaleX = docWidth / designWidth;
var scaleY = docHeight / designHeight;
if (scaleX < scaleY) {
$('body').css('zoom', scaleX);
scaleChange = scaleX;
} else {
$('body').css('zoom', scaleY);
scaleChange = scaleY;
}
}
}
But, the problem is: it only behave correctly in the initial run. When I shut down the app and restart it, the screen size behave wrong.
There is also one more weird behavior. After I try to reload the page by
window.location.reload()
The screen size behaves correctly.
How to fix it?
This is my first post. If I miss some of the information, I would clarify.
That method might only be called when the app creates the view initially, depending on how you have the app working, there may be an 'onCreate' or 'onSurfaceCreated' method that is getting called to correctly set the changes. Then when you bring the app up again, the surface has already been created, and so that app doesn't try and re-create it and somehow it's not in the right state. If you determine that you need to reload the settings every time, have the app call that reload method in the 'onSurfaceModified', or 'onResume', or whatever, or try to figure out how the view is getting modified between runs.