Do not clip ViewPager pages - android

I can't get my ViewPager not clipping its pages contents.
I'm currently using ViewPager with a FragmentStatePagerAdapter in my application. I have 3 pages, each page is showing a different fragment.
Everything works fine, but now I want to be able to draw outside of my pages bounds, like in this image:
I set clipChildren and clipToPadding to false in my all views parents to the views that needs to draw outside their bounds, but here's what I get : my views get clipped according to pages bounds.
Some additional info : In order to fix that issue, I used hierarchyviewer to check my view hierarchy and see if some "under the hood" views could have a clipping behaviour. And bingo : each of my fragments' root views is added under a NoSaveStateFrameLayout. I suspect this guy to clip its children...
Is my last assumption correct in your opinion? How would you do to get unclipped pages?

There are a few different Views that require disabled clipping. First, the FrameLayout that is at the root of the ViewPager needs to have clipping disabled. You can do this wherever you initialize your view pager. You also need to set the off-screen page limit to 2 to allow for an extra fragment to be loaded during a swipe. Without this, you'll see a pop going from 1->2 as 3 loads and adds it's overflow content to 2 once 2 has finished loading (this loads both 2 and 3 ahead of time).
mViewPager.setClipChildren(false);
mViewPager.setClipToPadding(false);
mViewPager.setOffscreenPageLimit(2);
Second, the XML you inflate in your FragmentStatePagerAdapter needs to include the appropriate tags or it can be done programmatically. Last, you need to force the NoSaveStateFrameLayout to not clip as well. This can be done after the view has been created in onViewCreated. Here's an example fragment:
public static class PlaceholderFragment extends Fragment {
public static PlaceholderFragment newInstance() {
PlaceholderFragment fragment = new PlaceholderFragment();
return fragment;
}
public PlaceholderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
LinearLayout rootView = (LinearLayout) inflater.inflate(R.layout.fragment_main, container, false);
//This can be done in XML
rootView.setClipChildren(false);
rootView.setClipToPadding(false);
return rootView;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//This is the NoSaveStateFrameLayout - force it to not clip
FrameLayout frameLayout = (FrameLayout) getView();
frameLayout.setClipChildren(false);
frameLayout.setClipToPadding(false);
}
}
I have tested this solution and am certain it works.
Full activity example:
https://gist.github.com/grantamos/d664f01a30f9ad97ba53

I had the same problem. I fixed this by adding
android:clipChildren="false"
android:clipToPadding="false"
to ViewPager and ViewGroup which is parent of ViewPager
For instance, if you have a layout like that:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:clipChildren="false"
android:clipToPadding="false"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.view.ViewPager
android:clipChildren="false"
android:clipToPadding="false"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</FrameLayout>
</RelativeLayout>
then only LinearLayout and ViewPager should have
android:clipChildren="false"
android:clipToPadding="false"

Have you tried to use a negative value for the margin between pages (using ViewPager.setPageMargin(int))? You will also have to set offscreen page limit to at least 2 (using ViewPager.setOffscreenPageLimit(int)).

Try this:
#Override
public float getPageWidth(int position) {
return 0.80f;
}

This is working for me perfectly
1. (Parent Layouts).setClipChildren(false);
(Parent Layouts).setClipToPadding(false);
2. `mViewPager.setPagerMargin(dp)`
3. `mViewPager.setOffscreenPageLimit(numberOfPage);`

Related

Android: tablayout/viewpager fragments within fragment or activity

