1. 程式人生 > >利用 CollapsingToolbarLayout 完成聯動的動畫效果

利用 CollapsingToolbarLayout 完成聯動的動畫效果

最近專案中需要實現個動畫效果,研究了下這裡做下簡單的分享.

效果圖如下:

示例.gif

最初的想法是自己去利用 Android 的巢狀滾動機制,去實現上面的巢狀滾動效果.但最後為了開發效率直接利用了 CollapsingToolbarLayout 和 CoordinatorLayout 的效果.
實現效果的原理十分簡單,監聽 CollapsingToolbarLayout 收縮和擴充套件的距離,換算成你想要的一個範圍比如移動的距離,縮放的比例.

部分原始碼:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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" tools:context="com.xf.mylab.activity.CollapsingToolbarLayoutTestActivity">
<android.support.design.widget.CoordinatorLayout
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.wswy.chechengwang.view.activity.SubscriptionDetailActivity"
>
<android.support.design.widget.AppBarLayout android:id="@+id/app_bar" android:layout_width="match_parent" android:layout_height="@dimen/subscription_head"> <android.support.design.widget.CollapsingToolbarLayout android:layout_width="match_parent" android:layout_height="match_parent" app:layout_scrollFlags="scroll|exitUntilCollapsed"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="@dimen/toolbar_height" app:layout_collapseMode="pin"> <ImageView android:id="@+id/back" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:src="@drawable/ic_white_left_arrow" /> </android.support.v7.widget.Toolbar> </android.support.design.widget.CollapsingToolbarLayout> </android.support.design.widget.AppBarLayout> <android.support.v4.widget.NestedScrollView android:id="@+id/subscription_show" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/white" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="夏洛克的貓" android:textSize="30sp" /> </android.support.v4.widget.NestedScrollView> </android.support.design.widget.CoordinatorLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="@dimen/subscription_head" android:gravity="center" android:orientation="vertical"> <ImageView android:id="@+id/iv_head" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_default_subscription" /> <TextView android:id="@+id/subscription_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:text="夏洛克的貓" android:textColor="@color/white" android:textSize="16sp" /> <TextView android:id="@+id/subscribe" android:layout_width="75dp" android:layout_height="24dp" android:layout_marginTop="8dp" android:button="@null" android:checked="false" android:gravity="center" android:text="訂閱" android:textColor="@color/white" /> </LinearLayout> </FrameLayout>
public class CollapsingToolbarLayoutTestActivity extends AppCompatActivity {

    private float mSelfHeight = 0;//用以判斷是否得到正確的寬高值
    private float mTitleScale;
    private float mSubScribeScale;
    private float mSubScribeScaleX;
    private float mHeadImgScale;

    @Bind(R.id.iv_head)
    ImageView mHeadImage;
    @Bind(R.id.subscription_title)
    TextView mSubscriptionTitle;
    @Bind(R.id.subscribe)
    TextView mSubscribe;
    @Bind(R.id.app_bar)
    AppBarLayout mAppBar;
    @Bind(R.id.toolbar)
    Toolbar mToolbar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_collapsing_toolbar_layout_test);
        ButterKnife.bind(this);

        final float screenW = getResources().getDisplayMetrics().widthPixels;
        final float toolbarHeight = getResources().getDimension(R.dimen.toolbar_height);
        final float initHeight = getResources().getDimension(R.dimen.subscription_head);
        mAppBar.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
            @Override
            public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
                if (mSelfHeight == 0) {
                    mSelfHeight = mSubscriptionTitle.getHeight();
                    float distanceTitle = mSubscriptionTitle.getTop() + (mSelfHeight - toolbarHeight) / 2.0f;
                    float distanceSubscribe = mSubscribe.getY() + (mSubscribe.getHeight() - toolbarHeight) / 2.0f;
                    float distanceHeadImg = mHeadImage.getY() + (mHeadImage.getHeight() - toolbarHeight) / 2.0f;
                    float distanceSubscribeX = screenW / 2.0f - (mSubscribe.getWidth() / 2.0f + getResources().getDimension(R.dimen.normal_space));
                    mTitleScale = distanceTitle / (initHeight - toolbarHeight);
                    mSubScribeScale = distanceSubscribe / (initHeight - toolbarHeight);
                    mHeadImgScale = distanceHeadImg / (initHeight - toolbarHeight);
                    mSubScribeScaleX = distanceSubscribeX / (initHeight - toolbarHeight);
                }
                float scale = 1.0f - (-verticalOffset) / (initHeight - toolbarHeight);
                mHeadImage.setScaleX(scale);
                mHeadImage.setScaleY(scale);
                mHeadImage.setTranslationY(mHeadImgScale * verticalOffset);
                mSubscriptionTitle.setTranslationY(mTitleScale * verticalOffset);
                mSubscribe.setTranslationY(mSubScribeScale * verticalOffset);
                mSubscribe.setTranslationX(-mSubScribeScaleX * verticalOffset);
            }
        });
    }
}

程式碼其實沒啥可說的,主要是一些距離的計算,主要就是頂在最上方時,文字都到了居中的位置.大家在圖中畫出開始和結束的位置,都能計算出來.

有一點提一下,最初我是把佈局嵌入到 CollapsingToolbarLayout 中去的,但是由於 CollapsingToolbarLayout 自身的收縮和擴充套件改變了自身高度,會影響裡面的 View 的位置,如果按照靜態的起始和結束位置計算,加上自身高度對 View 佈局的影響,這就複雜了,我沒有細研究.採用了一個笨拙的方式,用 FrameLayout 把佈局給抽取到頂部,不讓他們受到 CollapsingToolbarLayout 的影響.