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?

Xaver Kapeller
  • 49,491
  • 11
  • 98
  • 86
marmor
  • 27,641
  • 11
  • 107
  • 150
  • If you really have to create a tag, then at least create a properly named one. [tag:coordinator-layout] ist the wrong name. If you had ever read the tag description of [tag:android] then you would know: "When adding additional tags to questions, please use the Android-specific tags such as [tag:android-intent] and [tag:android-activity], not [tag:intent] and [tag:activity].". So the correct tag should be [tag:android-coordinator-layout] and instead of [tag:recyclerview] you are supposed to use [tag:android-recyclerview]. – Xaver Kapeller Jun 03 '15 at 12:43
  • The same goes for [tag:listview], you are supposed to use [tag:android-listview]. – Xaver Kapeller Jun 03 '15 at 12:45
  • 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 popular – marmor Jun 03 '15 at 14:52
  • 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. – Xaver Kapeller Jun 03 '15 at 14:56
  • Did you find a solution for this? If yes can you tell me Here is my question http://stackoverflow.com/questions/33482709/set-custom-list-view-inside-nestedscrollview – Android Nov 03 '15 at 07:31
  • No, I ended up switching all my lists to RecyclerViews – marmor Nov 03 '15 at 09:02

6 Answers6

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.

Nicolas POMEPUY
  • 1,123
  • 9
  • 11
  • 3
    Interesting find! I'll accept the answer, but still hoping for someone to provide a more backwards-compatible solution. – marmor Jun 07 '15 at 05:11
  • 1
    I agree. A `NestedScrollingListView` would be really appreciated. – Nicolas POMEPUY Jun 07 '15 at 07:52
  • thank you. but it only works for lollypop. can you please tell how to get it working in lower versions – Parth Anjaria Nov 03 '15 at 12:17
  • 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. – AndyDeveloper Mar 08 '16 at 00:42
  • 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... – angryITguy Jul 13 '21 at 00:23
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.

Community
  • 1
  • 1
hidro
  • 12,333
  • 6
  • 53
  • 53
  • Isn't the compat library make it compatible with earlier versions? – sivi Mar 28 '16 at 19:20
  • 2
    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. – hidro Mar 29 '16 at 00:26
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

TheoK
  • 3,601
  • 5
  • 27
  • 37
  • 1
    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. – marmor Jun 03 '15 at 08:43
  • I have tested on my App and `CoordinatorLayout` only works with `RecyclerView` – Vincent Paing Jun 05 '15 at 11:10
  • 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. – marmor Jun 07 '15 at 05:13
  • Wrapping `ListView` into `ScrollView` sucks, don't ever do this. – Miha_x64 Oct 12 '18 at 11:10
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);
}

}

I Love Coding
  • 333
  • 5
  • 6
  • Yes, it's not depends on Android version. But if we can, just uses RecyclerView – I Love Coding Aug 10 '16 at 10:27
  • 1
    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 listView – krossovochkin Oct 27 '16 at 09:13
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;
        }
    });
}
Ahamadullah Saikat
  • 4,437
  • 42
  • 39
  • 1
    interesting, might have worked for me, but i've switched all my ListViews to RecyclerViews a long time because of such limitations. – marmor Dec 12 '17 at 19:10
  • 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. – Ahamadullah Saikat Dec 13 '17 at 06:32
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.

A P
  • 2,131
  • 2
  • 24
  • 36