问题
This is my layout:
I want to be able to scroll upper parth of the layout (with name, image, textviews for followers/following...) when I scroll recyclerView in viewPager, so I read that I can use NestedScrollView to implement that, so I put NestedScrollView as my root layout:
NestedScrollView:
-----------------ConstraintLayout:
---------------------------------rest of the views as in picture above
I also added android:fillViewport="true" in NEstedScrollView because that solves a broblem of missing view pager. So the problem is that scroll view doesn't work, everything is same as before uting nestedScrollView , bottom part of my layout doesn't scroll of the screen while I scroll recyclerView. Doesn anyone know why, am I using nested scrollView wrong, I haven't found any example that can solve my problem and documentation is really bad. XML code for my layout:
<androidx.core.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:layout_height="match_parent"
android:fillViewport="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:theme="?attr/actionBarTheme"
android:minHeight="?attr/actionBarSize"
android:elevation="4dp"
android:id="@+id/title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:title="UserName"
app:layout_constraintEnd_toEndOf="parent"/>
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/profile_image"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_margin="8dp"
app:srcCompat="@drawable/ic_profile_24dp"
tools:src="@drawable/ic_profile_24dp"
app:civ_border_color="@color/primaryLightColor"
app:civ_border_width="1dp" android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@+id/title" android:layout_marginStart="8dp"
app:layout_constraintStart_toStartOf="parent" android:layout_marginLeft="8dp"/>
<TextView
android:layout_width="0dp"
android:layout_height="0dp"
android:id="@+id/textView"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="8dp" android:layout_marginRight="8dp"
app:layout_constraintStart_toEndOf="@+id/profile_image" android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
app:layout_constraintBottom_toBottomOf="@+id/profile_image"
app:layout_constraintTop_toTopOf="@+id/profile_image"
tools:text="Short description "/>
<TextView
android:id="@+id/questions_label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/questions"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@+id/profile_image" app:layout_constraintStart_toStartOf="parent"
android:layout_marginLeft="8dp" android:layout_marginStart="8dp"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintEnd_toStartOf="@+id/followers_label"/>
<TextView
android:id="@+id/questions_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="18sp"
android:textStyle="bold"
tools:text="0"
app:layout_constraintTop_toBottomOf="@+id/questions_label"
app:layout_constraintEnd_toEndOf="@+id/questions_label"
app:layout_constraintStart_toStartOf="@+id/questions_label"/>
<TextView
android:id="@+id/followers_label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/followers"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@+id/profile_image"
app:layout_constraintStart_toEndOf="@+id/questions_label" android:layout_marginStart="8dp"
android:layout_marginLeft="8dp" android:layout_marginEnd="8dp"
app:layout_constraintEnd_toStartOf="@+id/following_label" android:layout_marginRight="8dp"
app:layout_constraintHorizontal_bias="0.5"/>
<TextView
android:id="@+id/following_label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/following"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@+id/profile_image" android:layout_marginEnd="8dp"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginRight="8dp"
app:layout_constraintStart_toEndOf="@+id/followers_label" app:layout_constraintHorizontal_bias="0.5"/>
<TextView
android:id="@+id/followers_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="18sp"
android:textStyle="bold"
tools:text="0"
app:layout_constraintEnd_toEndOf="@+id/followers_label"
app:layout_constraintStart_toStartOf="@+id/followers_label"
app:layout_constraintTop_toBottomOf="@+id/followers_label"/>
<TextView
android:id="@+id/following_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="18sp"
android:textStyle="bold"
tools:text="0"
app:layout_constraintEnd_toEndOf="@+id/following_label"
app:layout_constraintStart_toStartOf="@+id/following_label"
app:layout_constraintTop_toBottomOf="@+id/following_label"/>
<com.google.android.material.tabs.TabLayout
android:id="@+id/sliding_tabs"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@color/primaryLightColor"
app:tabMode="fixed" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@+id/followers_number"/>
<androidx.viewpager.widget.ViewPager
android:id="@+id/view_pager"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@android:color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/sliding_tabs" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
EDIT: code after suggestion:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.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">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:theme="?attr/actionBarTheme"
android:minHeight="?attr/actionBarSize"
android:id="@+id/title"
tools:title="UserName"/>
</com.google.android.material.appbar.AppBarLayout>
<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.constraintlayout.widget.ConstraintLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/profile_image"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginStart="@dimen/spacing_normal"
android:layout_marginLeft="@dimen/spacing_normal"
android:layout_marginTop="@dimen/spacing_normal"
app:civ_border_color="@color/secondaryDarkColor"
app:civ_border_width="1dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_profile_24dp"
tools:src="@drawable/ic_profile_24dp" />
<com.google.android.material.textview.MaterialTextView
android:layout_width="0dp"
android:layout_height="0dp"
android:id="@+id/description"
android:layout_marginEnd="@dimen/spacing_normal"
android:layout_marginRight="@dimen/spacing_normal"
android:layout_marginLeft="@dimen/spacing_normal"
android:layout_marginStart="@dimen/spacing_normal"
app:layout_constraintStart_toEndOf="@+id/profile_image"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintBottom_toBottomOf="@+id/profile_image"
app:layout_constraintTop_toTopOf="@+id/profile_image"
tools:text="Short description "/>
<include
layout="@layout/qff_layout"
android:id="@+id/qff_layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_small"
app:layout_constraintTop_toBottomOf="@+id/profile_image"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<com.google.android.material.tabs.TabLayout
android:id="@+id/sliding_tabs"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@color/primaryLightColor"
app:tabMode="fixed"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/qff_layout"/>
<androidx.viewpager.widget.ViewPager
android:id="@+id/view_pager"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@android:color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/sliding_tabs" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
What is wrong here, It doesn't make any sense, nothing scrolls except ViewPager
回答1:
As you know NestedScrollView has one child and the height of his child must be wrap_content
if you want scroll behavior for your layout.
In your first layout, you must set the height of your ConstraintLayout
to wrap_content
but if you want your layout to be match_parent
for whatever reason (such as you want to bound your ViewPager in the bottom of your layout) you must use suggested answer(it means you must use CoordinatorLayout
) but with the following consideration:
1- You should put all your stuff you want to scroll in the AppbarLayout
.This layout extended from LinearLayout
so you can easily put your stuff into it. from your question, I think you want to scroll all stuff above your ViewPager
but you may change it as you want.
(Note: actually, I recommend using CollapsingToolbar
as a child of your AppbarLAyout
and then put your stuff into it but for your question, this is work perfectly too)
2- Add app:layout_scrollFlags="scroll"
attribute in the root of your stuff within your AppbarLayout
3- Add app:layout_behavior="@string/appbar_scrolling_view_behavior"
attribute to the sibling of your AppbarLayout
. In your question, you use this attribute in your NestedScrollView
.
This is my code:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.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">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
app:layout_scrollFlags="scroll"//I forget too add this, so I edit my answer
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
android:theme="?attr/actionBarTheme" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll"
android:orientation="vertical">
<View
android:id="@+id/profile_image"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginStart="10dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/description"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="@dimen/spacing_normal"
android:layout_marginLeft="@dimen/spacing_normal"
android:layout_marginEnd="@dimen/spacing_normal"
android:layout_marginRight="@dimen/spacing_normal"
app:layout_constraintBottom_toBottomOf="@+id/profile_image"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/profile_image"
app:layout_constraintTop_toTopOf="@+id/profile_image"
tools:text="Short description " />
<include
layout="@layout/qff_layout"
android:id="@+id/qff_layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/profile_image" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/sliding_tabs"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="#559735"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/qff_layout"
app:tabMode="fixed" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.viewpager.widget.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/sliding_tabs" />
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
I didn't try this code but I'm pretty sure this is work for you :)
回答2:
In NestedScrollView put
app:layout_behavior="@string/appbar_scrolling_view_behavior"
回答3:
Have you tried this one :
<CoordinatorLayout>
...<AppbarLayout>
......<Toolbar/>
...</AppbarLayout>
...<NestedScrollView> (set height and weight to match parent, then set fillViewPort="true" and for the last give layout_behavior="@string/appbar_scrolling_view_behavior")
......<ConstraintLayout> (change height to wrap_content because you don't need to set match parent, its already handled by nestedscrollview as a parent)
.........(All Child Components)
......</ConstraintLayout>
...</NestedScrollView>
</CoordinatorLayout>
From official documentation FillViewPort as an NestedScrollView Attributes is to stretch content's height to the viewport boundaries. Please check official document for better explanation : NestedScrollView - FillViewPort
UPDATE : Based on your current code I make some change, please check it.
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.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">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:theme="?attr/actionBarTheme"
android:minHeight="?attr/actionBarSize"
android:id="@+id/title"
tools:title="UserName"/>
</com.google.android.material.appbar.AppBarLayout>
<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.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/profile_image"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginStart="@dimen/spacing_normal"
android:layout_marginLeft="@dimen/spacing_normal"
android:layout_marginTop="@dimen/spacing_normal"
app:civ_border_color="@color/secondaryDarkColor"
app:civ_border_width="1dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_profile_24dp"
tools:src="@drawable/ic_profile_24dp" />
<com.google.android.material.textview.MaterialTextView
android:layout_width="0dp"
android:layout_height="0dp"
android:id="@+id/description"
android:layout_marginEnd="@dimen/spacing_normal"
android:layout_marginRight="@dimen/spacing_normal"
android:layout_marginLeft="@dimen/spacing_normal"
android:layout_marginStart="@dimen/spacing_normal"
app:layout_constraintStart_toEndOf="@+id/profile_image"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="@+id/profile_image"
app:layout_constraintTop_toTopOf="@+id/profile_image"
tools:text="Short description "/>
<include
layout="@layout/qff_layout"
android:id="@+id/qff_layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_small"
app:layout_constraintTop_toBottomOf="@+id/profile_image"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<com.google.android.material.tabs.TabLayout
android:id="@+id/sliding_tabs"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@color/primaryLightColor"
app:tabMode="fixed"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/qff_layout"/>
<androidx.viewpager.widget.ViewPager
android:id="@+id/view_pager"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@android:color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/sliding_tabs" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
回答4:
Your content fit in your display area. that's why it happened. For that, you have to set fix height of your ViewPager. After that your whole layout scroll. But this is not a proper solution.
Example
set ViewPager height 500dp and check it.
回答5:
I used HorizontalScrollView
as a container for the upper layout:
<ConstraintLayout>
<Toolbar/>
<HorizontalScrollView>
<ConstraintLayout android:id="@+id/top_container">
<!-- your upper widgets here -->
</ConstraintLayout>
</HorizontalScrollView>
<TabLayout/>
<ViewPager/>
</ConstraintLayout>
When the user scrolls the ViewPager
, it callbacks the ViewPager.OnPageChangeListener.onPageScrolled().
You can override it to programmatically scroll the upper layout by HorizontalScrollView.setScrollX() method, inherited from the base View
class:
class ScrollListener implements ViewPager.OnPageChangeListener {
private ViewGroup scrollView;
ScrollListener(ViewGroup scrollView) {
this.scrollView = scrollView;
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
scrollView.setScrollX(scrollView.getWidth() * position + positionOffsetPixels);
}
// the interface requires to override 2 more methods:
@Override
public void onPageSelected(int position) {}
@Override
public void onPageScrollStateChanged(int state) {}
}
Then in the MainActivity
you have to set this Listener to the ViewPager
by ViewPager.addOnPageChangeListener():
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewGroup scrollView = findViewById(R.id.top_scroll);
ViewPager viewPager = findViewById(R.id.view_pager);
viewPager.addOnPageChangeListener(new ScrollListener(scrollView));
viewPager.setAdapter(new MyAdapter(getApplicationContext(), 3));
}
Notice: This code will compact the upper layout, because HorizontalScrollView
don't use screen bounds to measure and to layout. Thus, it allows you to put widgets off the screen (screenshot) and gives the ability to scroll.
In your case there are no widgets off the screen, so you won’t see the result, because there’s nothing to scroll. For the test, I added a couple of lines in MainActivity
to stretch the upper layout:
Point size = new Point();
getWindowManager().getDefaultDisplay().getSize(size);
ConstraintLayout upperLayout = findViewById(R.id.top_container);
upperLayout .setMinWidth(size.x + 400);
My git source code example: link (there I removed scroll by touch for upper layout)
Hope this helps, good luck!
来源:https://stackoverflow.com/questions/57819500/android-nestedscrollview-doesnt-work-in-layout-with-viewpager