I have a project I am working on in Android. I have one big fragment that covers the whole of the screen in my main activity. Normally I for each ui screen the user uses I would just do a replace command and swap the fragments for each screen.
In my current one I want to put a tablayout/viewpager combo with three different fragments and depending on what tab is pushed I will show a new fragment. Problem with this is that is a tablayout showing three different fragments inside another fragment. Is this even possible or recommended? and if I have to change it to a new activity. Would the navigation drawer act normally and just shut as I loaded the activity or is there going to be a problem there as well.
Thanks for your help
You can check below official documentation for the same or can use my demo snippet:
https://developer.android.com/reference/android/support/v4/app/FragmentTabHost.html
Try to do this to handle the Tabs in main fragment:
public class MainFragment extends Fragment {
private FragmentTabHost mTabHost;
//Mandatory Constructor
public MainFragment() {
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_tabs,container, false);
mTabHost = (FragmentTabHost)rootView.findViewById(android.R.id.tabhost);
mTabHost.setup(getActivity(), getChildFragmentManager(), R.id.realtabcontent);
mTabHost.addTab(mTabHost.newTabSpec("fragmentb").setIndicator("Fragment B"),
FragmentB.class, null);
mTabHost.addTab(mTabHost.newTabSpec("fragmentc").setIndicator("Fragment C"),
FragmentC.class, null);
mTabHost.addTab(mTabHost.newTabSpec("fragmentd").setIndicator("Fragment D"),
FragmentD.class, null);
return rootView;
}
}
With this tabs layout:
<android.support.v4.app.FragmentTabHost
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#android:id/tabhost"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/realtabcontent"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
</LinearLayout>
Yes, TabLayout with viewPager will maintain it's different fragments in the memory.
Since, there are going to be only 3 tabs in your case it won't affect your memory performance, you can go with pagerAdapter.
You can reuse a single fragment for other tabs, but as i said since there are only 3 tabs, it won't affect your performance.
And Yes you can have tabLayout inside your main fragment, you main fragment is a part of your activity right, it will follow the same life cycle as the hosting activity except few other callbacks.

Fragment layout not fully inflating upon FragmentTransaction.add/replace

I am attempting to replace a nested fragment inside a tabLayout fragment.
My app structure follows this pattern.
Here is the XML for the Tab Fragment 2
<FrameLayout
android:id="#+id/scene_root"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".Home"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<FrameLayout
android:id="#+id/shows_fragment_list"
android:tag="list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<FrameLayout
android:id="#+id/shows_fragment_details"
android:tag="details"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<FrameLayout
android:id="#+id/shows_fragment_info"
android:tag="info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</FrameLayout>
I am using this code to add the NestedFragment to the TabFragment FrameLayout
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View root = inflater.inflate(R.layout.shows_fragment_layout, container, false);
ShowDetailsFragment newNestedFragment = new ShowDetailsFragment();
android.support.v4.app.FragmentManager fragmentManager = getChildFragmentManager();
android.support.v4.app.FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction
.add(R.id.show_fragment_details, newNestedFragment)
.addToBackStack("null")
.commit();
return root;
}
But when I add the Nested Fragment to the Tab Fragment, only the last part of my layout appears, in this case it is a list view (highlighted in red), but it always seems to be the which ever element is declared last when the layout is inflated by the corresponding Java file.
When I inflate these layouts as there own activity, there are no inflation problems, so it must relate to my implementation of fragments, any ideas where I am going wrong?
A good idea is always to use the Android Device Monitor to debug your layout.
I would also add some logging on the onCreateView of your fragments.
Ok so my problem was that in my XML layout for my ShowDetailsFragment (The Nested Fragment) I had a FrameLayout as the root, but there were two separate RelativeLayouts as children, meaning only the last element was being displayed, the problem was solved by nesting these two Relative Layouts in another single Relative Layout.
Props go to ctarabusi for pointing me in the right direction, although I am still unsure why this only caused a problem when inflating as a Fragment and not as an Activity.

Inflating a View inside a Layout after adding a MapFragment to the same layout weird behavior

