How to properly hide&show the actionbar using the new design library API?
Asked Answered
T

3

23

Background

Suppose I have these elements in the activity:

  • actionbar (actually a Toolbar)
  • tabs (using TabLayout )
  • ViewPager, with fragments.
  • each fragment has ListVIew/RecyclerView.

I wish to have the same behavior as on many apps, that when scrolling, the action bar will hide or show.

A perfect example for it is how the Play Store scrolling works.

Something like this (but with ListView in the fragments of course) :

enter image description here

The problem

I've found the "Android-ObservableScrollView" library which shows how to do it for many types of scrolling views, but it doesn't handle the case of the new TabLayout usage (plus it has issues), as shown on newer samples, like cheesesquare.

On the new API of the design library (shown on the "cheesesquare" example) , it's more automatic so you don't need to create so much boilerplate for handling the scrolling and what to do with it.

Something like that:

<android.support.v4.widget.DrawerLayout

  <android.support.design.widget.CoordinatorLayout>

    <android.support.design.widget.AppBarLayout>

      <android.support.v7.widget.Toolbar
       app:layout_scrollFlags="scroll|enterAlways"/>

      <android.support.design.widget.TabLayout/>

    </android.support.design.widget.AppBarLayout>

    <android.support.v4.view.ViewPager
     app:layout_behavior="@string/appbar_scrolling_view_behavior" />

    <android.support.design.widget.FloatingActionButton/>

  </android.support.design.widget.CoordinatorLayout>

  <android.support.design.widget.NavigationView/>

</android.support.v4.widget.DrawerLayout>

This hirerchy promises that snackbars will be below the FABs, and that the action bar will automatically be animated (shown/hidden) based on the inner RecyclerView of the fragments.

Thing is, this seems to work only with RecyclerView, and not with ListView.

Since they are quite complex, it will be very hard to convert them to RecyclverViews.

The question

What would it take to make the above mechanism work for ListViews too?

Is there a way to do it? If so, how?


EDIT: sadly it seems that Google will not support this behavior for ListView, but only for RecyclerView and NestedScrollView (written about here).

So I've tried to use the "Android-ObservableScrollView" library, but it has a lot of scrolling issues, including fling issues.

Now I've decided to give Android-ParallaxHeaderViewPager . Can anyone please tell me how to make it work in the current scenario? Is it even possible?


EDIT: I've decided to convert to RecyclerView after all. However, all the samples I've tried show an issue: when stopping to scroll, the actionbar (and maybe even the FAB) can stay truncated, as opposed to the play store, where it will get animated to either being hidden/shown.

EDIT: It seems I need to use setExpanded when having IDLE for onScrollStateChanged . question is, how to decide when to expand and when to collapse.

Tachistoscope answered 13/8, 2015 at 13:48 Comment(4)
I don't see where you might use a ListView, looking at the animation. Do you want to use ScrollView instead for paging up/down? That would be a different problem though.Analog
@TheOriginalAndroid There are ListViews inside the fragments of the ViewPager. The animated gif is just an example. You can look at the play store if you insist on seeing this combination.Tachistoscope
I don't know "Android-ObservableScrollView". For your needs, can you just use ListView in the Pager? And detect scrolling events in the ListView. When they are detected, you can change the ActionBar visibility to GONE. I figured you know Android well and have an opinion on this already.Analog
@TheOriginalAndroid That's actually how I've done it previously, but it has issues: it doesn't move the actionbar as you scroll, and the ListView should be enlarged and shrunk depending on the actionbar, yet when you do this, it messes with the touch events too (because the coordinates change). A possible solution was to have an empty header for the ListView, but this might be shown on some cases, plus it won't work well when there is also a tabs view.Tachistoscope
T
7

this is a nice workaround, but It's still very different than what the Play Store shows:

for each fragment, call something like that:

    recyclerView.addOnScrollListener(new OnScrollListener() {
        @Override
        public void onScrollStateChanged(final RecyclerView recyclerView, final int newState) {
            super.onScrollStateChanged(recyclerView, newState);
            ((MainActivity) getActivity()).onScrollStateChanged(recyclerView, newState);
        }

        @Override
        public void onScrolled(final RecyclerView recyclerView, final int dx, final int dy) {
            super.onScrolled(recyclerView, dx, dy);
            ((MainActivity) getActivity()).onScrolled(recyclerView, dx,dy);
        }
    });

for the hosting activity:

public void onScrollStateChanged(final RecyclerView recyclerView, final int newState) {
    switch (newState) {
        case RecyclerView.SCROLL_STATE_IDLE:
            mAppBarLayout.setExpanded(mLastDy <= 0, true);
            mLastDy = 0;
            break;
    }
}

public void onScrolled(final RecyclerView recyclerView, final int dx, final int dy) {
    mLastDy = dy == 0 ? mLastDy : dy;
}

Still, if anyone knows how to make it work well (using either ListView or RecyclerView for the fragments), just like on the Play-Store and other apps, please write it down.

Tachistoscope answered 19/8, 2015 at 8:3 Comment(5)
it is still using recyclerview instead of listview. How it gone be useful to you?Paradigm
Sadly, after all the time I've spent on ListView, I got to a dead end, as all that I've tried had bugs, so I've changed the question and now I'm trying to use RecyclerView. It's not as good as on the Play Store, but for now I will use this workaround, as I haven't found a good sample out there.Tachistoscope
Google Play it self have recyclerview. So its better to go with that. Also you can check the example given by me so that works fine with recyclerview. Now, its upto you how to move forward.Paradigm
@iDroidExplorer I will get back to this in a few weeks, as I've failed to do it all in a single day (as I've written, we have very complex listViews). Sorry and thank you for trying to help.Tachistoscope
no problem friend. I have already done the same thing for one of my app. Its also working fine, so suggest you to go with that. Yes, there you might have to update some of the code to handled the issue mention by you. But it is working fine. You should have to install this app and check the initial wall screen of this app. play.google.com/store/apps/details?id=com.thefancy.appParadigm
P
3

