I am creating a magazine app which uses a ViewPager and scrolls over different WebViews.
Most of the pages are portrait but some of them can be seen as landscape.
To handle this, I am using two methods in the Activity holding the ViewPager:
public void allowOrientationChanges() {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
}
public void enforcePortrait() {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
With this we make sure that all the pages that just need to be portrait stay portrait (calling getActivity().enforcePortrait()) and the ones that have portrait/landscape views goes through the whole restart process (calling getActivity().allowOrientationChanges()).
We reached a point where we want to add video to some of the magazine's pages. Unfortunately the HTML5 video tag isn't working fine so we added a VideoView to our layout.
The feature to add now is:
in portrait: Showing the video in a certain place. (Done!)
in landscape: Making the video full screen (like the youtube app).
I have been trying to do this and the only way I found is changing the size of the VideoView in the onConfigurationChanged() method in the Activity and notifying the fragment that the configuration changed. The problem with this is adding android:configChanges="orientation|keyboardHidden" to the Activity will "disallow" the views to be recreated.
The two possible solutions I thought of:
Changing the android:configChanges from the code and depending on the page but I couldn't find if that call exists.
Forcing a recreate on the onConfigurationChanged() method. I don't know how to force a recreation of the fragment.
Any ideas?
Maybe there is a way around it by defining the activity twice in the manifest with similar names and setting the second incarnation to enabled=false. Then switch the 2 activities around dynamically by calling PackageManager.setComponentEnabledSetting(....) it is just an idea though, not sure it if will work.
I am creating a magazine app which uses a ViewPager and scrolls over different WebViews.
Is this reliable?
Most of the pages are portrait but some of them can be seen as landscape.
Please allow all pages to be viewed in landscape. If your users want to view your app in landscape -- for example, they do not feel like turning their television on its side just to use your app -- please let them.
Any ideas?
In onResume() of the activity, have it notify the magazine page fragment to fix up its VideoView size/position based on the then-current Configuration (getResources().getConfiguration()).
I just fixed it. Thanks for your answers.
My solution was to make the Activity have the android:configChanges="orientation|keyboardHidden" flag.
After doing that I override the onConfigurationChanged() method inside the Fragment where I resize the WebView or the VideoView depending on what I need.
EDIT:
I just did a blog post about this.
Related
I am creating an app on android studio. The app is working fine but whenever I rotate the screen, the layout is changing, the items on the screen are getting overlapped and sometimes the app restarts.
What are the possible solutions? Is preventing the screen from rotation a good option? Please help. Thanks in advance.
You need to set orientation in AndroidMenifest.xml.Use keyboardHidden attribute if you are getting any issue in keyboard.
You can set you activity's screen orientation to portrait, by this your activity will never rotate, but if you want to support landscape version then you have to create a landscape layout as well and put the layout in layout-landscape folder, but you have to take care of your data also.
Choice is yours
You can prevent your activity to recreate when orientation changes. Just add this in your activity tag in Manifest file.
<activity
android:name=".YOUR_ACTIVITY_NAME"
android:configChanges="keyboardHidden|orientation|screenSize" />
Android Activites start from the beginning when you rotate a device. So your Activity Lifecycle methods like
onCreate(), onStart(), onResume()
will be called again.
There are two possible solutions, the first one is by Rob here.
<activity
android:name=".ACTIVITY"
android:configChanges="keyboardHidden|orientation|screenSize" />
This will handle the orientation changes itself.
Another solution is to create different layout types in different layout folders. The folders will be
layout-land, layout-sw600dp-land(for 7inch tablets) and layout-sw720dp-land(for 10inch tablets)
So when you rotate a device, the layouts in this folders will be automatically inflated by the system. There will be cases like when you want to preserve the current state of the activity, for this refer to this link
No, preventing the orientation to be landscape is not a good solution. Many tablets are landscape-only, and your app won't display correctly on those.
The correct thing to do is to save your state in onSaveInstanceState() and then read it in onCreate() when the activity is re-created (the savedInstanceState variable will not be null). For this to work, the data that you are saving must be Serializable or Parcelable.
If you want to save non-serializable data or you have something running in the background, you can use a Fragment with setRetainInstance(true); and no layout. See this link.
The view will be created again, yes, but you will set the saved values so that it will look as it was before the rotation.
Application has two fragments: the first one contains a small representation of pager with photos, and the second one contains full screen pager. The second fragment replaces the first and passes a page number to the previous every time it changes. I made connection between my fragments just like Android Developers says.
Everything works till device orientation doesn't change. The first fragment is not recreated until it is not on top of stack, that is why all page number changes after that are missed for first fragment.
I do not really what to disable views destroy on orientation change, but looks like it is the only way.
What is the best solution?
In your manifest file in your parent activity in which fragments are write following line :
android:configChanges="keyboardHidden|screenSize|orientation"
Let me know if that works for you. Best of luck :)
When you change the orientation, the activity gets rebuild, thus essentially destroying the fragment that was built before the change in orientation.
Perhaps you would wish to refer to this https://developer.android.com/guide/topics/resources/runtime-changes.html on retaining the state during a change in configuration.
I tried to solve this problem, searched through the Internet, no real answer was found.
I'm developing an app where the YouTube player is needed. This player is the YouTubePlayerSupportFragment since it's in a support fragment (so it's also nested). After the initial setup I realized that whenever I rotate the phone, the video stops playing and it has to be restarted again. This is obviously not what I want.
Then I found some SO answers (like this) where people say that the Activity needs to handle the config changes, so I added android:configChanges="keyboardHidden|orientation|screenSize" to the Activity in the manifest. This solved the problem, the video now keeps playing even on orientation change and the full-screen rotation doesn't look ugly. Cool, I said, only to realize that this messed up big time the other parts of the app because the config is now being updated after all the views are in place. This means that if my phone was in portrait mode and I rotated it to landscape, the layout being used was still going to be the portrait one.
I found solutions for this suggesting calling setContentView(...) in the Activity's onConfigurationChanged(...) and reinflating the view in the Fragments, but this seems to be a rather cumbersome if not terrible solution as It's not just a simple content setting I need, the fragments need to be retained (e.g. scroll position in RecyclerView) too. The built-in setRetainInstance(true) does not work in this case as the view would be recreated with that either, but the config is still the old one when the onCreateView(...) is being called.
It would be nice if I could catch the configChanges events if the user is on that screen (fragment) only but retain Activity recreation anywhere else.
Don't suggest using more Activities, I cannot use a separate Activity for this screen and the rest of the app because of UI/UX reasons.
As per this YouTube Android Player API Guidelines
It is encouraged that you handle configuration changes caused by
orientation in your activity manually, to avoid recreating this
fragment's view every time the configuration changes. For the best
user experience, you should also manually handle the fullscreen event
by changing the layout of your activity.(See the second approch)
As you are already handling on configuration change on your own, I can see two possible solutions of your problem.
Set setFullscreenControlFlags (To your YouTubePlayer object) to FULLSCREEN_FLAG_ALWAYS_FULLSCREEN_IN_LANDSCAPE (This will causes the player to automatically enter fullscreen whenever the device enters landscape orientation.) in conjunction with FULLSCREEN_FLAG_CONTROL_ORIENTATION(To enable automatic control of the orientation.) flag.
Note that this flag should only be set if your activity is locked in
portrait (and is specified as such in the manifest).
The flag is useful if you don't have a landscape layout for your
activity, but would like to enable landscape orientation solely for
fullscreen video watching.
This approach might solve your problem and it is also suggested by official documentation as I mentioned above. In setFullscreenControlFlags method set FULLSCREEN_FLAG_CUSTOM_LAYOUT, that disables the default fullscreen layout handler, enabling you to control the transition to fullscreen layout manually. As well as enabling you to implement a custom fullscreen layout, this option also provides the advantage of avoiding the rebuffering that occurs in the default fullscreen behavior.
An application implementing custom fullscreen behavior should ensure
two things
That the player's view takes up the whole available space of
application's window whenever onFullscreen(boolean) is called. That
the activity is not recreated when the orientation changes to
landscape. To achieve this for an activity that supports portrait, you
need to specify that your activity handles some configuration changes
on its own in your application's manifest, including orientation,
keyboardHidden and screenSize.
See here on how to use this flag.
As per the first link I shared, this second approach will avoid recreating fragment's view every time the configuration changes. Also you don't need to call setContentView(...) in onConfigurationChanged(...).
Hope this will help you.
I have a problem with switching activities & fragments on certain device sizes, when an orientation change happens. In my case it's large screens, but it could happen with other screen sizes depending on the app. I've looked around for answers, but nothing seems to properly address this.
I have two activities, MainActivity and SubordinateActivity. MainActivity is the only entry point to the app; MainActivity launches SubordinateActivity. Each activity has its own fragment, MainFragment and SubordinateFragment. When running on normal devices, there is only enough space on the screen for one fragment at a time, regardless of the orientation. In that case, each activity will manage its own fragment. On xlarge devices, there is enough space for two fragments, regardless of the orientation. In this case, there are different layout files which allow for two fragments on the screen. Both MainFragment and SubordinateFragment are managed by MainActivity (SubordinateActivity is never used).
The trouble arises with large screens. Using landscape orientation, there's enough space for two fragments, but in portrait orientation there is not. I have the proper layout files for each. In landscape mode, MainActivity manages both fragments (as with xlarge devices) and in portrait mode, each activity manages its own fragment (as with normal devices). This produces problems in two scenarios:
SubordinateActivity is loaded in portrait mode, and the orientation changes to landscape mode. What I want: SubordinateActivity should be discarded and MainActivity should load, with the content previously displayed by SubordinateActivity, displayed in its own SubordinateFragment. Problem: SubordinateActivity stays loaded by itself in landscape mode.
MainActivity is loaded with MainFragment and SubordinateFragment in landscape mode, and the orientation switches to portrait. What I want: The content previously displayed in SubordinateFragment should now be displayed alone by SubordinateActivity. Problem: MainActivity is shown with only the content from MainFragment.
A good example of this problem is the GMail app. Here are some screen shots from that app in case it's not clear what I'm talking about. I realize the UI of the GMail app is actually more complicated than mine, but the problem is the same.
I'm sure that this is a problem others have encountered because the GMail developers encountered it too. I can't figure out what a good solution is, because every possibility seems to involve either violating the Android UI best practices, or creating some ungodly tangle between Activity code and XML layouts.
Here are some ideas I have, none of which seems really correct:
Detect the orientation change in both activities, and launch the other activity (eg, by using FLAG_ACTIVITY_CLEAR_TOP) to go back down the stack and load a previously loaded activity, with a new intent. This is a problem because the orientation change code should only be executed on large devices, which means mingling code that checks which layouts are available with the activity code.
Drop SubordinateActivity altogether. It seems a bit superfluous and MainActivity can manage the fragments itself, even on normal size devices where it can just swap MainFragment and SubordinateFragment as necessary. In the end I don't think this solves the problem since MainActivity still relies on the layout files to tell it which and how many fragments to display. This also would violate the principle that an Activity represents a discrete thing that the user does.
Here are some resources I've been looking at to try to solve this problem. As I said, this seems to be a common problem, but there seems to be no canonical Android solution. This is a bit of a shortcoming since the documentation recommends using Fragments, and every developer who does, is going to run into this problem.
Tasks and Back Stack
How to Controll Android back stack
How to start a new activity when screen orientation changes? Android
onNewIntent() lifecycle and registered listeners
Multi-pane Layouts
Switch from dual pane to single pane on orientation change maintaining fragment stack
android Fragment issue with orientation change
Fragments restore state on orientation changed
Summary: I have the multi-pane mode working for xlarge devices. The problem I'm trying to solve is switching between single pane (portrait) and multi-pane (landscape) mode on large devices that can only handle multiple panes in landscape orientation.
Kill SubordinateActivity and have the only entry point in your application be MainActivity which then decides to either fill the entire viewport with MainFragment or SubordinateFragment. You can find an example on how to do this by looking at the developer docs for the design guide you linked to.
After you do this, you can create a resource folder specific for large and landscape, res/layout/large-land as stated here and set up your layout file for MainActivity to include two fragments similar to lesson #2 in the link from the previous paragraph.
Creating that, alongside your normal XML layouts changing to what's described in that doc should automatically handle everything you're describing since an orientation change is technically an activity re-creation.
What I expected in my application is data coming from the server and everything, even the view, are dynamically created. The activity layout is divided into two parts. On the left is a list and on the right side are dynamic fragment changes. Whenever the orientation changes the server call is made again (As the activity is destroyed and re-created). This is working fine.
There are 3 fragments(2 are Maps) that have to be called from inside of each other on the right side of the layout.
Now I dont want this thing to happen. I don't want everything to refresh again and start from scratch.
I can't set configChange in the manifest, as the image sizes in the layout for landscape and portrait are somewhat different.
What is needed is a layout change on orientation change without re-creating the activity.
I have tried using onSaveInstanceState() and onRetainNonConfigurationInstance() but couldn't get it to work.
I think your solution is to set the configChange on your manifest AND create different layouts for portrait and landscape so you can have the results you are expecting: No refresh of the activity, and control the sizes of your ImageViews.