Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to accomplish this animation on CardView with Coordinator Layout and Behavior?

I am trying to accomplish the following effect with coordinator layout:

enter image description here

I've spent about 30h+ trying to solve this and I know the way to go is with Coordinator Layout Behavior, but there are a lot of things I am getting wrong.

Here's my xml:

<android.support.constraint.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="com.example.StatisticsFragment">

<!-- TODO: Update blank fragment layout -->

<android.support.design.widget.CoordinatorLayout
    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"
    android:background="@android:color/background_light">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/main.appbar"
        android:layout_width="match_parent"
        android:layout_height="137dp"
        android:background="@android:color/transparent">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/main.collapsing"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            app:expandedTitleMarginStart="48dp"
            app:expandedTitleMarginEnd="64dp">

            <android.support.v7.widget.CardView
                android:id="@+id/earningsCardView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginEnd="8dp"
                android:layout_marginStart="8dp"
                android:layout_marginTop="8dp"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent">

                <android.support.constraint.ConstraintLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent">

                    <android.support.v7.widget.AppCompatTextView
                        android:id="@+id/todayLabel"
                        android:layout_width="108dp"
                        android:layout_height="26dp"
                        android:layout_marginEnd="8dp"
                        android:layout_marginStart="8dp"
                        android:layout_marginTop="12dp"
                        android:gravity="center"
                        android:text="TODAY"
                        android:textAlignment="center"
                        android:textColor="@android:color/darker_gray"
                        android:textSize="18sp"
                       app:layout_constraintEnd_toStartOf="@+id/guideline"
                        app:layout_constraintStart_toStartOf="parent"
                        app:layout_constraintTop_toTopOf="parent" />


                    <android.support.v7.widget.AppCompatTextView
                        android:id="@+id/weekLabel"
                        android:layout_width="108dp"
                        android:layout_height="26dp"
                        android:layout_marginEnd="8dp"
                        android:layout_marginStart="8dp"
                        android:layout_marginTop="12dp"
                        android:gravity="center"
                        android:text="WEEK"
                        android:textAlignment="center"
                        android:textColor="@android:color/darker_gray"
                        android:textSize="18sp"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintStart_toStartOf="@+id/guideline"
                        app:layout_constraintTop_toTopOf="parent" />


                    <android.support.v7.widget.AppCompatTextView
                        android:id="@+id/weekEarningsLabel"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="8dp"
                        android:gravity="center"
                        android:text="$800"
                        android:textAlignment="center"
                        android:textColor="@color/colorAccent"
                        android:textSize="32sp"
                        app:layout_constraintEnd_toEndOf="@+id/todayLabel"
                        app:layout_constraintStart_toStartOf="@+id/todayLabel"
                        app:layout_constraintTop_toBottomOf="@+id/todayLabel" />


                    <android.support.v7.widget.AppCompatTextView
                        android:id="@+id/todayEarningsLabel"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginEnd="8dp"
                        android:layout_marginTop="8dp"
                        android:gravity="center"
                        android:text="$5200"
                        android:textAlignment="center"
                        android:textColor="@color/green_light"
                        android:textSize="32sp"
                        app:layout_constraintEnd_toEndOf="@+id/weekLabel"
                        app:layout_constraintStart_toStartOf="@+id/weekLabel"
                        app:layout_constraintTop_toBottomOf="@+id/weekLabel" />

                    <android.support.constraint.Guideline
                        android:id="@+id/guideline"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:orientation="vertical"
                        app:layout_constraintEnd_toEndOf="parent"
                        app:layout_constraintGuide_percent="0.5012658"
                        app:layout_constraintHorizontal_bias="0.5"
                        app:layout_constraintStart_toStartOf="parent"/>

                    <android.support.v7.widget.AppCompatTextView
                        android:id="@+id/todayOrderAmount"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="8dp"
                        android:layout_marginBottom="16dp"
                        android:text="18 deliveries"
                        android:textSize="12sp"
                        app:layout_constraintEnd_toEndOf="@+id/weekEarningsLabel"
                        app:layout_constraintStart_toStartOf="@+id/weekEarningsLabel"
                        app:layout_constraintTop_toBottomOf="@+id/weekEarningsLabel" />

                    <TextView
                        android:id="@+id/weekOrderAmount"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginEnd="8dp"
                        android:layout_marginStart="8dp"
                        android:layout_marginTop="8dp"
                        android:layout_marginBottom="8dp"
                        android:text="87 deliveries"
                        android:textSize="12sp"
                        app:layout_constraintEnd_toEndOf="@+id/todayEarningsLabel"
                        app:layout_constraintHorizontal_bias="0.51"
                        app:layout_constraintStart_toStartOf="@+id/todayEarningsLabel"
                        app:layout_constraintTop_toBottomOf="@+id/todayEarningsLabel" />

                </android.support.constraint.ConstraintLayout>

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

        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>

    <android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/historicRecyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/earningsCardView" />
    </android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>

