I've just started writing a custom view in Android (for the first time) and I've realised that I need to implement a scrolling feature.
The custom view also uses a header containing some text (which should stay fixed and not scroll).
I've read through the documentation on GestureDetector.SimpleOnGestureListener
and Scroller
. I also read the documentation on Animating a Scroll Gesture but I found the examples difficult to understand. I've also looked at other questions on Stack Overflow which helped a little.
Using what I understood from the documentation with the Stack Overflow answer as a reference to guide me, I have added the following to my custom view:
Variables and fields:
private OverScroller mScroller;
private final GestureDetector mGestureDetector =
new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
// Note 0 as the x-distance to prevent horizontal scrolling
scrollBy(0, (int) distanceY);
return true;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2,
float velocityX, float velocityY) {
final int maxScrollX = 0;
// wholeViewHeight is height of everything that is drawn
int wholeViewHeight = calculateWholeHeight();
int visibleHeight = getHeight();
final int maxScrollY = wholeViewHeight - visibleHeight;
mScroller.forceFinished(true);
mScroller.fling(0, // No startX as there is no horizontal scrolling
getScrollY(),
0, // No velocityX as there is no horizontal scrolling
- (int) velocityY,
0,
maxScrollX,
0,
maxScrollY);
invalidate();
return true;
}
@Override
public boolean onDown(MotionEvent e) {
if (!mScroller.isFinished()) {
mScroller.forceFinished(true);
}
return true;
}
});
Initialization of mScroller
:
// Called from the constructor
private void init() {
mScroller = new OverScroller(getContext(), new FastOutLinearInInterpolator());
...
}
Stuff in onDraw()
:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
...
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
}
}
Stuff in onTouchEvent()
:
@Override
public boolean onTouchEvent(MotionEvent event) {
return mGestureDetector.onTouchEvent(event);
}
The result of these additions is a custom view which can scroll vertically (and not horizontally), however there are a few issues:
RecyclerView
or ScrollView
)Could someone explain how scrolling works in a custom view and how to implement it properly with these features?
In order to place multiple views in the scroll view, one needs to make a view group(like LinearLayout) as a direct child and then we can define many views inside it. A ScrollView supports Vertical scrolling only, so in order to create a horizontally scrollable view, HorizontalScrollView is used.
Grid view requires an adapter to fetch data from the resources. This view can be scrolled both horizontally and vertically. The scrolling ability of the GridView by default is set to enabled.
NestedScrollView is just like ScrollView , but it supports acting as both a nested scrolling parent and child on both new and old versions of Android. Nested scrolling is enabled by default.
HorizontalScrollView is used to scroll the child elements or views in a horizontal direction. HorizontalScrollView only supports horizontal scrolling. For vertical scroll, android uses ScrollView. Let's implement simple example of HorizontalScrollView.
May I offer a simple layout that has a fixed header and vertically scrolling content that sounds like it will do what you want and does not require complex programming? It may save you from having to spend hours of research and programming in the long run:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:fillViewport="true">
<FrameLayout
android:id="@+id/scrolling_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</android.support.v4.widget.NestedScrollView>
</LinearLayout>
Using this layout, you would replace or insert inside the FrameLayout
with ID "header" with your fixed header view. You would then put your scolling content inside of the FrameLayout
with ID "scrolling_content". The NestedScrollingView
would be located directly below your fixed header and automatically give you the scrolling behavior you want without any further code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With