ScrollingViewBehavior for ListView
Asked Answered
L

6

34

I have two activities using AppBarLayout with a Toolbar and TabLayout from support library 22.

The layout of both is pretty similar: A Toolbar at the top, below it TabLayout, below it a ViewPager containing 3 Fragments.

The first activity's Fragment has a RecyclerView, the second activity's Fragment is using a ListView instead.

The scrollable Toolbar example from https://github.com/chrisbanes/cheesesquare is working fine on the first activity using the RecyclerView, but on with the ListView.

I've tried created a custom ListViewScrollBehavior that extends AppBarLayout.ScrollingViewBehavior, but so far no luck. The TouchEvents are passed to the custom class only for horizontal scrolling, but not when scrolling the ListView (vertically).

Any way to use a CoordinatorLayout with ListView?

Lalia answered 3/6, 2015 at 6:30 Comment(6)
If you really have to create a tag, then at least create a properly named one. coordinator-layout ist the wrong name. If you had ever read the tag description of android then you would know: "When adding additional tags to questions, please use the Android-specific tags such as android-intent and android-activity, not intent and activity.". So the correct tag should be android-coordinator-layout and instead of recyclerview you are supposed to use android-recyclerview.Cleora
The same goes for listview, you are supposed to use android-listview.Cleora
ok, got it. I usually just use the most popular one, and when searching for these tags the ones w/o 'android-' were a lot more popularLalia
yeah we are working on fixing that through synonyms and burninations. Just remember next time: use tags to describe what your question is about and not what your question contains.Cleora
Did you find a solution for this? If yes can you tell me Here is my question #33483209Couturier
No, I ended up switching all my lists to RecyclerViewsLalia
P
38

The only solution to make it work now is to use this:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
     listView.setNestedScrollingEnabled(true);
}

It will obviously only work on Lollipop.

Prettify answered 6/6, 2015 at 7:31 Comment(5)
Interesting find! I'll accept the answer, but still hoping for someone to provide a more backwards-compatible solution.Lalia
I agree. A NestedScrollingListView would be really appreciated.Prettify
thank you. but it only works for lollypop. can you please tell how to get it working in lower versionsChokebore
This worked for me, but the scrolling was not as smooth as with a RecyclerView, especially if you fling the list. I'd recommend migrating from ListView to RecyclerView, which is what the OP ended up doing.Visionary
This answer still relevant +1! Looks like a Google added this as a hack for backward compatibility. ListViews may get deprecated so they may not want invest alot in it. Also, seems that you can only do this programmatically, using equivalent XML attribute in listview element causes app to crash...Hwang
M
14

Alternative solution to Nicolas POMEPUY's answer is to use ViewCompat.setNestedScrollingEnabled(View, boolean)

ViewCompat.setNestedScrollingEnabled(listView, true);

Of course nested scrolling behavior will only work from Lollipop.

Manchineel answered 15/9, 2015 at 3:15 Comment(2)
Isn't the compat library make it compatible with earlier versions?Matamoros
It makes the code compiled and gracefully falls back in earlier versions, in some cases it may also works seamlessly but that's not always the case.Manchineel
D
11

I believe that the CoordinatorLayout works only with RecyclerView and NestedScrollView. Try wrapping your ListView in a NestedScrollView or convert it to a RecyclerView with a LinearLayoutManager

Didier answered 3/6, 2015 at 7:8 Comment(4)
It looks like Behavior is an extendable class that is there for adding support to coordination of more classes, so I do think it is possible. However without any documentation, samples, or source code to explain it I find it hard to understand how it works.Lalia
I have tested on my App and CoordinatorLayout only works with RecyclerViewScrape
CoordinatorLayout works with RecyclerView and any other scrolling view that support NestedScrollView. However, there seem to be classes that provide this behavior to extend to more scrolling classes like ListView, I'm hoping someone might be able to show a working code for this. So far the accepted answer shows how to make ListView work with Coordinator for API >= Lollipop.Lalia
Wrapping ListView into ScrollView sucks, don't ever do this.Yoakum
L
6