I have the following parent layout for an activity:
<?xml version="1.0" encoding="utf-8"?> <ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
I also have the following layout for a Fragment that goes into that ViewPager:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<FrameLayout
android:id="#+id/mapLayout"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
</RelativeLayout>
</LinearLayout>
At runtime the FrameLayout gets populated on the onCreateView() method of the Fragment like this:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
ViewGroup MapView = (ViewGroup) inflater.inflate(R.layout.frament_mood_map, container, false);
if(savedInstanceState == null) {
Fragment mapFragment = new SupportMapFragment();
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
//Adding map fragment to the FrameLayout.
transaction.add(R.id.mapLayout, mapFragment, "map").commit();
}
//Adding the mood panel.
ViewGroup mapLayout = (ViewGroup) MapView.findViewById(R.id.mapLayout);
mapLayout.addView(inflater.inflate(R.layout.mood_panel_layout, null));
...
return MapView;
}
The expected behavior is that since the container that gets populated by the Fragment and the View is a FrameLayout, the former gets added first to the container, and then the later would be on top of the map, I want to show a map with a little panel on top which has buttons, I cannot define the Fragment inside the XML because (citing the docs, and yes I've tried to do it):
Note: You cannot inflate a layout into a fragment when that layout
includes a . Nested fragments are only supported when added
to a fragment dynamically
What am I doing wrong? how can I workaround this?
After hours of fiddling and trying to find a workaround I found out that there's a updated MapView on Google Maps Android API v2, so for those having the same issue where you need to nest within a ViewPager page fragment a MapFragment just use the MapView.
I found about MapView in the following SO question: Should I use MapView or MapFragment

YoutubePlayerView in Fragment

I have a flexible UI layout. One fragment in handheld devices and 2 fragments in a tablet. In the Details Fragment, this will contain a youtube video and some other views. I am using Youtube API to do this.
I have tried using YoutubePlayerSupportFragment defined in the xml file and it works fine on first load. But when going back to the List view and then reopening the details view, I get a duplicate ID error. This is because the onCreateView for the DetailsFragment is loading the layout again.
I tried to replace this with FrameLayout and dynamically add the YoutubeFragment which solved the problem above. However, when changing orientation, it reloads the entire fragment and the youtube video is restarted from the beginning.
What I want is to have the youtube video inside the fragment and handle screen changes (like orientation or fullscreen mode) without the video rebuffering. There was a sample app provided in android developer page to accomplish this but they are using YoutubePlayerView in the XML file which requires the container to be an activity inheriting from YoutubeBaseActivity which doesn't work in my situation since I'm using a Fragment. What is the proper way of implementing this?
DetailFragment.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView .. />
<fragment
android:id="#+id/youtube_fragment"
android:name="com.google.android.youtube.player.YouTubePlayerSupportFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<Button ... />
</LinearLayout>
DetailFragment.java
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater
.inflate(R.layout.DetailFragment, container, false);
...
YouTubePlayerSupportFragment youTubeView = (YouTubePlayerSupportFragment) getActivity()
.getSupportFragmentManager().findFragmentById(
R.id.youtube_fragment);
youTubeView.initialize(Utilities.DEVELOPER_KEY, this);
return view;
}

Android overlapping layouts

I has a Sliding ViewPager with about 5 pages. Each layout is inflated like this:
public static class SectionFragment extends Fragment {
...
#Override
public View onCreateView(LayoutInflater inflater, ...) {
...
rootView = inflater.inflate(R.layout.gridpage1,container,false);
...
}
Now I want to check if a condition is true and if Yes, I want to inflate the gridpage1 layout first, then another layout on top of it.
How can i do this? All I need is the help on inflating two views one on top of the other.
Inflating a view basically just means creating it from an XML file and returning it.
In your specific case, you just need to return your fragment content view from the onCreateView function. This must be a single view, so if your condition is true and you want 2 views do the following:
Create a FrameLayout view yourself programmatically
Something like: FrameLayout frameLayout = new FrameLayout(context);
Add the 1st view to your FrameLayout after inflating it
Either do frameLayout.addView(inflater.inflate(R.layout.gridpage1,frameLayout,false));
or even inflater.inflate(R.layout.gridpage1,frameLayout,true); is enough because true tells it to add the view to the container.
Add the 2nd view to your FrameLayout after inflating it
Return the FrameLayout from your onCreateView
Addition:
How to save the reference to each view:
Option 1:
View v1 = inflater.inflate(R.layout.gridpage1,frameLayout,false);
this.v1Reference = v1;
frameLayout.addView(v1);
Option 2:
inflater.inflate(R.layout.gridpage1,frameLayout,true);
this.v1Reference = frameLayout.findViewById(...);
You can use the <include /> tag in your main layout and then hide/show the views you want with setVisibility(View.GONE/VISIBLE).
For example:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hello_world" />
<include android:id="#+id/gridpage1_layout" layout="#layout/gridpage1"/>
<include android:id="#+id/gridpage2_layout" layout="#layout/gridpage2"/>
...
</RelativeLayout>
And in your Fragment, you can inflate only the root layout and find the other views by ID.

Categories

Resources