I created a behavior subclass to try to animate the today earnings label (in green):

class TodayEarningsLabelBehavior : CoordinatorLayout.Behavior<TextView>() {
     override fun layoutDependsOn(parent: CoordinatorLayout?, child: TextView?, dependency: View?): Boolean {
        return dependency is CardView
     }

     override fun onDependentViewChanged(parent: CoordinatorLayout?, child: TextView?, dependency: View?): Boolean {
        if (child != null){
            child.textSize = dependency!!.height / 3.0F
        }

        return false
     }
}

But how do I set this behavior in xml through app:layout_behavior to the $800 TextView if it's not a child of CoordinatorLayout?

Any method of solving this problem (which might not involve Coordinator Layout) is more than welcome as well.

like image 363
rgoncalv Avatar asked Jan 13 '18 01:01

rgoncalv


People also ask

What is a coordinator layout?

CoordinatorLayout is a super-powered FrameLayout . CoordinatorLayout is intended for two primary use cases: As a top-level application decor or chrome layout. As a container for a specific interaction with one or more child views.

What is CoordinatorLayout and ConstraintLayout?

Use Coordinatorlayout as the top-level application decor. It will usually used to layout AppBarLayout , FloatingActionButton , and the main body of your screen, say NestedScrollView . Inside the NestedScrollView use ConstraintLayout to describe the rest of the layout as a flat hierarchy.


1 Answers

Here you are :

screenShot of the result

There are a Few Important notes:

  • because we define the recycler in the scrollview, it wont recycle the views!!!! so try to not put many graphical objects in your list
  • Views height and width should be calculated after the whole view is created. otherwise you will get zero. That is why i use globalLayoutListener

I declare my activity as this :

public class CustomCardAnimationActivity extends AppCompatActivity {


NestedScrollView scrollView;
RecyclerView recyclerView;
CardView cardView;
TextView tvToday;
TextView tvTodayPrice;
TextView tvTodayDelivery;
TextView tvWeek;
TextView tvWeekPrice;
TextView tvWeekDelivery;
int cardHeight;
int textViewHeight;
float tvTitleTodayX;
float tvPriceTodayX;
float tvTitleWeekX;
float tvPriceWeekX;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_custom_card_animation);

    recyclerView= (RecyclerView) findViewById(R.id.recyclerView);
    scrollView= (NestedScrollView) findViewById(R.id.nested_scrollView);
    cardView= (CardView) findViewById(R.id.cardView);
    tvToday=(TextView) findViewById(R.id.textView_today);
    tvTodayPrice=(TextView) findViewById(R.id.textView_today_price);
    tvTodayDelivery=(TextView) findViewById(R.id.textView_today_delivery);
    tvWeek=(TextView) findViewById(R.id.textView_week);
    tvWeekPrice=(TextView) findViewById(R.id.textView_week_price);
    tvWeekDelivery=(TextView) findViewById(R.id.textView_week_delivery);
    scrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
        @Override
        public void onScrollChanged() {
            int scrollY = scrollView.getScrollY();
            FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) cardView.getLayoutParams();
            int height = Math.max(textViewHeight,cardHeight-scrollY);
            lp.height = height;
            cardView.setLayoutParams(lp);

            // alpha delivery textViews
            tvTodayDelivery.setAlpha(Math.max(0f,(float) ((cardHeight/2)-scrollY)/(cardHeight/2)));
            tvWeekDelivery.setAlpha(Math.max(0f,(float) ((cardHeight/2)-scrollY)/(cardHeight/2)));

            // move titles to left
            float titleMovementChange = Math.max(-scrollY , -textViewHeight);
            tvToday.setX(tvTitleTodayX + titleMovementChange);
            tvWeek.setX(tvTitleWeekX + titleMovementChange*1.2f);

            // move prices to right
            float priceMovementChange = Math.max(-scrollY , -textViewHeight/2);
            tvTodayPrice.setX(tvPriceTodayX - priceMovementChange);
            tvWeekPrice.setX(tvPriceWeekX - priceMovementChange * 1.2f);
        }
    });

    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    recyclerView.setAdapter(new TestListAdapter());
    recyclerView.setNestedScrollingEnabled(false);

    //if you remove this part, the card would be shown in its minimum state at start
    recyclerView.post(new Runnable() {
        @Override
        public void run() {
            scrollView.scrollTo(0,0);
        }
    });

    // The calculation for heights of views should be done after the view created
    View rootView = findViewById(R.id.root_view);
    rootView.getViewTreeObserver().addOnGlobalLayoutListener(
            new ViewTreeObserver.OnGlobalLayoutListener() {
                public void onGlobalLayout() {
                    //Remove the listener before proceeding
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                        rootView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                    } else {
                        rootView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                    }

                    // measure your views here
                    cardHeight = cardView.getHeight();
                    textViewHeight= tvToday.getHeight();
                    tvTitleTodayX = tvToday.getX();
                    tvPriceTodayX = tvTodayPrice.getX();
                    tvTitleWeekX = tvWeek.getX();
                    tvPriceWeekX = tvWeekPrice.getX();
                    scrollView.scrollTo(0,0);
                }
            });
}
}

