Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How I Can make Scrolling toolbar with search box like amazone

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;
}
like image 460
Vasudev Vyas Avatar asked Nov 19 '19 04:11

Vasudev Vyas


2 Answers

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);

like image 80
Networks Avatar answered Sep 23 '22 00:09

Networks


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.

like image 20
Gil Goldzweig Avatar answered Sep 25 '22 00:09

Gil Goldzweig