如何使用 Coordinator Layout and Behavior 在 CardView 上完成这个动画?

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

我正在尝试使用协调器布局实现以下效果:

我花了大约 30 小时以上的时间试图解决这个问题,我知道解决方法是使用 Coordinator Layout Behavior,但有很多地方我都弄错了。

这是我的 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="0"
                        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="00"
                        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>

我创建了一个行为子类来尝试为今天的收入标签(绿色)设置动画:

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
     }
}

但是,如果 TextView 不是 CoordinatorLayout 的子项,我如何在 xml 到 app:layout_behavior 中将此行为设置为 $800 TextView?

任何解决此问题的方法(可能不涉及 Coordinator Layout)也非常受欢迎。

给你:

有几点重要说明:

  • 因为我们在scrollview中定义了recycler,它不会回收views!!!!所以尽量不要在列表中放置很多图形对象
  • 视图的高度和宽度应在创建整个视图后计算。否则你会得到零。这就是为什么我使用 globalLayoutListener

我这样声明我的 activity :

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

我的xml也是如下:

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