also my xml is as below :

<FrameLayout
android:id="@+id/root_view"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="#0099cc"
tools:context="com.bisphone.interviewtest.test.CustomCardAnimationActivity">

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp">
    <android.support.v7.widget.CardView
        android:id="@+id/cardView"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:layout_margin="16dp"
        app:cardElevation="4dp">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="horizontal">
        <FrameLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center">
            <TextView
                android:id="@+id/textView_today"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:layout_gravity="top|center"
                android:padding="8dp"
                android:textSize="12sp"
                android:text="TODAY"/>

            <TextView
                android:id="@+id/textView_today_price"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="800 $"
                android:gravity="center"
                android:padding="8dp"
                android:textSize="14sp"
                android:textStyle="bold"
                android:textAlignment="center"
                android:layout_gravity="center"
                android:textColor="#4caf50"/>

            <TextView
                android:id="@+id/textView_today_delivery"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="18 Deliveries"
                android:padding="8dp"
                android:textSize="12sp"
                android:layout_gravity="bottom|center"
                android:gravity="center"/>
        </FrameLayout>
            <FrameLayout
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center">
                <TextView
                    android:id="@+id/textView_week"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:gravity="center"
                    android:layout_gravity="top|center"
                    android:textSize="12sp"
                    android:padding="8dp"
                    android:text="THIS WEEK"/>

                <TextView
                    android:id="@+id/textView_week_price"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="5200 $"
                    android:textSize="14sp"
                    android:gravity="center"
                    android:layout_gravity="center"
                    android:textStyle="bold"
                    android:textAlignment="center"
                    android:textColor="#009688"/>

                <TextView
                    android:id="@+id/textView_week_delivery"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="87 Deliveries"
                    android:layout_gravity="bottom|center"
                    android:textSize="12sp"
                    android:gravity="center"
                    android:padding="8dp"/>
            </FrameLayout>
        </LinearLayout>
    </android.support.v7.widget.CardView>
    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v4.widget.NestedScrollView
            android:id="@+id/nested_scrollView"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">
                <FrameLayout
                    android:layout_width="match_parent"
                    android:layout_height="100dp"
                    android:layout_margin="16dp"/>
                <android.support.v7.widget.RecyclerView
                    android:id="@+id/recyclerView"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:nestedScrollingEnabled="false"/>
            </LinearLayout>
        </android.support.v4.widget.NestedScrollView>
    </android.support.v7.widget.CardView>

</FrameLayout>

like image 99
Omid Heshmatinia Avatar answered Oct 16 '22 06:10

Omid Heshmatinia