New design API are for new components only. If that component is going to support older versions then it must work for older versions.

Related to your issue: I don't know, whether you have used the suggested library or not. But it works well here in my case. I know that its a very old library and you must have to use listview with that. You can't implement recyclerview with that. But as i want to have very flexible and smooth effect, I have try it out and it works very well.

You can also make smooth scroll of toolbar with Listview scrolling and also make paralax effect as you have seen in the example.

See this images for the effect I have implemented for my app. Mark with red color is my toolbar of the app.

Image when it is expanded:

enter image description here

Image when it is collapsed:

enter image description here

I suggest to try that library for your actionbar related issue, if you do not think to change your listview to recyclerview. But if you want to change your listview to recyclerview then you can use new design API for such effect.

Let me know if I can help you more in this case.

Sincerely,

Shreyash

Updated answer

I assume that you need something like THIS.

If it is what you want, then you should have to refer this library and implement like that.

Please refer, Part1, Part2 and part3 for similar effect.

Let me know if you need anything else.

Paradigm answered 18/8, 2015 at 13:3 Comment(17)
I'm trying it now, but it lacks a very important thing: animating of the actionbar when the scrolling is idle. I need it to behave like on the Play Store: there can't be a situation that you don't touch the screen, yet only half of the header is shown. It's either the tabs or the tab&actionbar that is shown. Only while scrolling it should show the semi-scrolling states of them. I tried now to add "onScrollStateChanged", and add animation there, but "onScroll" tries to start from the wrong position because of the way it calculates the scrolling position (which is also wrong)Tachistoscope
Here's my POC project : drive.google.com/file/d/0B-PZZGk2vPohTjFJbzdvNVlkVFk . I hope you can understand what I've tried there.Tachistoscope
Also, another issue is that the POC doesn't resize the actionbar when you are not on the top of the listview. I need to handle those 2 issues... :(Tachistoscope
And, even the sample itself has an issue: when swiping between the fragments, the scrolling gets reset, and might even show empty area on the top.Tachistoscope
@androiddeveloper Please upload your project on github and give me access of it to fork and update it. This is not the way, I can do this. Actually, here i dont have access of dropbox or google drive. I can get it from the github if you want and also update it.Paradigm
Ohh OK. I got it what you want.Paradigm
@androiddeveloper, Please check my updated answer. I hope you will like it.Paradigm
Example that you've shown is using a RecyclerView, while I asked about ListView. Also, it doesn't work as on the play store, as I can easily make the action bar half-shown (truncated), as on the play store, it's either hidden or shown when you are stop scrolling.Tachistoscope
@androiddeveloper Buddy its work with listview also. Please check part 3 of that tutorial. You can update the code littlebit and that should work for listview as well Check this: github.com/mzgreen/HideOnScrollExampleParadigm
Even if it does support ListView (which is not), it still has an issue: github.com/mzgreen/HideOnScrollExample/issues/9 . I think it should use onScrollStateChanged and setExpanded based on the scrolling information.Tachistoscope
@androiddeveloper its seems like you have not review the mechanisum the author is using for making that effect. You should try on that. And share your points if you facing any issue in any line.Paradigm
Sorry, please use part 2 for same thingParadigm
Part 2 is also buggy. Again, I want to have the same behavior as on the Play Store, without issues.Tachistoscope
@androiddeveloper I have just checked it. It seems like working fine. I dont know whats the issue you are facing. Anyway, I am sure that it is going to work for you but still If you found that the solution is not working for you, you should have to make your own. Thats what i can say.Paradigm
Check here: github.com/mzgreen/HideOnScrollExample/issues/9 . It can show empty space.Tachistoscope
hummm, I guess you need to workout on it. I have already resolved that issue in my case and it works fine as i want. You can check the fancy app for same reference. You should have to try to update the code and resolved it.Paradigm
Sadly it seems I will have to convert everything to RecyclerView, as we ditched the idea of still using ListView for this.Tachistoscope
I
2

Based on @android developer solution which sometimes expand the app bar when list is fully scrolled I made a small change expanding or collapsing the list depending on the scrolled amount (if it's bigger than the header height then expand).

mHeaderHeight = getResources().getDimensionPixelSize(R.dimen.appbar_height);

mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);
        switch (newState) {
            case RecyclerView.SCROLL_STATE_IDLE:
                mAppBar.setExpanded(mScrolledAmount < mHeaderHeight , true);
                break;
        }
    }

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        mScrolledAmount += dy;
    }
});

EDIT: What works even better for me is having different thresholds to expand and collapse

     switch (newState) {
            case RecyclerView.SCROLL_STATE_IDLE:
                if (mScrolledAmount < 50){
                    mAppBar.setExpanded(true , true);
                }
                if (mScrolledAmount > mHeaderHeight) {
                    mAppBar.setExpanded(false, true);
                }
                break;
        }
Intelligentsia answered 26/8, 2015 at 13:4 Comment(1)
Looks better, but I had to go to another topic. I will need to check it out again when I'll be back to this issue. For now, have a +1 for the effort.Tachistoscope

© 2022 - 2024 — McMap. All rights reserved.