Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android BottomSheet adapt/shrink related map view

Trying to mimic the current google maps Bar at the bottom. I failed so hard and tried so much; CollapsingToolbar, BottomSheet, custom libs.

What I want: Making the map view adapt it's size and camera when the BottomSheet is sliding, so that the bottom sheet does not slide over the map but instead the map fits in the space that is left.

Take a look at this video to see what I mean (And I don't mean the zoom functionality)

like image 304
EssenceBlue Avatar asked Aug 14 '17 15:08

EssenceBlue


2 Answers

The BottomBar library I used is deprecated. But perhaps my code gives you an idea how it could be done.

I reacted to onSlide events of the BottomSheet like this:

@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
    switch (bssBehavior.getState()) {
        case BottomSheetBehavior.STATE_DRAGGING:
            setMapPaddingBotttom(slideOffset);
            searchVM.map.moveCamera(CameraUpdateFactory.newLatLng(lastValidMapCenter));
            break;
        case BottomSheetBehavior.STATE_SETTLING:
            setMapPaddingBotttom(slideOffset);
            searchVM.map.moveCamera(CameraUpdateFactory.newLatLng(lastValidMapCenter));
            break;
        case BottomSheetBehavior.STATE_HIDDEN:
            break;
        case BottomSheetBehavior.STATE_EXPANDED:
            break;
        case BottomSheetBehavior.STATE_COLLAPSED:
            break;
    }

And smoothly resized the map by changing its padding. For calculating it accordingly I used sth. like this.

private void setMapPaddingBotttom(Float offset) {
    //From 0.0 (min) - 1.0 (max)
    Float maxMapPaddingBottom = bsExpanded - bsCollapsed;
    //left,top,right,bottom
    searchVM.map.setPadding(0, 0, 0, Math.round(offset * maxMapPaddingBottom));
}
like image 67
EssenceBlue Avatar answered Oct 06 '22 00:10

EssenceBlue


Here is a full example to achieve the effect that the Google Map's padding automatically resizes when a BottomSheet on top of it moves/slides. This way the Google logo of the map always moves together with the bottomsheet.

  1. Define your layout. We usually have an activity/fragment with MapFragment and a BottomSheet layout on top of it. Here is a sample with a full screen map:

<androidx.coordinatorlayout.widget.CoordinatorLayout
    android:id="@+id/root_coordinator_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment
        class="com.google.android.gms.maps.SupportMapFragment"
        android:id="@+id/map_fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <LinearLayout
        android:id="@+id/bottom_sheet"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/bottom_sheet_behavior"
        android:elevation="4dp"
        app:behavior_peekHeight="420dp"
        android:orientation="vertical">

        <!-- Your bottom sheet's content-->

    </LinearLayout>

</androidx.coordinatorlayout.widget.CoordinatorLayout>
  1. Setup your activity or fragment. Similar to the idea posted by @EssenceBlue we use the onSlide() callback of the bottomsheet and adjust the map's bottom padding everytime.
class MapWithSheetActivity : AppCompatActivity() {

    private var googleMap: GoogleMap? = null
    private var bottomSheetContainer: CoordinatorLayout? = null
    private var bottomSheetLayout: LinearLayout? = null


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_booking_detail)
        this.bottomSheetLayout = findViewById<LinearLayout>(R.id.bottom_sheet)
        this.bottomSheetContainer = findViewById<CoordinatorLayout>(R.id.root_coordinator_layout)
        initMap()
        initBottomSheet()
    }

    private fun initMap() {
        val mapFragment = supportFragmentManager.findFragmentById(R.id.map_fragment) as? SupportMapFragment
        mapFragment?.getMapAsync { googleMap ->
            this.googleMap = googleMap
        }
    }

    private fun initBottomSheet() {
        val bottomSheetBehavior = BottomSheetBehavior.from(findViewById(R.id.bottom_sheet))
        bottomSheetBehavior.addBottomSheetCallback(object :
            BottomSheetBehavior.BottomSheetCallback() {
            override fun onSlide(bottomSheet: View, slideOffset: Float) {
                when (bottomSheetBehavior.state) {
                    BottomSheetBehavior.STATE_DRAGGING, BottomSheetBehavior.STATE_SETTLING -> {
                        adjustMapPaddingToBottomSheet()
                    }
                    else -> {
                        // No adjustment needed on other slide states.
                    }
                }
            }

            override fun onStateChanged(bottomSheet: View, newState: Int) {
                // Needed only in case you manually change the bottomsheet's state in code somewhere.
                when (newState) {
                    BottomSheetBehavior.STATE_EXPANDED -> {
                        // Nothing to do here
                    }
                    else -> {
                        adjustMapPaddingToBottomSheet()
                    }
                }
            }

        })
    }

    private fun adjustMapPaddingToBottomSheet() {
        googleMap?.let { map ->
            if (this.bottomSheetContainer != null && this.bottomSheetLayout != null) {
                val bottomSheetContainerHeight = this.bottomSheetContainer.height
                val currentBottomSheetTop = this.bottomSheetLayout.top
                map.setPadding(
                    0, // left
                    0, // top
                    0, // right
                    bottomSheetContainerHeight - currentBottomSheetTop // bottom
                )
            }
        }
    }

}
like image 23
user2350644 Avatar answered Oct 06 '22 01:10

user2350644