0

I am working on a project where I use GoogleMap (v2) and Recyclerview (and a bunch of other views). When the app is in portrait mode, (showing map on top, and a recyclerview below), I want it to be possible to "drag" the intersection between the recyclerview and the map, so the map can be higher/lower (and the recyclerview height adjusts accordingly).

In principle, my layout is like this:

<?xml version="1.0" encoding="utf-8"?>
<layout
    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">

    <androidx.appcompat.widget.LinearLayoutCompat
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:background="@color/fragmentBackgroundColor">



        <com.google.android.material.card.MaterialCardView
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:layout_margin="4dp">

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

<!-- other views in same level here , not essentially for the Q-->

                
                <com.google.android.gms.maps.MapView
                    android:id="@+id/fragment_home_map_view"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    app:liteMode="false"
                    app:mapType="hybrid" />

            </androidx.constraintlayout.widget.ConstraintLayout>



        </com.google.android.material.card.MaterialCardView>

        <include
            android:id="@+id/fragment_home_info_card_car_position"
            layout="@layout/info_card_car_position"
            app:layout_constraintTop_toTopOf="parent"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="4dp"
            tools:visibility="visible"
            android:visibility="visible" />

        <include
            android:id="@+id/fragment_home_filter_chips"
            layout="@layout/selection_chips"
            app:layout_constraintTop_toBottomOf="@id/fragment_home_info_card_car_position"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="4dp" />

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:layout_marginStart="4dp"
            android:layout_marginEnd="4dp"
            android:background="?android:attr/listDivider"/>

        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:orientation="vertical"
            android:layout_weight="1">

            <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
                android:id="@+id/fragment_home_swiper_refresh_layout"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintBottom_toTopOf="@id/fragment_home_divider_2"
                android:layout_width="match_parent"
                android:layout_height="0dp">

                <androidx.recyclerview.widget.RecyclerView
                    android:id="@+id/fragment_home_recyclerview"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:fadeScrollbars="false"
                    android:scrollbars="vertical"
                    android:scrollbarSize="4dp"
                    android:scrollbarStyle="outsideOverlay"
                    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
                    tools:listitem="@layout/list_item_order_list"/>



            </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

            <View
                app:layout_constraintBottom_toTopOf="@id/fragment_home_bottom_navigation"
                android:layout_marginBottom="4dp"
                android:id="@+id/fragment_home_divider_2"
                android:layout_width="match_parent"
                android:layout_height="1dp"
                android:background="?android:attr/listDivider"/>

            <com.google.android.material.bottomnavigation.BottomNavigationView
                android:id="@+id/fragment_home_bottom_navigation"
                style="@style/Widget.MaterialComponents.BottomNavigationView.Colored"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:menu="@menu/bottom_navigation_view_menu"
                app:layout_constraintBottom_toBottomOf="parent"
                />

        </androidx.constraintlayout.widget.ConstraintLayout>

    </androidx.appcompat.widget.LinearLayoutCompat>

</layout>

shows where I want drag handle

In the middle between the map and the recyclerview, there are two included views which could be used as drag handler (touch and drag them up/down).

