11

I'm trying to make a search fragment similar to the one in the Play Store:

Google Play Store

My AppBarLayout has a CardView in it, and the background is set to transparent:

<android.support.design.widget.CoordinatorLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/transparent"
        android:theme="@style/ThemeOverlay.AppCompat.ActionBar"
        app:elevation="0dp">

        <!-- CardView -->

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

    <android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <!-- Main content -->

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

But as you can see, there is a solid background behind the CardView so that the content behind it is hidden:

Hidden content

How can I make the AppBarLayout's background transparent so that the content behind is visible?

joharei
  • 578
  • 1
  • 5
  • 22
  • Do you want the NestedScrollView to scroll under the AppbarLayout? – jayeshsolanki93 Jul 25 '16 at 09:23
  • I want the content of the NestedScrollView to hug the search area closely, but right now no content is visible above the search area, and below it there is a line of gray separating the search area from the content. Edit: Yes, that's what I want. – joharei Jul 25 '16 at 09:24
  • Try adding `android:fitsSystemWindows="true"` to `AppbarLayout` and `NestedScrollView` – jayeshsolanki93 Jul 25 '16 at 09:27
  • That creates even more margin below the search area, and the scrolling behavior acts strange. – joharei Jul 25 '16 at 09:31

4 Answers4

10

Another option is to add negative top margin to NestedScrollView (or whatever view is using appbar_scrolling_view_behavior) and the same positive top margin (or padding) to its direct child:

<androidx.core.widget.NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    android:layout_marginTop="-100dp" >

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingTop="100dp" >

        ...

    </androidx.constraintlayout.widget.ConstraintLayout>

</androidx.core.widget.NestedScrollView>

<com.google.android.material.appbar.AppBarLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@null" >

    <com.google.android.material.appbar.CollapsingToolbarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_scrollFlags="scroll|exitUntilCollapsed" >

        ...

    </com.google.android.material.appbar.CollapsingToolbarLayout>

</com.google.android.material.appbar.AppBarLayout>

Note that in order for this approach to work the AppBarLayout should be placed below NestedScrollView in the layout-file. In case fading edges of NestedScrollView produce visual artifacts, add android:overScrollMode="never" to its parameters. Also take care when using elevation since it, when used in wrong place (say, when applied to whole AppBarLayout), may also produce visual artifacts.

a.ch.
  • 8,285
  • 5
  • 40
  • 53
7

The same problem happened to me and at this point it has nothing to do with the transparency of the AppBarLayout. But the AppBarLayout pushes the content of your NestedScrollView below itself when fading in.

You can solve this by

  1. moving your NestedScrollView up and behind the AppBarLayout and
  2. adding a space element to your NestedScrollView with the size of the AppBar

Step 1 can be easily achieved by adding this to your onCreate()

mNestedScroll.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        mNestedScroll.getViewTreeObserver().removeOnGlobalLayoutListener(this);

        final int appBarHeight = mAppBar.getHeight();
        mNestedScroll.setTranslationY(-appBarHeight);
        mNestedScroll.getLayoutParams().height = mNestedScroll.getHeight() + appBarHeight;
    }
});

For Step 2 you need to add an empty view with the height of the appBar as a spacer to your NestedScrollView. Note: I used a RecyclerView instead of a NestedScrollView in my approach, so I had to add an empty ViewHolder with the appBarHeight to my RecyclerView. So this snippet is not tested, but it should do the trick for you:

<View
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"/>
Stefan Medack
  • 2,731
  • 26
  • 32
  • This fixes the problem in the question nicely. Thanks! However, the other problem you describe, where the `snap` behavior pushes the `NestedScrollView` content down, still persists. Were you able to fix that using this workaround? – joharei Jan 12 '17 at 13:12
  • @joharei Sorry, but I do not quite understand your question. The problem I described is that all the content will be drawn below the `AppBarLayout`, making the `AppBarLayout` exclusively claim the space it occupies. Therefore the scrollable content is displayed below the AppBar. What I described are two steps to overcome this drawback. The first step will move the content below the AppBar and solves the whole problem. The second step is only to align the scrollable content back to normal (and may not be needed for your implementation) – Stefan Medack Jan 16 '17 at 09:31
  • I may have misunderstood, so please disregard my previous comment. Still, I do have a problem with your solution. If the content in the `NestedScrollView` is taller than the screen, it will appear scrolled up and behind the `AppBarLayout` when the activity loads. When I start scrolling, everything behaves fine. – joharei Jan 17 '17 at 11:31
  • @joharei Okay, sounds to me like the second step did not work correctly for you. I can just assume but it could be that the spacer view from *Step 2* is too small or you center you align your content in the scrollview weirdly (e.g. center it). You can try out to increase the `layout_height` of the view added in *Step 2* – Stefan Medack Jan 17 '17 at 14:56
0

Try putting the CardView into a FrameLayout with match_parent dimensions, so that the CardView has the space around it. I'm not 100% confident that it will work, but it's worth trying.

Aleksandar Stefanović
  • 1,583
  • 2
  • 20
  • 36
  • I actually already had a FrameLayout around it to adjust some paddings. Doesn't work unfortunately. – joharei Jul 25 '16 at 09:18
0

I tried a lot of codes to give me more or less what you looking for. It's an alternative solution, but you will need change the AppBarLayout to a simple view... Look this:

<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/annonce.main.coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:ignore="RtlHardcoded">

<android.support.v7.widget.RecyclerView
    android:id="@+id/rv"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

<!-- here you put your search bar, can be any view !-->
<android.support.v7.widget.CardView
    android:id="@+id/searchbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:cardElevation="4dp"
    app:cardUseCompatPadding="true"
    app:layout_behavior="[package].RecyclerSearchBehavior">

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:hint="Pesquise aqui"
        android:minHeight="58dp"/>

</android.support.v7.widget.CardView>

Note: this is inside a CoordinatorLayout. Create a Behavior that view is the type of the search bar you using:

public class RecyclerSearchBehavior extends CoordinatorLayout.Behavior<CardView> {
//Initial height and location
private int mViewHeight;
private int[] mViewStartLocation;


public RecyclerSearchBehavior(Context context, AttributeSet attrs) {
}

@Override
public boolean layoutDependsOn(CoordinatorLayout parent, final CardView child, View dependency) {
    //the first function called. The initial variables settings.
    //getting height 
    mViewHeight = child.getHeight();
    //getting location on screen
    mViewStartLocation = new int[2];
    child.getLocationOnScreen(mViewStartLocation);
    //if the dependecy is your recycler
    if (dependency instanceof RecyclerView)
        //add scroll listener
        ((RecyclerView) dependency).addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
            }
            //here the magic happens
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                //animate function
                animate(child, dy);
            }
        });
    return dependency instanceof RecyclerView;
}

private void animate(CardView child, int dy) {
    //the initial position of your search bar is 0, because it is on top, so...
    if (child.getY() <= 0)
        //dy is an integer that shows you if user scrolls up or down
        if (dy >= 0) {
            //move to top is a negative value, so *-1
            //if that is < than height, scrools up
            if ((child.getY() * -1) <= (mViewHeight))
                child.setTranslationY(child.getY() - dy * 0.1f);
        } else {
            //if the position of search bar is < than zero, scrolls down
            if (child.getY() < 0)
                child.setTranslationY(child.getY() - dy * 0.1f);
            //else, put the view on 0y
            else child.setY(0);
        }
    //else, put the view on 0y again. this function is called after any user                    
    //touches... so everything happens fast and with numbers different than 
    //1 
    else child.setY(0);
}

}

And done. Your "search bar" is here.

The only problem here, it's you will need add padding to your first item on Recycler, or a transparent view as a first item.

Guilherme Ramos
  • 196
  • 2
  • 10