To a view able to react on AppBarLayout, it need to implement NestedScrollingChild. ListView is not. But it could be implement by a delegate class easily. Use it, it will do like what RecyclerView did

public class NestedScrollingListView extends ListView implements NestedScrollingChild {
private NestedScrollingChildHelper mNestedScrollingChildHelper;

public NestedScrollingListView(final Context context) {
    super(context);
    initHelper();
}

public NestedScrollingListView(final Context context, final AttributeSet attrs) {
    super(context, attrs);
    initHelper();
}

public NestedScrollingListView(final Context context, final AttributeSet attrs, final int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    initHelper();
}

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public NestedScrollingListView(final Context context, final AttributeSet attrs, final int defStyleAttr, final int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
    initHelper();
}

private void initHelper() {
    mNestedScrollingChildHelper = new NestedScrollingChildHelper(this);
    setNestedScrollingEnabled(true);
}

@Override
public void setNestedScrollingEnabled(final boolean enabled) {
    mNestedScrollingChildHelper.setNestedScrollingEnabled(enabled);
}

@Override
public boolean isNestedScrollingEnabled() {
    return mNestedScrollingChildHelper.isNestedScrollingEnabled();
}

@Override
public boolean startNestedScroll(final int axes) {
    return mNestedScrollingChildHelper.startNestedScroll(axes);
}

@Override
public void stopNestedScroll() {
    mNestedScrollingChildHelper.stopNestedScroll();
}

@Override
public boolean hasNestedScrollingParent() {
    return mNestedScrollingChildHelper.hasNestedScrollingParent();
}

@Override
public boolean dispatchNestedScroll(final int dxConsumed, final int dyConsumed, final int dxUnconsumed, final int dyUnconsumed, final int[] offsetInWindow) {
    return mNestedScrollingChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);
}

@Override
public boolean dispatchNestedPreScroll(final int dx, final int dy, final int[] consumed, final int[] offsetInWindow) {
    return mNestedScrollingChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
}

@Override
public boolean dispatchNestedFling(final float velocityX, final float velocityY, final boolean consumed) {
    return mNestedScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
}

@Override
public boolean dispatchNestedPreFling(final float velocityX, final float velocityY) {
    return mNestedScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY);
}

}

Locution answered 13/4, 2016 at 6:56 Comment(2)
Yes, it's not depends on Android version. But if we can, just uses RecyclerViewLocution
Actually doesn't work correctly on pre-lollipop. It scrolls toolbar only when you scroll on toolbar. When you scroll listView itself - it doesn't scroll toolbar, but only scrolls listViewTamikotamil
A
1

ListView ScrollingViewBehavior supports only >= 21.

Otherwise you should to write code as below way:

private int mPreviousVisibleItem;


if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    listView.setNestedScrollingEnabled(true);
} else {
    listView.setOnScrollListener(new AbsListView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
        }
        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            if (firstVisibleItem > mPreviousVisibleItem) {
                appBarLayout.setExpanded(false, true);
            } else if (firstVisibleItem < mPreviousVisibleItem) {
                appBarLayout.setExpanded(true, true);
            }
            mPreviousVisibleItem = firstVisibleItem;
        }
    });
}
Attach answered 8/12, 2017 at 13:27 Comment(2)
interesting, might have worked for me, but i've switched all my ListViews to RecyclerViews a long time because of such limitations.Lalia
I used that code for expandableListView. But this codes also works for both listview & expandablelistview. I'm also using recyclerview too. & it's good to me. But there have no official library for expandableRecyclerview. That's why I'm using expandableListView.Attach
U
0

You can add

    android:nestedScrollingEnabled="true"

to the ListView from XML, just note that this only supports API 21+. Alternatively, you can swap out your ListView for a RecyclerView, and that should work better.

Unpretentious answered 4/5, 2020 at 17:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.