I hope for (and haven't found any yet) some kind if gesture handler I could wrap around those two to make them draggable up/down and resize both map and recyclerview, and leave the other as they are (and let them adjust if needed).

Or do you have any other good suggestions.

Roar Grønmo
  • 2,926
  • 2
  • 24
  • 37
  • See [How to scale a view in android using a handle?](https://stackoverflow.com/questions/23092178/how-to-scale-a-view-in-android-using-a-handle) – ADM Mar 07 '21 at 09:24

2 Answers2

0

I'll suggest BottomSheetBehaviour for this. Just include the Handle + Layout in the Layout inheriting BottomSheetBehavior and will work like charm. And for this, you'll need very few changes in your existing code which is why I've preferred this way.

Second, if you want advanced dragging/swiping with advanced control over the handle, the swipes with Expanding/Shrinking views while swiping, go with MotionLayout. Search for these two, you'll find many answers, or I'll help more.

Lalit Fauzdar
  • 5,953
  • 2
  • 26
  • 50
  • I checked the `BottomSheet` and `BottomSheetBehavior`, but I struggle with your suggestion on Handle & Layout, do you have any link to examples on how this is done ? I'll check out MotionLayout further (I feel I have some more to learn here... ;) ) Thanx anyway!!. I'll upvote and accept when I have checked it out. – Roar Grønmo Mar 08 '21 at 17:24
  • Hey @RoarGrønmo, Have you found a solution yet? I was busy, sorry. So, what I was saying with BottomSheetBehavior is that you can swipe the views using it and by setting up a callback, you can then further expand the views' sizes. As bottomsheet is a `ViewGroup`, what you have to do is make the handle the first child of the `ViewGroup` and it will act like a drag handle (although you can drag from any point of the `ViewGroup` but you can prevent it by interception the touch events of other child views.). – Lalit Fauzdar Mar 17 '21 at 13:33
  • Second, the MotionLayout, the king of all animations of Android, and the control of it, man it's wonderful. What you've to do is to make two ConstraintSets, One for initial position/sizes and One for Final Position and sizes, and it will automatically calculate the in between frames for you. Then, set an `OnSwipe` action on any view, in this case, your handle and it will act like a Drawer which when opened, also expands the sizes of the views, moves them here and there as defined by you. – Lalit Fauzdar Mar 17 '21 at 13:36
  • 1
    @RoarGrønmo, Here, have [this](https://proandroiddev.com/complex-ui-animations-on-android-featuring-motionlayout-aa82d83b8660) link I, myself, prefer for complex animations using `MotionLayout`. Well written articles explaining the `MotionLayout`. – Lalit Fauzdar Mar 17 '21 at 13:38
  • Late comment: I'll check out MotionLayout... Thanx !! – Roar Grønmo Sep 25 '22 at 19:37
0

I have found an answer through this one:

Android make a resizable split screen inside a contraintlayout

In short term:

  1. Divide your layout with a Guideline,and a View as drag handle, embrace it with ConstraintLayout
  2. Make a "drawer handle" with a simple View (add a background color to it)
  3. Reference the guideline to the views over and under the guideline.
  4. In code, listen to onTouchEvent for your draghandle View
  5. Change the percentage of the Guideline respectively.

layout.xml

<layout
    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">

  <data>

  </data>

  <androidx.constraintlayout.widget.ConstraintLayout
      android:id="fragment_home_content_constraint"
      android:layout_widt="match_parent"
      amdroid:layout_height="match_parent">

<!-- I.E. A Card on top-->

      <com.google.android.material.card.MaterialCardView
          android:id="@+id/top_card"
          app:layout_constraintTop_toTopOf="parent"
          app:layout_constraintStart_toStartOf="parent"
          app:layout_constraintEnd_toEndOf="parent"
          app:layout_constraintBottom_toTopOf="@id/fragment_home_drag_handle"
...       >

<!-- The drag handle -->
      <View
          android:id="@+id/fragment_home_drag_handle"
          app:layout_constraintStart_toStartOf="parent"
          app:layout_constraintEnd_toEndOf="parent"
          app:layout_constraintTop_toBottomOf="@id/fragment_home_map_guideline"
          android:background="?android:attr/listDivider"
...       >

<!-- The guideline -->
      <androidx.constraintlayout.widget.Guideline
            android:id="@+id/fragment_home_map_guideline"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            app:layout_constraintGuide_percent="0.5"
            />
<!-- I.E. A card on bottom -->

      <com.google.android.material.card.MaterialCardView
          android:id="@+id/bottom_card"
          app:layout_constraintTop_toBottomOf="@id/fragment_home_drag_handle"
          app:layout_constraintStart_toStartOf="parent"
          app:layout_constraintEnd_toEndOf="parent"
          app:layout_constraintBottom_toBottomOf="parent"
...       >


               

  </androidx.constraintlayout.widget.ConstraintLayout>



</layout>


in code, where you can listen to changes to views. i.e. with bindings.


binding.fragmentHomeDragHandle?.setOnTouchListener { v, event ->

            when(event.actionMasked){
                MotionEvent.ACTION_DOWN -> return@setOnTouchListener true
                MotionEvent.ACTION_MOVE -> {

                val height = binding.fragmentHomeContentConstraint?.height?:0

                val percentage = (binding.fragmentHomeMapGuideline?.layoutParams as ConstraintLayout.LayoutParams?)?.guidePercent
                    

                    val delta = event.y / height.toFloat()

                    var newPercent = percentage?.plus(delta)

                    if((newPercent?:0.0F)<0.04F) newPercent = 0.04F //You can go to 0, but I don't want to loose my draghandle... 4% should do

                    if((newPercent?:0.0F)>0.96F) newPercent = 0.96F //Likewise above, 96% shoudl do

                    newPercent?.let{
                        binding.fragmentHomeMapGuideline?.setGuidelinePercent(it)
                    }

                    Log.i(TAG, "onViewCreated: setOnTouchListener ACTION_MOVE: percentage = $percentage event.y=${event.y}, delta = $delta, newPercent = $newPercent, event.rawY = ${event.rawY}, height = $height")
                    return@setOnTouchListener true
                }
                MotionEvent.ACTION_UP -> {
                    v.performClick()
                    return@setOnTouchListener true
                }
                else-> return@setOnTouchListener false
            }


        }


I hope this helps.

(I don't like to answer my own as solved, but it did here)

Roar Grønmo
  • 2,926
  • 2
  • 24
  • 37