7

Hi I want to make scrollable toolbar with searchbox. on scrolling toolbar should be hide toolbar and show search box. Give me some suggestion that how i can build this kind of UI for my application.

Here i want something like this.

enter image description here

Here i am posting my xml design that is doing functionality near by this design but still it need furnishing.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:id="@+id/coordinatorLayout"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@+id/navigation"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:context=".MainActivity">

        <com.google.android.material.appbar.AppBarLayout
            android:id="@+id/appBar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/ThemeOverlay.AppCompat.Dark">

            <com.google.android.material.appbar.CollapsingToolbarLayout
                android:id="@+id/collapsingToolbarLayout"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:layout_scrollFlags="scroll|enterAlways|snap|exitUntilCollapsed"
                app:statusBarScrim="?attr/colorAccent">

                <androidx.constraintlayout.motion.widget.MotionLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    app:layoutDescription="@xml/collapsing_toolbar">

                    <View
                        android:id="@+id/toolbars"
                        android:layout_width="match_parent"
                        android:layout_height="120dp"
                        android:background="#efefef" />

                    <androidx.appcompat.widget.SearchView
                        android:id="@+id/search_view"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginStart="16dp"
                        android:layout_marginTop="56dp"
                        android:layout_marginEnd="16dp"
                        android:background="@android:color/white"
                        android:elevation="4dp"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent" />

                </androidx.constraintlayout.motion.widget.MotionLayout>

                <androidx.appcompat.widget.Toolbar
                    android:id="@+id/toolbar"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="#efefef"
                    android:minHeight="?attr/actionBarSize"
                    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
                    app:layout_collapseMode="pin"
                    app:popupTheme="@style/ThemeOverlay.AppCompat.Light">


                    <androidx.appcompat.widget.SearchView
                        android:id="@+id/search_view_2"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:visibility="gone"
                        android:layout_marginStart="16dp"
                        android:layout_marginEnd="?android:attr/actionBarSize"
                        android:background="@android:color/white"
                        android:elevation="4dp"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent" />

                </androidx.appcompat.widget.Toolbar>

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

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

        <androidx.core.widget.NestedScrollView
            android:id="@+id/nestedScrollView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:clipToPadding="true"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">

            <FrameLayout
                android:id="@+id/fragment_container"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_marginBottom="56dp"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

        </androidx.core.widget.NestedScrollView>

    </androidx.coordinatorlayout.widget.CoordinatorLayout>

        <com.google.android.material.bottomnavigation.BottomNavigationView
            android:id="@+id/navigation"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="0dp"
            android:layout_marginEnd="0dp"
            android:background="?android:attr/windowBackground"
            app:labelVisibilityMode="labeled"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:menu="@menu/navigation" />

    </androidx.constraintlayout.widget.ConstraintLayout>
  • res/xml/collapsing_toolbar.xml

    <Transition
        motion:constraintSetStart="@id/start"
        motion:constraintSetEnd="@+id/end">
        <OnSwipe
            motion:dragDirection="dragDown"
            motion:touchAnchorId="@id/toolbars"
            motion:touchAnchorSide="bottom" />
        <OnSwipe />
    </Transition>
    
    <ConstraintSet android:id="@+id/start">
        /** Very simple animation no need for start state*/
    
    
    </ConstraintSet>
    
    <ConstraintSet android:id="@+id/end">
        <Constraint
            android:id="@id/search_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="24dp"
            android:layout_marginTop="0dp"
            android:layout_marginEnd="24dp"
            android:elevation="4dp"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toTopOf="parent" />
    
        <Constraint
            android:id="@id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="56dp" />
    
    </ConstraintSet>
    

below code placed in on Create Mehod

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initUI();

          nestedScrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
            @Override
            public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {

                boolean isSearchViewVisible = isVisible(v);

                if (isSearchViewVisible){
                    searchView.setVisibility(View.GONE);
                    searchViewMain.setVisibility(View.VISIBLE);
                } else{
                    searchView.setVisibility(View.VISIBLE);
                    searchViewMain.setVisibility(View.GONE);
                }

            }
        });


    }




private boolean isVisible(View view){
    Rect scrollBounds = new Rect();
    nestedScrollView.getDrawingRect(scrollBounds);

    float top = 0f;
    View view1 = view;

    while (view1 instanceof NestedScrollView){
        top += (view1).getY();
        view1 = (View)view1.getParent();
    }

    float bottom = top + view.getHeight();
    return scrollBounds.top < bottom && scrollBounds.bottom >top;
}
Vasudev Vyas
  • 726
  • 1
  • 10
  • 28
  • https://stackoverflow.com/questions/31290337/how-to-hide-actionbar-while-scrolling-listview-in-android refer this – Malavan Nov 19 '19 at 04:46
  • https://stackoverflow.com/questions/21585326/implementing-searchview-in-action-bar for search bar – Malavan Nov 19 '19 at 04:47

