介紹
本系列我們已經(jīng)介紹了ConstraintLayout的基本用法婉宰。學(xué)習(xí)到這里,相信你已經(jīng)熟悉ConstraintLayout的基本使用了推穷,如果你對(duì)它的用法還不了解心包,建議您先閱讀我之前的文章。
使用ConstraintLayout創(chuàng)建動(dòng)畫(huà)的基本思想是我們創(chuàng)建兩個(gè)不同的布局缨恒,每個(gè)布局有其不同的約束,從而我們使用其動(dòng)畫(huà)框架來(lái)進(jìn)行兩種約束之間的切換轮听。
傳統(tǒng)動(dòng)畫(huà)
以往在我們創(chuàng)建簡(jiǎn)單動(dòng)畫(huà)時(shí)骗露,通常我們會(huì)使用
- 視圖動(dòng)畫(huà)(View Animation)
- 幀動(dòng)畫(huà)(Drawable Animation)
- 屬性動(dòng)畫(huà)(Property Animation)
這三種在我們制作簡(jiǎn)單動(dòng)畫(huà)時(shí)非常簡(jiǎn)單和方便,特別是當(dāng)我們只對(duì)某個(gè)特定的View制作動(dòng)畫(huà)時(shí)血巍。但是當(dāng)我們需要制作復(fù)雜動(dòng)畫(huà)時(shí)萧锉,特別是整個(gè)頁(yè)面多個(gè)View同時(shí)執(zhí)行動(dòng)畫(huà)時(shí),這幾種方式就顯得力不從心了述寡,需要大量的工作柿隙。
當(dāng)然還有一種方式就是使用轉(zhuǎn)場(chǎng)動(dòng)畫(huà)框架(Transition Framework),通過(guò)共享元素(Shared Element)制作動(dòng)畫(huà)鲫凶,這個(gè)后面我們也會(huì)提到禀崖。
ConstraintLayout動(dòng)畫(huà)
我們這里通過(guò)一個(gè)示例來(lái)說(shuō)明ConstraintLayout動(dòng)畫(huà)的創(chuàng)建。
- 首先螟炫,我們創(chuàng)建第一個(gè)布局(activity_main.xml)波附,它是是我們的初始布局。
-
效果:
代碼:
<?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="評(píng)分: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="無(wú)敵破壞王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>
我們的初始布局創(chuàng)建完畢后昼钻,我們需要?jiǎng)?chuàng)建一個(gè)動(dòng)畫(huà)結(jié)束布局掸屡,讓動(dòng)畫(huà)框架知道執(zhí)行完動(dòng)畫(huà)后布局是什么樣的。
- 創(chuàng)建動(dòng)畫(huà)結(jié)束布局(activity_main_detail.xml)然评。
- 效果:
- 代碼:
<?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="評(píng)分: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="無(wú)敵破壞王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>
這個(gè)頁(yè)面是我們執(zhí)行動(dòng)畫(huà)結(jié)束后的樣子仅财。那么開(kāi)始和結(jié)束的布局我們都有了,我們?cè)鯓訄?zhí)行動(dòng)畫(huà)碗淌,讓兩個(gè)布局之間進(jìn)行過(guò)渡呢盏求?
答案是通過(guò)Android的TransitionManager來(lái)執(zhí)行抖锥。
- 使用TransitionManager來(lái)執(zhí)行動(dòng)畫(huà)
- 代碼(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;
}
});
}
}
我們來(lái)解釋以上代碼。
首先我們發(fā)現(xiàn)使用了這個(gè)類(lèi)ConstraintSet风喇,它是一個(gè)約束集合宁改,保存了布局上所有元素的約束,因?yàn)槲覀冃枰趦蓚€(gè)布局之間執(zhí)行動(dòng)畫(huà)魂莫,所以我們需要?jiǎng)?chuàng)建兩個(gè)約束集合的對(duì)象还蹲。
ConstraintSet constraintSet1 = new ConstraintSet();
ConstraintSet constraintSet2 = new ConstraintSet();
創(chuàng)建完約束集對(duì)象后,我們需要設(shè)置每個(gè)約束集對(duì)應(yīng)的約束:
constraintSet2.clone(this, R.layout.activity_main_detail);
constraintSet1.clone(mConstraintLayout);
這里我們將當(dāng)前布局的約束應(yīng)用到constraintSet1中耙考,將目標(biāo)布局的約束應(yīng)用到constraintSet2中谜喊。
接下來(lái)我們執(zhí)行動(dòng)畫(huà):
TransitionManager.beginDelayedTransition(mConstraintLayout);
# 從constraintSet1過(guò)渡到constraintSet2
constraintSet2.applyTo(mConstraintLayout);
# 從constraintSet2過(guò)渡到constraintSet1
constraintSet1.applyTo(mConstraintLayout);
最終效果:
只需簡(jiǎn)單的幾行代碼,就可以實(shí)現(xiàn)復(fù)雜的動(dòng)畫(huà)了倦始!當(dāng)然本示例為了說(shuō)明ConstraintLayout動(dòng)畫(huà)的創(chuàng)建方法斗遏,布局比較簡(jiǎn)單。
如果需要復(fù)雜布局的動(dòng)畫(huà)切換鞋邑,這種方式的優(yōu)勢(shì)就非常明顯诵次。如果使用傳統(tǒng)創(chuàng)建動(dòng)畫(huà)方法,則有可能需要大量的時(shí)間和代碼來(lái)實(shí)現(xiàn)枚碗。
問(wèn)題探討
為什么需要?jiǎng)?chuàng)建兩個(gè)布局文件逾一?
可能有人認(rèn)為創(chuàng)建兩個(gè)布局文件不是一個(gè)好的方式,兩個(gè)布局中存在重復(fù)的代碼肮雨,這樣好嗎遵堵?
實(shí)際上可能并沒(méi)有你想象的那么不好,創(chuàng)建兩個(gè)布局文件的目的只是讓動(dòng)畫(huà)框架知道不同的約束而已怨规,然后將不同的約束應(yīng)用在過(guò)渡動(dòng)畫(huà)中陌宿,你可以在布局中把與約束無(wú)關(guān)的屬性去掉。
如果你實(shí)在不喜歡創(chuàng)建兩個(gè)布局文件的話(huà)波丰,當(dāng)然也可以在代碼中來(lái)描述不同的約束壳坪。顯然這樣會(huì)大大增加復(fù)雜度和代碼量。
與使用共享元素動(dòng)畫(huà)(Shared Element)有什么區(qū)別掰烟?
使用共享元素動(dòng)畫(huà)當(dāng)然也可以實(shí)現(xiàn)這樣的效果弥虐,但是使用共享元素動(dòng)畫(huà)你也需要?jiǎng)?chuàng)建兩個(gè)布局,而且關(guān)鍵的不同是:
使用ConstraintLayout執(zhí)行動(dòng)畫(huà)時(shí)媚赖,動(dòng)畫(huà)前后的View是同一個(gè)View對(duì)象霜瘪。
而使用共享元素動(dòng)畫(huà)時(shí)動(dòng)畫(huà)前后的View是兩個(gè)不同的View對(duì)象!
系列總結(jié)
本篇是本系列博客《掌握ConstraintLayout》的最后一篇惧磺。通過(guò)本系列的學(xué)習(xí)颖对,相信你已經(jīng)掌握了使用ConstraintLayout的大部分功能。
在實(shí)際開(kāi)發(fā)的過(guò)程中磨隘,使用ConstraintLayout會(huì)使開(kāi)發(fā)速度有不少的提升缤底,再結(jié)合我的另一個(gè)系列《使用Data Binding》顾患,會(huì)大大減少開(kāi)發(fā)Android時(shí)的工作量,達(dá)到事半功倍的效果个唧,提升生產(chǎn)力江解!
謝謝你的支持!
如有更多疑問(wèn)徙歼,請(qǐng)參考我的其它Android相關(guān)博客:我的博客地址