Android overlapping layouts - android

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.

Related

Inflate ViewStub with a native widget

Is it possible to inflate a ViewStub with a native android widget?
For example:
<LinearLayout...>
<TextView...>
<TextView...>
<ViewStub...>
</LinearLayout>
I want to replace/inflate the ViewStub with a Switch or a Checkbox. Is that possible?
To set the layout to inflate into the ViewStub either:
Set the layout via XML:
<ViewStub
android:id="#+id/stub"
android:inflatedId="#+id/myLayout"
android:layout="#layout/myLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
Or, set the layout at runtime:
viewStub.setLayoutResource(R.layout.myLayout);
Then, you can inflate the ViewStub.
View inflatedView = viewStub.inflate();
You could call this inflate() method from where you want, such as a Switch or Checkbox being clicked (as you asked).
Currently there is no way to replace the StubView with a View object. You must create a layout representing the view you want to replace. So for example, if you want to switch it with a single CheckBox, you need to create a layout like this
<?xml version="1.0" encoding="utf-8"?>
<CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/my_checkbox"
android:text="Click me"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
Then calling
viewStub.setLayoutResource(R.layout.my_checkbox);
viewStub.inflate();
If you need a reference to the inflated view you can use this instead
CheckBox checkInflated = (CheckBox)viewStub.inflate();

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.

StackOverflowError when inflating custom view which contains its own views

I am having a Fragment, where I inflate "fragment_board.xml":
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<view
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.app.BoardView"
android:id="#+id/view_board"/>
</FrameLayout>
As you can see, fragment_board contains a custom view "BoardView", from where I want to load the following "view_board.xml":
<com.app.BoardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
android:fadingEdge="none"
android:requiresFadingEdge="none"
android:overScrollMode="always"
android:id="#+id/board_scrollview_vertical">
<android.widget.HorizontalScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scrollbars="none"
android:fadingEdge="none"
android:requiresFadingEdge="none"
android:overScrollMode="always"
android:id="#+id/board_scrollview_horizontal"
android:foregroundGravity="left">
</android.widget.HorizontalScrollView>
</com.app.BoardView>
My custom view contains two scroll views (I use it for panning), and I want to be able to re-use it in other layouts. The BoardView extends the outer (vertical) scroll view like this:
public class BoardView extends ScrollView
When I use it stand-alone, it inflates fine and I can findViewById() both scroll views in the inflated layout. But when I use it in a layout tree (such as a fragment), I run into problems.
In the fragment, I inflate the layout tree like this:
View view = inflater.inflate(R.layout.fragment_board, container, false)
From the fragment, I can findViewById() the outer (vertical) scroll view, but findViewById() returns null for the inner (horizontal) scroll view.
In BoardView, I inflate like this:
View view = inflate(getContext(), R.layout.view_board, this);
As said, I can find both scroll views fine when inflated by itself, but when inflated as part of the fragment, I get StackOverflowError.
It is clear why: I inflate in the fragment first, and then I inflate the same XML in the view again, which triggers another inflation of the view and so on. I get a cyclic reference here. Problem I can't figure out how I can add the inner (horizontal) scroll view to the already existing layout. I think I need to merge somehow or inflate layouts manually, but I can't figure out how.
Here are some refs (which didn't help in my case):
Android - Writing a custom (compound) component
How to inflate XML-Layout-File correctly inside Custom ViewGroup?
Create a custom View by inflating a layout?
InvocationTargetException on inflating an xml - android
Android: StackOverFlowError with InvocationTargetException when inflating layout
Can anybody suggest what I should do. If possible, I'd want BoardView to work as a generic component, which I can plug into a layout tree where needed.
As Android [according to one of the refs above] does not officially support composite components, would my best option be to drop XML layouts for the inner views and add them in code?
Please check if something like this fixes the issue:
fragment_board.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.app.BoardView
android:id="#+id/view_board"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
view_board.xml
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
android:fadingEdge="none"
android:requiresFadingEdge="none"
android:overScrollMode="always"
android:id="#+id/board_scrollview_vertical">
<android.widget.HorizontalScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scrollbars="none"
android:fadingEdge="none"
android:requiresFadingEdge="none"
android:overScrollMode="always"
android:id="#+id/board_scrollview_horizontal"
android:foregroundGravity="left">
</android.widget.HorizontalScrollView>
</merge>
More information on using MERGE and INCLUDE

layout_below does not work because of custom view

I have a custom view (MyView) which extends SurfaceView in which I override the onDraw method. I create an instance of this view dynamically with a custom constructor:
MyView myView = new MyView(...);
In this constructor I call the super(Context context) method.
After that, I wrap my custom view in a RelativeLayout like this:
((RelativeLayout) findViewById(R.id.container)).addView(myView);
And this is the layout file that I am using:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<RelativeLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Test"
android:layout_below="#+id/container"/>
</RelativeLayout>
The problem is that the TextView is at the top of the screen instead of being below the RelativeLayout (with the #+id/container id). As it will be without the android:layout_below property.
It behaves like my custom view (MyView) does not set its dimensions. I tried to use the setLayoutParams() but it did not change anything.
I think the problem is that your container view is inflated and laid out before your custom view is added to it and so gets a height of 0 since it has no content. After you add it to its container, a relayout is forced at which point the container asks the child view to measure itself. Since the height of the container is wrap_content, the child needs to report a specific height at this point. My guess is that your MyView class is not doing this.
An easy thing to do in order to set the height of your MyView objects is to override and implement the onMeasure() method.

Do not clip ViewPager pages

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);`

Categories

Resources