I need to create an interface like Google Newsstand which is a sort of ViewPager (horizontal scroll) over a collapsing header (vertical scroll). One of my requirements is to use the new Design Support Library presented at the Google IO 2015. (http://android-developers.blogspot.ca/2015/05/android-design-support-library.html)
Based on the sample created by Chris Banes (https://github.com/chrisbanes/cheesesquare) I've reach the point that I'm able to do the collapsing behaviour but with a basic LinearLayout (without horizontal scroll).
I tried to replace the LinearLayout by a ViewPager and I got a blank screen. I played with: width, weight and all kind of view groups but.... still a blank screen. It seems that ViewPager and NestedScrollView don't like each other.
I tried a workaround by using an HorizontalScrollView: it works but I loose the benefit of the PagerTitleStrip feature and the focus on a single panel (I can stop the horizontally between 2 panels).
Now I have no more ideas, if anyone can lead me to a solution...
Thanks
Here is my latest layout file :
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="@dimen/header_height"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleMarginEnd="64dp"
app:expandedTitleMarginStart="48dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<include
layout="@layout/part_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_collapseMode="parallax"/>
<android.support.v7.widget.Toolbar
android:id="@+id/activity_main_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:id="@+id/activity_main_nestedscrollview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.v4.view.ViewPager
android:id="@+id/activity_main_viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFA0"/>
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
ViewPager
inside a LinearLayout
? Just curious if that would render the ViewPager
properly.
ViewPager
inside a NestedScrollView
as the only layouts - seemed to work fine. I was also able to drop into a DrawerLayout
successfully. Perhaps there is another aspect of your code preventing the functionality you want. Care to share more of your source?
ViewPager
. I'm able to scroll the views up and down, as well as scroll the ViewPager
left/right.
I meet this problem ,i solve it by setFillViewport (true)
<android.support.v4.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:id="@+id/nest_scrollview"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="mio.kon.yyb.nestedscrollviewbug.ScrollingActivity"
tools:showIn="@layout/activity_scrolling">
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
in activity
NestedScrollView scrollView = (NestedScrollView) findViewById (R.id.nest_scrollview);
scrollView.setFillViewport (true);
https://i.stack.imgur.com/5GtM3.gif
Okay, I made a small demo with the ViewPager
and NestedScrollView
. The problem I faced was with the height of ViewPager
and the ListView
. So I did a little modification on ListView
and ViewPager's
height measuring.
If anyone would like to look into the code, Here is the link: https://github.com/TheLittleNaruto/SupportDesignExample/
Output:
https://i.stack.imgur.com/qKeaF.gif
NestedScrollView
and using a LinearLayout
instead. What would be the benefits of having a NestedScrollView
over a LinearLayout
if the whole purpose is to scroll the contents within the ViewPager? Please correct me if I'm wrong. :x Thanks
Add android:fillViewport="true"
in your NestedScrollView
. period.
Instead of placing ViewPager inside the NestedScrollView, do it the other way around.
Place the NestedScrollView in the child Views inside the ViewPager which in essence is the Fragment's layout. It's also very likely that you won't even need to use NestedScrollView if your Fragment's list layout is very simple.
Example Layouts
Activity's Layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="@dimen/app_bar_height"
android:fitsSystemWindows="true"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/toolbar_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="@dimen/app_bar_height"
android:scaleType="centerCrop"
android:src="@drawable/my_bg"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0.8"
app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed" />
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.design.widget.TabLayout
android:id="@+id/content_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
If you don't need the TabLayout, you can ditch the LinearLayout and make ViewPager standalone. But be sure to use app:layout_behavior="@string/appbar_scrolling_view_behavior"
in ViewPager attributes.
Simple Fragment's Layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Complex Fragment's Layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:fillViewport="true"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hello World"/>
</RelativeLayout>
</android.support.v4.widget.NestedScrollView>
Example App: CollapsingToolbarPoc
https://i.stack.imgur.com/uR6XF.gif
Had the same issues.
When putting the NestedScrollView around the ViewPager, it wouldn't work.
What DID work though is putting the NestedScrollView inside the layout of the fragment I'm loading inside the ViewPager.
<android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@id/fragment_detail"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="fill_vertical"
android:orientation="vertical">
...Content...
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
you can try like this, viewpager works fine, but the viewpager's height is fixed.
i am still finding the better solution ..., so please let me know any point can do it better, thanks
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="800dp"
android:orientation="vertical">
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
I had the same issue, and just fixed it with some changes. ViewPager
doesn't work within the NestedScrollView
. Simply put your ViewPager
as a direct child of your CoordinatorLayout
. Then the content for each ViewPager's Fragment should be enclosed in NestedScrollView
. That's it.
Just put this line in the NestedScrollView
android:fillViewport="true"
You dont need use the NestedScrollView to have the parallax effects. try something like this:
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true">
<android.support.v7.widget.Toolbar
android:id="@+id/my_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|enterAlways"/>
<android.support.design.widget.TabLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/my_tab_layout"
android:fillViewport="false"
app:tabMode="fixed"/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/my_view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>
ViewPager
fragments are ether RecyclerView
, or NestedScrollView
. If they aren't (e.g. ListView
), then a workaround for Lollipop and above is to call listView.setNestedScrollingEnabled(true)
I had a layout with an app toolbar with NestedScrollView below it, then inside nested toolbar was a LinearLayout, below the LinearLayout was a view pager. Idea was the LinearLayout inside the NestedScrollView was fixed - you could slide left/right between the views of the view pager but this content wouldn't move, horizontally at least. You should still be able to scroll all the content vertically because of the nested scroll view. That was the idea. So I set nested NestedScrollView height to match_parent, fill viewport, then all the child layouts/ViewPager to wrap_content.
In my head that was right. But it didn't work - ViewPager allowed me to go left/right, but no vertical scrolling, whether the viewpager page content pushed below the bottom of the current screen or not. Turns out it was basically because the ViewPager doesn't respect wrap_content across its various pages.
I got round this by subclassing the ViewPager to make it change height depending on the current selected item, sort of forcing it to behave like layout_height="wrap_content":
public class ContentWrappingViewPager extends ViewPager {
public ContentWrappingViewPager(Context context) {
super(context);
}
public ContentWrappingViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = 0;
if (getChildCount() > getCurrentItem()) {
View child = getChildAt(getCurrentItem());
child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int h = child.getMeasuredHeight();
if(h > height) height = h;
}
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
Solution was inspired by this answer. Worked for me!
Use below customized class to resolve your problem. Don't forget to call onRefresh() method on pagechangelistener.
public class ContentWrappingViewPager extends ViewPager
{
private int width = 0;
public ContentWrappingViewPager(Context context) {
super(context);
}
public ContentWrappingViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int height = 0;
width = widthMeasureSpec;
if (getChildCount() > getCurrentItem()) {
View child = getChildAt(getCurrentItem());
child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int h = child.getMeasuredHeight();
if(h > height) height = h;
}
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
public void onRefresh()
{
try {
int height = 0;
if (getChildCount() > getCurrentItem()) {
View child = getChildAt(getCurrentItem());
child.measure(width, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int h = child.getMeasuredHeight();
if(h > height) height = h;
}
int heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
ViewGroup.LayoutParams layoutParams = this.getLayoutParams();
layoutParams.height = heightMeasureSpec;
this.setLayoutParams(layoutParams);
} catch (Exception e) {
e.printStackTrace();
}
}
}
I removed NestedScrollView from the main layout and inserted as a Parent in all my Fragment layouts that were supposed to load in ViewPager fixed this issue for me.
I was having a similar problem (but without CollapsingToolbarLayout
, just a simple Toolbar
+ TabLayout
within the AppBarLayout
). I wanted the Toolbar to scroll out of sight when scrolling down the contents of a ViewPager
within the NestedScrollView
.
My layout went something like this:
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true">
<android.support.v7.widget.Toolbar
android:id="@+id/my_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|enterAlways"/>
<android.support.design.widget.TabLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/my_tab_layout"
android:fillViewport="false"
app:tabMode="fixed"/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:layout_gravity="fill_vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.v4.view.ViewPager
android:id="@+id/my_view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
</android.support.v4.view.ViewPager>
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
This layout wasn't working as intended, but I solved it by simply getting rid of the NestedScrollView
and using a LinearLayout
instead. It worked for me right away on Android Support Library v23.1.0. Here's how the layout ended up:
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true">
<android.support.v7.widget.Toolbar
android:id="@+id/my_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|enterAlways"/>
<android.support.design.widget.TabLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/my_tab_layout"
android:fillViewport="false"
app:tabMode="fixed"/>
</android.support.design.widget.AppBarLayout>
<LinearLayout
android:orientation="vertical"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="4dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.v4.view.ViewPager
android:id="@+id/my_view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
</android.support.v4.view.ViewPager>
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
PS: These were actually two layout files in my project. I copied and pasted them here and tried to structure them as it would look like in a single file. I'm sorry if I screwed up while copy-pasting but please let me know if that's the case so I can fix it.
Just add android:fillViewport="true" to NeastedScrollView xml tag
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
>
<androidx.viewpager.widget.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.core.widget.NestedScrollView>
The following should ensure the proper height of the view pager. The fragment is the first fragment provided by the view pager.
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<fragment
class="com.mydomain.MyViewPagerFragment"
android:id="@+id/myFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"/>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
I have the same wish, nest a ViewPager
inside a NestedScrollView
. But it doesn't work for me neither. In my case, the ViewPager
is inside a Fragment
so I can switch content depending on the interactions with the drawer. Of course, AppBar, collapse, etc and all new feature of support library must work.
If a user vertically scrolls a page in the pager, I want other pages to be scrolled the same, to give the user the overall impression that the pager itself scrolled.
What I did, which works, is that I no longer embed the ViewPager
inside a NestedScrollView
, instead, I embed the content of the contained fragments inside a NestedScrollView
.
Then, in case of a scroll in one fragment, I inform the container of the new scrolling position, which stores it.
Finally, on a swipe left or right detected from the pager (using addOnPageChangeListener
looking for drag states) I inform the target left or right fragment where it must scroll (based on the container knowledge of it) to be aligned from the fragment I come from.
I know one working solution: You use Activity, with CoordinatorLayout and use FrameLayout, container. Then. use fragment manager to put fragment in container. For example my code:
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:elevation="0dp">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
app:layout_scrollFlags="scroll|enterAlways"
android:layout_height="@dimen/toolbar_height"
android:background="?attr/colorPrimary"
android:gravity="center">
</android.support.v7.widget.Toolbar>
</android.support.design.widget.AppBarLayout>
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
</FrameLayout>
And Fragment:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.improvemdia.inmyroom.MainFragment">
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Then use FragmentManager
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, new MainFragment())
.commit();
LinearLayout
within the Fragment's layout is unnecessary here. See my answer, I used a LinearLayout
instead of a FrameLayout
in my Activity's layout, and my fragment's root is a ViewPager (no wrapping layout).
you just removed the NestedScrollView on your xml and add this code on your class
yourGridView.setNestedScrollingEnabled(true);
After spending hours tried all of the suggestions above, finally I made it work. The key is putting NestedScrollView
inside PageViewer
, AND set layout_behavior
to '@string/appbar_scrolling_view_behavior'
for BOTH views. The final layout is like
<CoordinatorLayout>
<ViewPager app:layout_behavior="@string/appbar_scrolling_view_behavior">
<NestedScrollView fillViewport="true" app:layout_behavior="@string/appbar_scrolling_view_behavior">
<Content>...</Content>
</NestedScrollView>
</ViewPager>
<AppBarLayout>...</AppBarLayout>
</CoordinatorLayout>
Actually the NestedScrollView doesn't need to to be direct sibling of CollapsingToolbarLayout in CoordinatorLayout. I have achieved something realy bizzare. The appbar_scrolling_view_behavior works in 2 nested fragments.
My activity layout:
<android.support.design.widget.CoordinatorLayout>
<android.support.design.widget.AppBarLayout>
<android.support.design.widget.CollapsingToolbarLayout>
<include
layout="@layout/toolbar"/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<FrameLayout
android:id="@+id/container"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>
In the "container" frameLayout I have inflated this fragment:
<android.support.constraint.ConstraintLayout>
... some content ...
<android.support.v4.view.ViewPager/>
...
</android.support.constraint.ConstraintLayout>
And fragments inside that ViewPager looks like this:
<RelativeLayout>
<android.support.v7.widget.RecyclerView/>
....
</RelativeLayout>
Fact that NestedScrollView can be so deep in the CoordinatorLayout children hierarchy and scroll behaviors stil works did suprised me.
Success story sharing
android:fillViewport="true"
to theNestedScrollView
object. The documentation describes this attribute asDefines whether the scrollview should stretch its content to fill the viewport.
.android:fillViewport="true"
onNestedScrollView
and inside thefragment
I have alistView
where I satandroid:nestedScrollingEnabled="true"
.. Fixed the scrolling issue