Android開發 - 掌握ConstraintLayout(十一)複雜動畫!如此簡單!

羅伊德發表於2018-12-05

介紹

本系列我們已經介紹了ConstraintLayout的基本用法。學習到這裡,相信你已經熟悉ConstraintLayout的基本使用了,如果你對它的用法還不瞭解,建議您先閱讀我之前的文章

使用ConstraintLayout建立動畫的基本思想是我們建立兩個不同的佈局,每個佈局有其不同的約束,從而我們使用其動畫框架來進行兩種約束之間的切換。

傳統動畫

以往在我們建立簡單動畫時,通常我們會使用

  • 檢視動畫(View Animation)
  • 幀動畫(Drawable Animation)
  • 屬性動畫(Property Animation)

這三種在我們製作簡單動畫時非常簡單和方便,特別是當我們只對某個特定的View製作動畫時。但是當我們需要製作複雜動畫時,特別是整個頁面多個View同時執行動畫時,這幾種方式就顯得力不從心了,需要大量的工作。

當然還有一種方式就是使用轉場動畫框架(Transition Framework),通過共享元素(Shared Element)製作動畫,這個後面我們也會提到。

ConstraintLayout動畫

我們這裡通過一個示例來說明ConstraintLayout動畫的建立。

  1. 首先,我們建立第一個佈局(activity_main.xml),它是是我們的初始佈局。
  • 效果:

    -w290

  • 程式碼:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    android:id="@+id/cl_root"
    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=".MainActivity">

    <TextView
        android:id="@+id/tv_score"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="評分:8.3分"
        app:layout_constraintStart_toStartOf="@+id/tv_name"
        app:layout_constraintTop_toBottomOf="@+id/tv_name" />

    <TextView
        android:id="@+id/tv_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="20dp"
        android:layout_marginTop="8dp"
        android:text="無敵破壞王2"
        android:textColor="#282828"
        android:textSize="20sp"
        app:layout_constraintStart_toEndOf="@+id/iv_poster"
        app:layout_constraintTop_toTopOf="@+id/iv_poster" />

    <ImageView
        android:id="@+id/iv_poster"
        android:layout_width="120dp"
        android:layout_height="160dp"
        android:layout_marginStart="20dp"
        android:layout_marginTop="20dp"
        android:scaleType="centerCrop"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@mipmap/wreck_it_ralph" />

</android.support.constraint.ConstraintLayout>
複製程式碼

我們的初始佈局建立完畢後,我們需要建立一個動畫結束佈局,讓動畫框架知道執行完動畫後佈局是什麼樣的。

  1. 建立動畫結束佈局(activity_main_detail.xml)。
  • 效果:

-w289

  • 程式碼:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    android:id="@+id/cl_root"
    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=".MainActivity">

    <TextView
        android:id="@+id/tv_score"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="52dp"
        android:text="評分:8.3分"
        app:layout_constraintBottom_toBottomOf="@+id/tv_name"
        app:layout_constraintStart_toEndOf="@+id/tv_name"
        app:layout_constraintTop_toTopOf="@+id/tv_name"/>

    <TextView
        android:id="@+id/tv_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="20dp"
        android:layout_marginTop="20dp"
        android:text="無敵破壞王2"
        android:textColor="#282828"
        android:textSize="20sp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/iv_poster"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginTop="65dp"
        android:scaleType="centerCrop"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@mipmap/wreck_it_ralph" />

</android.support.constraint.ConstraintLayout>
複製程式碼

這個頁面是我們執行動畫結束後的樣子。那麼開始和結束的佈局我們都有了,我們怎樣執行動畫,讓兩個佈局之間進行過渡呢?

答案是通過Android的TransitionManager來執行。

  1. 使用TransitionManager來執行動畫
  • 程式碼(MainActivity.java)
package cn.examplecode.constraintlayoutdemo;

import android.support.constraint.ConstraintLayout;
import android.support.constraint.ConstraintSet;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.transition.TransitionManager;

public class MainActivity extends AppCompatActivity {

    private ConstraintLayout mConstraintLayout;
    private boolean mIsDetail;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mConstraintLayout = findViewById(R.id.cl_root);

        ConstraintSet constraintSet1 = new ConstraintSet();
        ConstraintSet constraintSet2 = new ConstraintSet();

        constraintSet2.clone(this, R.layout.activity_main_detail);
        constraintSet1.clone(mConstraintLayout);

        findViewById(R.id.iv_poster).setOnClickListener(v -> {
            TransitionManager.beginDelayedTransition(mConstraintLayout);
            if (!mIsDetail) {
                constraintSet2.applyTo(mConstraintLayout);
                mIsDetail = true;
            } else {
                constraintSet1.applyTo(mConstraintLayout);
                mIsDetail = false;
            }
        });
    }
}
複製程式碼

我們來解釋以上程式碼。 首先我們發現使用了這個類ConstraintSet,它是一個約束集合,儲存了佈局上所有元素的約束,因為我們需要在兩個佈局之間執行動畫,所以我們需要建立兩個約束集合的物件。

ConstraintSet constraintSet1 = new ConstraintSet();
ConstraintSet constraintSet2 = new ConstraintSet();
複製程式碼

建立完約束集物件後,我們需要設定每個約束集對應的約束:

constraintSet2.clone(this, R.layout.activity_main_detail);
constraintSet1.clone(mConstraintLayout);
複製程式碼

這裡我們將當前佈局的約束應用到constraintSet1中,將目標佈局的約束應用到constraintSet2中。

接下來我們執行動畫:

TransitionManager.beginDelayedTransition(mConstraintLayout);
# 從constraintSet1過渡到constraintSet2
constraintSet2.applyTo(mConstraintLayout);

# 從constraintSet2過渡到constraintSet1
constraintSet1.applyTo(mConstraintLayout);
複製程式碼

最終效果:

ezgif-5-34173ef48cf2

只需簡單的幾行程式碼,就可以實現複雜的動畫了!當然本示例為了說明ConstraintLayout動畫的建立方法,佈局比較簡單。 如果需要複雜佈局的動畫切換,這種方式的優勢就非常明顯。如果使用傳統建立動畫方法,則有可能需要大量的時間和程式碼來實現。

問題探討

為什麼需要建立兩個佈局檔案?

可能有人認為建立兩個佈局檔案不是一個好的方式,兩個佈局中存在重複的程式碼,這樣好嗎?

實際上可能並沒有你想象的那麼不好,建立兩個佈局檔案的目的只是讓動畫框架知道不同的約束而已,然後將不同的約束應用在過渡動畫中,你可以在佈局中把與約束無關的屬性去掉。 如果你實在不喜歡建立兩個佈局檔案的話,當然也可以在程式碼中來描述不同的約束。顯然這樣會大大增加複雜度和程式碼量。

與使用共享元素動畫(Shared Element)有什麼區別?

使用共享元素動畫當然也可以實現這樣的效果,但是使用共享元素動畫你也需要建立兩個佈局,而且關鍵的不同是: 使用ConstraintLayout執行動畫時,動畫前後的View是同一個View物件。 而使用共享元素動畫時動畫前後的View是兩個不同的View物件!

系列總結

本篇是本系列部落格《掌握ConstraintLayout》的最後一篇。通過本系列的學習,相信你已經掌握了使用ConstraintLayout的大部分功能。

在實際開發的過程中,使用ConstraintLayout會使開發速度有不少的提升,再結合我的另一個系列《使用Data Binding》,會大大減少開發Android時的工作量,達到事半功倍的效果,提升生產力!

謝謝你的支援!

如有更多疑問,請參考我的其它Android相關部落格:我的部落格地址

相關文章