Introduction
In this tutorial, you will learn to use Android ScrollView
component in application. ScrollView
is a different kind of layout than LinearLayout
, RelativeLayout, etc. which is designed to view larger than its actual size. If the views inside ScrollView
are more than the views that can be hold/contained within ScrollView
then a scrollbar is automatically added.
ScrollView
can hold only one direct child. This means that, if you have complex layout with more view controls, you must enclose them inside another standard layout like LinearLayout
, TableLayout
or RelativeLayout
.
ScrollView
only supports vertical scrolling. Use HorizontalScrollView
for horizontal scrolling.
The android:fillViewport
property defines whether the ScrollView
should stretch its content to fill the viewport. You can set the same property by calling setFillViewport(boolean)
method.
After creating the project, open the activity_main.xml file of your application from res/layout directory and replace the content with the content given below.
<?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <ImageView android:id="@+id/imageView" android:layout_width="wrap_content" android:layout_height="200dp" android:scaleType="centerCrop" android:src="@drawable/image" /> <TextView android:layout_width="fill_parent" android:layout_height="fill_parent" android:padding="10dp" android:text="Android ScrollView Example" android:textStyle="bold" /> </LinearLayout> </ScrollView>
Custom ScrollView
In android ScrollView
there is no way or method to disable scrolling. But sometime it's necessary to disable scrolling of ScrollView
while you are inside inner element, like if you want to disable ScrollView
when you are inside Map view.
We can make our own ScrollView
which have extra features for enable or disable scrolling.
public class ToggleScrollView extends ScrollView { private boolean enableScrolling = true; public ToggleScrollView(Context context) { super(context); } public ToggleScrollView(Context context, AttributeSet attrs) { super(context, attrs); } public ToggleScrollView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public ToggleScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (scrollingEnabled()) { return super.onInterceptTouchEvent(ev); } else { return false; } } @Override public boolean onTouchEvent(MotionEvent ev) { if (scrollingEnabled()) { return super.onTouchEvent(ev); } else { return false; } } private boolean scrollingEnabled(){ return enableScrolling; } public void setScrolling(boolean enableScrolling) { this.enableScrolling = enableScrolling; } }
Also create a layout with new ToggleScrollView
.
<me.proft.projectA.ToggleScrollView android:id="@+id/svToggle" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- child views in here --> </me.proft.projectA.ToggleScrollView>
So now you have embedded your own ScrollView
. To disable scrolling just call setScrolling()
method and pass true
value. As shown in following example:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ToggleScrollView svToggle = (ToggleScrollView) findViewById(R.id.svToggle); svToggle.setScrolling(false); // to disable scrolling svToggle.setScrolling(true); // to enable scrolling } }
Horizontally Scrolling
In other cases, we want content to horizontally scroll in which case we need to use the HorizontalScrollView instead like this:
<HorizontalScrollView android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <!-- child views in here --> </LinearLayout> </HorizontalScrollView>
and now you have a horizontally scrolling view.
Scrollable TextView
Note that a TextView
doesn't require a ScrollView
and if you just need a scrolling TextView
simply set the scrollbars
property and apply the correct MovementMethod
:
<TextView android:id="@+id/tv_long" android:layout_width="wrap_content" android:layout_height="match_parent" android:scrollbars = "vertical" android:text="@string/really_long_string"> </TextView>
and then in the activity:
TextView tvTitle = (TextView) findViewById(R.id.tvTitle); tvTitle.setMovementMethod(new ScrollingMovementMethod());
Now the TextView
will automatically scroll vertically.
Nested ScrollViews
Adding a ScrollView
within another ScrollView
can be difficult. Most of the times it won’t end well. You will end up adding few workarounds. Instead, use the NestedScrollView
as outlined here.
How to detect when ScrollView stops scrolling and do some unscroll
public class MainActivity extends AppCompatActivity { NestedScrollView scrollView; Button btn1, btn2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ... scrollView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { switch (motionEvent.getAction()) { case MotionEvent.ACTION_UP: int scrolled = scrollView.getScrollY(); final int maxScroll = scrollView.getChildAt(0).getMeasuredHeight() - scrollView.getHeight(); int btn1Height = btn1.getHeight(); if (isIntersect(btn2, btn1) || (scrolled >= (maxScroll - btn1Height))) { Handler handler = new Handler(); handler.postDelayed(new Runnable() { public void run() { scrollView.smoothScrollTo(0, maxScroll); } }, 200); } break; } return false; } }); } public boolean isIntersect(View firstView, View secondView) { int[] firstPosition = new int[2]; int[] secondPosition = new int[2]; firstView.getLocationOnScreen(firstPosition); secondView.getLocationOnScreen(secondPosition); Rect rectFirstView = new Rect(firstPosition[0], firstPosition[1], firstPosition[0] + firstView.getMeasuredWidth(), firstPosition[1] + firstView.getMeasuredHeight()); Rect rectSecondView = new Rect(secondPosition[0], secondPosition[1], secondPosition[0] + secondView.getMeasuredWidth(), secondPosition[1] + secondView.getMeasuredHeight()); return rectFirstView.intersect(rectSecondView); } }
How to programmatically scroll to end of screen
There are two possible solutions with pros and cons.
First. Use the method fullScroll(int)
on NestedScrollView
. NestedScrollView
must be drawn before using this method and the focus will be lost on the View
that gained it before.
nestedScrollView.post(new Runnable() { @Override public void run() { nestedScrollView.fullScroll(View.FOCUS_DOWN); } });
Second. Use the method scrollBy(int,int)
/smoothScrollBy(int,int)
. It requires much more code, but you will not lose the current focus:
nestedScrollView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { final int scrollViewHeight = nestedScrollView.getHeight(); if (scrollViewHeight > 0) { nestedScrollView.getViewTreeObserver().removeOnGlobalLayoutListener(this); final View lastView = nestedScrollView.getChildAt(nestedScrollView.getChildCount() - 1); final int lastViewBottom = lastView.getBottom() + nestedScrollView.getPaddingBottom(); final int deltaScrollY = lastViewBottom - scrollViewHeight - nestedScrollView.getScrollY(); /* If you want to see the scroll animation, call this. */ nestedScrollView.smoothScrollBy(0, deltaScrollY); /* If you don't want, call this. */ nestedScrollView.scrollBy(0, deltaScrollY); } } });