Search code examples
androidkotlinandroid-constraintlayoutontouchlistenerandroid-motionlayout

How to refresh height of a Constrained View in ConstraintLayout


I have a ConstraintLayout in which i have a View(called Bar) and a RecyclerView Constrained to The Bottom of The Bar, The RecyclerView has it's height set to match Constraint(0dp),

So if in The Android Layout Editor i move up The Bar for example, The RecyclerView height increase and is always "anchored" To The Bar, it's work,

But This is Not the same behavior at runtime, when i move the Bar(with an onTouchListener), The RecyclerView height does not change at all and stay like if the Bar was at the same position..

To achieve this Behavior(move the Bar increase/decrease the RecyclerView height), i have thinked and tried to The ConstraintLayout Solution,

So i have set a constraint(Top) to the Bottom of the Bar and i have set the height to match the Constraint,

I have also tried to achieve this behavior with the LayoutParam, changing the height based on pixels moved, But the formula is not 100% good and using this way change height from both bottom and top of the view(apparently not with the Constraint solution)

<ConstraintLayout>
...

 <!-- Removed all Constraint to the Bar to let it freely move on Touch on it's Y Axis, also tried to constraint start, end, top to the parent, but same behavior -->
 <include layout="@layout/theBar"
             android:layout_width="match_parent" 
             android:layout_height="wrap_content"
             android:id="@+id/theBarWhoMove"/>

    <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerViewConstrainedToTheBar"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_marginBottom="2dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            android:layout_marginStart="2dp"
            app:layout_constraintEnd_toEndOf="parent"
            android:layout_marginEnd="2dp"
            android:layout_marginTop="8dp"              

        app:layout_constraintTop_toBottomOf="@+id/theBarWhoMove"/>

...
</ConstraintLayout>


barWhoMove.setOnTouchListener { v, event ->

        when(event.actionMasked) {

            MotionEvent.ACTION_DOWN -> {
                barWhoMoveDY = v.y - event.rawY
                return@setOnTouchListener true
            }
            MotionEvent.ACTION_MOVE -> {
                ////We move the Bar
                v.animate().translationY(barWhoMoveDY + 
                event.rawY).setDuration(0).start()

               /* To try to relayout and resize based on match Constrain taking in consideration the new barWhoMove position*/
                recyclerViewConstrainedToTheBar.invalidate()
                recyclerViewConstrainedToTheBar.requestLayout()
                recyclerViewConstrainedToTheBar.forceLayout()

                ////Tried Also
   constraintSet.constrainHeight(recyclerViewConstrainedToTheBar.id, 
ConstraintSet.MATCH_CONSTRAINT)
   constraintSet.applyTo(rootConstraintLayout)

   ////Tried Also
   rootConstraintLayout.invalidate()
   rootConstraintLayout.requestLayout()
   rootConstraintLayout.forceLayout()

                if(v.y < oldDY) {

               /*#Solution 2 increase with LayoutParam but The formula is not 100% correct and it change height from both bottom and top, When View is moving Up or Down(it's work for detecting this)*/

              ....
              oldDy = v.y
                }
                else {
                ....
                oldDy = v.y
                }

                return@setOnTouchListener true
            }
            MotionEvent.ACTION_UP -> {
            }
        }

        return@setOnTouchListener false
    }

Edit :

This is the content of The <include> tag in the layout 1 :

<?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"
    tools:showIn="@layout/fragment_layout_main">

<data>
    <variable name="variable" type="com.demo.ourClass" />
</data>

<android.support.v7.widget.CardView
  android:id="@+id/card"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  app:layout_constraintStart_toStartOf="parent"
  app:layout_constraintTop_toBottomOf="@id/textViewTitle"
  android:layout_marginTop="8dp"
  app:cardElevation="6dp">

  <android.support.constraint.ConstraintLayout 
  android:layout_width="match_parent"                                        
  android:layout_height="match_parent">
  <ImageView
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          app:srcCompat="@mipmap/ic_launcher_round"
          android:id="@+id/imageViewA"
          android:layout_marginTop="8dp"
          app:layout_constraintTop_toTopOf="parent"
          app:layout_constraintStart_toStartOf="parent"
          android:layout_marginStart="8dp"/>
  <TextView
          android:text="@{variable.name}"
          tools:text="TextView"
          android:textAppearance="@style/TextAppearance.AppCompat.Light.SearchResult.Title"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:id="@+id/textViewA"
          app:layout_constraintStart_toEndOf="@+id/imageViewA"
          app:layout_constraintBottom_toBottomOf="@id/imageViewA"
          android:layout_marginStart="8dp" 
          android:layout_marginTop="8dp"
          app:layout_constraintTop_toTopOf="parent"/>

  </android.support.constraint.ConstraintLayout>

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


Solution

  • You are animating the translationY property of the view. Changing this value moved the view post layout. In other words, the layout is completed then the move is made. As a result, the RecyclerView is constrained to the post-layout position before the translation. See setTranslationY(float):

    Sets the vertical location of this view relative to its top position. This effectively positions the object post-layout, in addition to wherever the object's layout placed it.