3 Answers3

6

You could use two searchviews (one inside toolbar and the other below toolbar) to achieve this. In your layout:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ScrollSearchActivity">

    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:id="@+id/coordinatorLayout"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@+id/navigation"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:context=".MainActivity">

        <com.google.android.material.appbar.AppBarLayout
            android:id="@+id/appBar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/ThemeOverlay.AppCompat.Dark">

            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="#efefef"
                android:minHeight="?attr/actionBarSize"
                android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light">


                <androidx.appcompat.widget.SearchView
                    android:id="@+id/search_view_2"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:visibility="gone"
                    android:layout_marginStart="16dp"
                    android:layout_marginEnd="?android:attr/actionBarSize"
                    android:background="@android:color/white"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toTopOf="parent" />

            </androidx.appcompat.widget.Toolbar>

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

        <androidx.core.widget.NestedScrollView
            android:id="@+id/nestedScrollView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:clipToPadding="true"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">
                <FrameLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="@color/colorPrimary"
                    android:padding="4dp">

                    <androidx.appcompat.widget.SearchView
                        android:id="@+id/search_view"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginStart="16dp"
                        android:layout_marginEnd="16dp"
                        android:background="@android:color/white"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent" />
                </FrameLayout>

                <FrameLayout
                    android:id="@+id/fragment_container"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:layout_marginBottom="56dp"
                    app:layout_constraintLeft_toLeftOf="parent"
                    app:layout_constraintTop_toTopOf="parent" />

            </LinearLayout>

        </androidx.core.widget.NestedScrollView>

    </androidx.coordinatorlayout.widget.CoordinatorLayout>

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/navigation"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="0dp"
        android:layout_marginEnd="0dp"
        android:background="?android:attr/windowBackground"
        app:labelVisibilityMode="labeled"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Then in your activity, toggle the visibility of the search in the toolbar when the searchview below it is scrolled in and out of the screen.

public class ScrollSearchActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scroll_search_2);

        final SearchView mainSearchView = findViewById(R.id.search_view);
        final SearchView toolbarSearchView = findViewById(R.id.search_view_2);
        final NestedScrollView nestedScrollView = findViewById(R.id.nestedScrollView);

        nestedScrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
            @Override
            public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {

                boolean isSearchViewVisible = isSearchViewVisible(v,mainSearchView);

                if (isSearchViewVisible){
                    toolbarSearchView.setVisibility(View.GONE);
                    mainSearchView.setVisibility(View.VISIBLE);
                } else{
                    toolbarSearchView.setVisibility(View.VISIBLE);
                    mainSearchView.setVisibility(View.GONE);
                }

            }
        });
    }

    private boolean isSearchViewVisible(NestedScrollView nestedScrollView, View view){
        Rect scrollBounds = new Rect();
        nestedScrollView.getDrawingRect(scrollBounds);

        float top = 0f;
        View view1 = view;

        while (!(view1 instanceof NestedScrollView)){
            top += (view1).getY();
            view1 = (View)view1.getParent();
        }

        float bottom = top + view.getHeight();
        return scrollBounds.top < bottom && scrollBounds.bottom >top;
    }
}

You should get something like this:

enter image description here

EDIT

Use this in place of your isVisible() method

private boolean isViewVisible(NestedScrollView nestedScrollView, View view){
        Rect scrollBounds = new Rect();
        nestedScrollView.getDrawingRect(scrollBounds);

        float top = 0f;
        View view1 = view;

        while (!(view1 instanceof NestedScrollView)){
            top += (view1).getY();
            view1 = (View)view1.getParent();
        }

        float bottom = top + view.getHeight();
        return scrollBounds.top < bottom && scrollBounds.bottom >top;
    }

And call it this way boolean isSearchViewVisible = isSearchViewVisible(nestedScrollView,mainSearchView);

Networks
  • 2,144
  • 1
  • 10
  • 17
4

If you want to achieve an animation like in the gif and actually even better, you can use the new MotionLayout the gist of it is that you create your two states and swipe action in a motion scene. and MotionLayout will take care of everything.

Your layout file (This is only for the header. You can basically define your start state in the layout file)

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layoutDescription="@xml/sample_collapsing_animation_scene">

    <View
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:background="@color/md_green_300" />

    <androidx.appcompat.widget.SearchView
        android:id="@+id/search_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/md_white_1000"
        android:elevation="4dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginStart="36dp"
        android:layout_marginEnd="36dp"
        android:layout_marginTop="40dp"/>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/your_scrolling_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@id/toolbar"/>


</androidx.constraintlayout.motion.widget.MotionLayout>

Your motion scene (Where you define your states and swipe triggers)

<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:motion="http://schemas.android.com/apk/res-auto">

    <Transition
        motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@id/start">
        <OnSwipe
            motion:dragDirection="dragUp"
            motion:touchAnchorId="@id/your_scrolling_content"
            motion:touchAnchorSide="top" />
        <OnSwipe />
    </Transition>

    <ConstraintSet android:id="@+id/start">
        /** Very simple animation no need for start state*/
    </ConstraintSet>

    <ConstraintSet android:id="@+id/end">

        <Constraint
            android:id="@id/search_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="24dp"
            android:layout_marginTop="0dp"
            android:layout_marginEnd="24dp"
            android:elevation="4dp"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toTopOf="parent" />

        <Constraint
            android:id="@id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="56dp" />
    </ConstraintSet>
</MotionScene>

That's all you need, but in a real app, things can be a bit different.

Here is the link for MotionLayout

Here is a great reference on creating a CollapsingToolbar feel using motion layout.

Gil Goldzweig
  • 1,809
  • 1
  • 13
  • 26
  • @Networks and Gil Goldzweig both example is helpful for me because i am trying to combine both answer ... still i am working on it... thanks to u both.. – Vasudev Vyas Nov 22 '19 at 08:54
  • Thanks, I recently started working with `MotionLayout` for creating a `CollapsingToolbar`, and it gave me a lot of flexibility in pretty much every aspect of the transition, it is a new tool that I found to be very powerful for my needs, and especially for simple transitions like you wanted. – Gil Goldzweig Nov 22 '19 at 09:26
  • i feel it is not much smooth scrolling... can you share your code? for me it is just completed and i am also sharing my code for review and suggetion. – Vasudev Vyas Nov 22 '19 at 11:19
  • Mine is not complete. Send me your code and I'll take a look – Gil Goldzweig Nov 22 '19 at 12:00
  • Ok @Vasudev lemme know if u need any explanation(on my answer) – Networks Nov 22 '19 at 16:06
  • i merged both of you answer – Vasudev Vyas Nov 23 '19 at 07:48
  • @Networks when we are hold and scroll slowly at that time searchview not hide .. this is issue how can make exat when it will reach top at that time show both toolbar and searchview and scroll down to bottom hide search view and show into toolbar – Vasudev Vyas Nov 23 '19 at 07:52
  • @VasudevVyas I don't understand your question can you rephrase it? – Networks Nov 23 '19 at 08:26
0

enter image description here

To achieve something similar to this, just paste this code inside your XML Layout:

      <androidx.coordinatorlayout.widget.CoordinatorLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintTop_toTopOf="parent">

            <com.google.android.material.appbar.AppBarLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:fitsSystemWindows="true">

                <com.google.android.material.appbar.MaterialToolbar
                    android:id="@+id/toolbar"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    app:layout_scrollFlags="scroll|enterAlways" />

                <androidx.appcompat.widget.LinearLayoutCompat
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginHorizontal="10dp"
                    android:background="@color/white"
                    android:orientation="horizontal">

                    <androidx.appcompat.widget.AppCompatEditText
                        android:id="@+id/etSearch"
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:background="@null"
                        android:drawableStart="@drawable/ic_toolbar_search"
                        android:drawablePadding="@dimen/margin_6"
                        android:hint="@string/search_hint"
                        android:imeOptions="actionSearch"
                        android:inputType="text"
                        android:maxLines="1"
                        android:padding="5dp"
                        android:textColor="@color/black"
                        android:textColorHint="@color/text_hint"
                        android:textSize="@dimen/text_size_13sp" />

                    <androidx.appcompat.widget.AppCompatImageView
                        android:id="@+id/ivCLear"
                        android:layout_width="@dimen/xlarge_margin"
                        android:layout_height="@dimen/xlarge_margin"
                        android:layout_gravity="center_vertical"
                        android:layout_marginEnd="@dimen/margin_6"
                        android:src="@drawable/ic_close"
                        android:visibility="gone" />
                </androidx.appcompat.widget.LinearLayoutCompat>
            </com.google.android.material.appbar.AppBarLayout>
        </androidx.coordinatorlayout.widget.CoordinatorLayout>
Kishan Solanki
  • 13,761
  • 4
  • 85
  • 82