原文地址:https://www.raywenderlich.com/9475-constraintlayout-tutorial-for-android-complex-layouts
- 轉換其他類型的布局到ConstraintLayout
- 相對于在屏幕上的其他元素動態(tài)定位UI元素
- 讓你的Views實現(xiàn)動畫
在這篇教程中豌汇,會構建一個星際旅行的app瓣颅,火箭將會繞著行星不停的轉動芋齿。
在這個app中有很多元素,你能夠學到如何使用復雜的ConstraintLayout來正常的顯示他們瞒津。
轉換一個布局到ConstraintLayout
在Component Tree面板中,在最頂層的布局中右鍵并且選擇Convert LinearLayout to ConstraintLayout:
接著會看到一個彈出框并且有一些選項:
閱讀完所有內(nèi)容后不改變默認的選中狀態(tài)携取,然后點擊ok關閉對話框太示,AndroidStudio接下來就會把你的布局轉換成ConstraintLayout。
轉換過后香浩,你的布局就變成下面的這個樣子:
如果此時所有的視圖都跑到了左上角类缤,請不要驚慌,
請確保關閉AutoConnect
image
移除推斷約束
執(zhí)行轉換的過程中邻吭,AndroidStudio會執(zhí)行好幾個步驟餐弱,最后一步就是進行推斷約束,但是結果并不是你想要的,此時膏蚓,你只需要轉到編輯菜單并選擇撤銷推斷約束瓢谢。
或者,執(zhí)行cmd+Z驮瞧,現(xiàn)在你的界面看起來如下:
你可以稍微拖動視圖氓扛,讓他看起來更像原始的樣子:
如果在拖動過程中Android Studio自動添加了任何的約束,可以點擊Clear All Constraints按鈕去清除他們论笔。
調整Image
通過點擊頂部的每個圖標采郎,spaceStationIcon,flightIcon和roverIcon來修復圖像的大小狂魔,接著在屬性面板蒜埋,將layout_width和layout_heigth從wrap_content改為30dp。
此時你將會在Comoonent Tree中看到一堆錯誤最楷,這是因為沒有任何約束信息告訴它在哪里定位整份,現(xiàn)在開始解決這個問題。
添加約束:找出對齊方式
使用自上而下的方法設置約束籽孙,從屏幕頂部的元素開始烈评,一直向下設置。
希望頂部三個圖標水平分布排列蚯撩,然后標簽置于圖標下方
約束第一個圖標
單擊第一個圖標并且顯示約束錨點础倍,點擊上面的錨點并且拖動到頂部的view,該圖標就會自動滑動到頂部胎挎。先不要連接左側的約束沟启、
接著,切換到Code界面檢查第一個圖標xml的更新犹菇,發(fā)現(xiàn)添加了一個新的約束app:layout_constraintTop_toTopOf="parent"德迹,XML看起來如下:
<ImageView
android:id="@+id/spaceStationIcon"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginTop="15dp"
android:src="@drawable/space_station_icon"
app:layout_constraintTop_toTopOf="parent" />
你也可以在設計視圖中調整邊距,切換到設計視圖點擊Attributestab揭芍。
接著點擊第一個圖標并且查看屬性胳搞,你會看到margins的圖形表示。
你可以通過從下拉菜單中選擇邊距或單擊數(shù)字并輸入新值來為邊距選擇新值称杨。
水平對齊頂部的三個圖標:使用Chains
接著肌毅,希望頂部的三個圖標在同一條水平線上并且平均分布,需要為每一個圖標添加一系列的約束姑原,這里有個更快速的方法就是使用chains悬而。
Chains
如果有雙向約束,就會出現(xiàn)鏈條锭汛,當你使用了菜單中的對齊約束時笨奠,Android Studio實際上就使用了鏈條袭蝗,你可以將不同的樣式、權重般婆、邊距應用到鏈條.
接著切換到設計面板到腥,同時選中頂部的三個圖標,右鍵然后選擇Center -> Horizontally,此時會自動創(chuàng)建一個鏈條并且生成約束蔚袍。
在設計面板你就能看到鏈與其他約束的不同乡范,其他的是波浪線表示,而鏈條是一條鏈页响。
探索鏈條
要探索某些鏈的模式篓足,選擇一個元素,單擊圖標底部顯示的循環(huán)鏈模式按鈕闰蚕。
模式有:
- Packed:元素會被壓縮到一起
- Spread:如上所示栈拖,元素被分布到可用空間上
- Spread inside: 與spread類似,但鏈的端點不會分散没陡。
確保以spread為鏈的模式涩哟,修改方法有兩種:
- 視圖將以實力屏幕截圖中的圖標間隔顯示
- 在其中一個圖標的xml中將會出現(xiàn)app:layout_constraintHorizontal_chainStyle="spread",更新該屬性盼玄,可以將鏈的模式改變成其他的贴彼。
對齊Views
接著,再次選中三個圖標埃儿,從tool bar中器仗,選擇Align -> Vertical Centers.Android Studio會添加約束用來使每一個view的底部和頂部與相鄰的view對齊。
布局看起來如下:
此時三個圖標的xml如下所示:
<ImageView
android:id="@+id/spaceStationIcon"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginTop="15dp"
android:src="@drawable/space_station_icon"
app:layout_constraintEnd_toStartOf="@+id/flightsIcon"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/flightsIcon"
android:layout_width="30dp"
android:layout_height="30dp"
android:src="@drawable/rocket_icon"
app:layout_constraintBottom_toBottomOf="@+id/spaceStationIcon"
app:layout_constraintEnd_toStartOf="@+id/roverIcon"
app:layout_constraintStart_toEndOf="@+id/spaceStationIcon"
app:layout_constraintTop_toTopOf="@+id/spaceStationIcon" />
<ImageView
android:id="@+id/roverIcon"
android:layout_width="30dp"
android:layout_height="30dp"
android:src="@drawable/rover_icon"
app:layout_constraintBottom_toBottomOf="@+id/flightsIcon"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/flightsIcon"
app:layout_constraintTop_toTopOf="@+id/flightsIcon" />
如果你的界面跟圖片不匹配童番,檢查Text和Design面板精钮,就重新再做一次。
對齊每個圖標的文本
接著設置圖標下面的文字剃斧,將第一個Text的左側的約束連接到第一個圖標的左側轨香,右側的約束連接到右側,上面的約束添加到上面幼东,其他三個Text做同樣的操作臂容。
然后將工具欄中的默認邊距更改為15dp,只需要將頂部錨點拖動到圖標的底部錨點根蟹,即可在一個步驟中設置約束和邊距脓杉。
現(xiàn)在上面兩行的約束錯誤已經(jīng)消失了,XML中圖標和標簽的代碼如下:
<TextView
android:id="@+id/roverLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:text="@string/rovers"
app:layout_constraintEnd_toEndOf="@+id/roverIcon"
app:layout_constraintStart_toStartOf="@+id/roverIcon"
app:layout_constraintTop_toBottomOf="@+id/roverIcon" />
<TextView
android:id="@+id/flightsLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:text="@string/flights"
app:layout_constraintEnd_toEndOf="@+id/flightsIcon"
app:layout_constraintStart_toStartOf="@+id/flightsIcon"
app:layout_constraintTop_toBottomOf="@+id/flightsIcon" />
<TextView
android:id="@+id/spaceStationLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:text="@string/space_stations"
app:layout_constraintEnd_toEndOf="@+id/spaceStationIcon"
app:layout_constraintStart_toStartOf="@+id/spaceStationIcon"
app:layout_constraintTop_toBottomOf="@+id/spaceStationIcon" />
Using Guidelines
在最終的布局中简逮,雙箭頭圖像應居中并且與兩個綠色視圖重疊
設置水平和垂直的Guidelines
選中雙箭頭圖標并且設置寬高為60dp丽已,然后在該圖標上點擊右鍵,選擇Center -> Horizontally in Parent.
為每一個綠色的TextView設置寬為124dp买决,高為98dp沛婴。
確保雙箭頭圖片在兩個綠色的TextView之上,將左側TextView的右側約束到雙箭頭圖片的右側督赤,并將右邊距設置為40dp嘁灯。
同樣的將右側綠色的TextView的左側約束到雙箭頭圖標的左側,并將左側邊距設置為40dp躲舌。
最后丑婿,將兩個TextView的上下分別約束到雙箭頭圖標的上下。
[圖片上傳失敗...(image-5a6638-1550557283059)]
最后没卸,點擊Guidelines羹奉,選擇Add Horizontal Guideline
將會添加一條水平的虛線到布局中。
在Conponent Tree中選中水平guideline约计,在Attributes 檢查器中诀拭,改變ID為guideline1,注意guideline屬性:layout_constraintGuide_begin 和 layout_constraintGuide_percent
對于水平的guideline1煤蚌,設置layout_constraintGuide_begin為200dp
最后耕挨,添加一個豎直的guideline,設置id為guideline2并且設置layout_constraintGuide_percent為0.05尉桩,這將guideline2定位到距離屏幕左側為屏幕寬度5%的位置筒占。
定位Guidelines
定位guideline使用下面三個屬性:
- layout_constraintGuide_begin: 從左側或其父級的頂部定位具有指定dp的guideline
- layout_constraintGuide_end: 從右側或其父級底部定位指定dp的guideline
- layout_constraintGuide_percent: 使用百分比來定位guideline
添加約束到Guidelines
現(xiàn)在guidelines已經(jīng)設置了,可以給他們添加一些約束蜘犁。
首先翰苫,對于雙箭頭圖標:
- 將底部約束到水平guideline
- 設置底部邊距為40dp
對于開關:
- 設置寬度為160dp
- 設置左側約束到垂直guideline
- 設置top約束到父布局
- 設置top margin為200dp
對于開關下面的標簽:
- 設置左邊的約束到垂直guideline
- 設置頂部約束到開關的底部
對于galaxy icon
- 設置寬高為90dp
- 設置top約束到水平guideline
- 約束bottom到父布局的底部,這樣就會在水平guideline和底部之間居中
- 在父視圖中將其水平居中
對于rocket icon:
- 設置寬和高為30dp
- 約束rocket icon的上下和右到galaxy圖標的上下和左邊
最后这橙,對于DEPART按鈕:
- 將寬度從wrap_content修改為match_parent
- 將底部約束到parent的底部
此時奏窑,你已經(jīng)設置完了所有的約束,在Component Tree中也沒有任何錯誤了析恋,布局此時看起來如下所示:
圓形位置約束
除了上面的良哲,還可以使用距離和角度來約束UI元素。允許你將他們防止到一個圓上助隧,其中一個元素位于圓的中心筑凫,另一個元素位于圓周上。
選擇rocket icon并村,并在代碼視圖中更新其代碼巍实,代碼如下:
<ImageView
android:id="@+id/rocketIcon"
android:layout_width="30dp"
android:layout_height="30dp"
android:src="@drawable/rocket_icon"
app:layout_constraintCircle="@id/galaxyIcon"
app:layout_constraintCircleAngle="270"
app:layout_constraintCircleRadius="100dp" />
第一個約束屬性layout_constraintCircle指示將位于圓心的UI元素的ID,另外兩個屬性表示角度和半徑哩牍。
讓UI元素在屏幕上動起來
約束集
使用ConstraintLayout棚潦,你可以設置幀動畫從而是你的views動起來,為此膝昆,你需要提供布局文件的副本丸边,稱為ConstraintSet叠必,ConstraintSet只需要包含給定ConstraintLayout中元素的約束,邊距以及填充妹窖。
如果你使用的是kotlin代碼纬朝,那么你可以直接將ConstraintSet應用到你的ConstraintLayout。
要構建動畫骄呼,你需要指定單個布局文件和ConstraintSet作為起始和結束關鍵幀共苛,你也可以應用過渡是動畫更有趣。
設置動畫的起始布局
在項目中復制布局文件并命名為keyframe1.xml蜓萄,并將此布局設置為應用程序的起始布局隅茎。
打開keyframe1.xml,將guideline1的layout_constraintGuide_begin屬性值從200dp改為0dp,這樣會移動guideline嫉沽,限制在guideline中的元素辟犀,將會移除屏幕
接著將guideline2的layout_constraintGuide_percent屬性值從0.05修改成1,這會將指南移動到屏幕最右側耻蛇,從而受其約束的元素被移動到屏幕外踪蹬。
接著修改MainActivity中的setContentView中的R.layout.activity_main為R.layout.keyframe1
動畫視圖
將MainActivity中的:
import kotlinx.android.synthetic.main.activity_main.*
改為:
import kotlinx.android.synthetic.main.keyframe1.*
可以讓你直接飲用UI中的id,而不用使用findViewById(),
接著臣咖,添加如下的代碼:
private val constraintSet1 = ConstraintSet()
private val constraintSet2 = ConstraintSet()
private var isOffscreen = true
Transition Manager
可以使用Transition Manager類來處理從一個keyframe到另一個的過渡跃捣,創(chuàng)建一個布局動畫,你只需要向Transition Manager提供要設置動畫的ConstraintSet夺蛇,他將會處理其余的部分疚漆。
將如下的代碼添加到onCreate方法中:
constraintSet1.clone(constraintLayout) //1
constraintSet2.clone(this, R.layout.activity_main) //2
departButton.setOnClickListener { //3
//apply the transition
TransitionManager.beginDelayedTransition(constraintLayout) //4
val constraint = if (!isOffscreen) constraintSet1 else constraintSet2
isOffscreen = !isOffscreen
constraint.applyTo(constraintLayout) //5
}
動畫視圖的界限
不僅可以通過影響其約束來更改屏幕上元素的位置,還可以改變其大小刁赦。
打開keyframe1.xml選擇galaxy icon娶聘,id為galaxyIcon,將高度從90dp改為10dp甚脉。
接著運行app可以看到大小的改變丸升。
使用自定義過渡使動畫更簡單
創(chuàng)建一個自定義動畫來替代默認的動畫,可以自定義動畫的時長牺氨。
添加如下方法到MainActivity中狡耻。
override fun onEnterAnimationComplete() {
super.onEnterAnimationComplete()
constraintSet2.clone(this, R.layout.activity_main)
val transition = AutoTransition()
transition.duration = 1000
TransitionManager.beginDelayedTransition(constraintLayout,transition)
constraintSet2.applyTo(constraintLayout)
}
- 動畫執(zhí)行過程中,Activity無法繪制任何內(nèi)容猴凹,onEnterAnimationComplete()方法表示動畫執(zhí)行完成夷狰,可以調用繪制代碼。
- 會將布局信息從最終布局拉入constraintSet2
- 創(chuàng)建一個自定義過渡郊霎,使用AutoTransition沼头,首先淡出要消失的目標,然后移動并調整現(xiàn)有目標的大小,最后淡出出現(xiàn)的目標进倍。
- 動畫執(zhí)行時長為1000毫秒
- 調用Transition Manager的beginDelayedTransition方法土至,但這次提供的是自定義過渡
- 應用一個新的ConstraintSet到當前消失的ConstraintLayout上。
效果如下:
使圓形約束動起來
要在火星周圍制作火箭動畫背捌,必須改變兩個屬性:圓形約束的角度毙籽,他將火箭的位置移動到圓周,以及火箭的旋轉來完成動畫毡庆,你還可以檢查單向/往返開關值以確定火箭是飛行半圈還是一整圈。
替換DEPART button的點擊事件的代碼為如下的代碼:
departButton.setOnClickListener {
// TransitionManager.beginDelayedTransition(constraintLayout)
// val constraint = if (!isOffscreen) constraintSet1 else constraintSet2
// isOffscreen = !isOffscreen
// constraint.applyTo(constraintLayout)
val layoutParams = rocketIcon.layoutParams as ConstraintLayout.LayoutParams
val startAngle = layoutParams.circleAngle
val endAngle = startAngle + (if (switch1.isChecked) 360 else 180)
val anim = ValueAnimator.ofFloat(startAngle,endAngle)
anim.addUpdateListener { valueAnimator ->
val animatedValue = valueAnimator.animatedValue as Float
val layoutParams = rocketIcon.layoutParams as ConstraintLayout.LayoutParams
layoutParams.circleAngle = animatedValue
rocketIcon.layoutParams = layoutParams
rocketIcon.rotation = (animatedValue % 360 - 270)
}
anim.duration = if(switch1.isChecked) 2000 else 1000
anim.interpolator = LinearInterpolator()
anim.start()
}
- 在動畫開始之前烙如,將火箭的startAngle設置為火箭的當前角度么抗,依賴one way/ Round Trip切換,endAngle在startAngle的之上添加180或360
- 使用startAngle和endAngle創(chuàng)建一個ValueAnimator
- 在動畫監(jiān)聽器中亚铁,獲取動畫值并將其設置給rocket的layoutParams中的circleAngle屬性
- 用動畫值旋轉火箭蝇刀,將使火箭飛的更加自然。
- 單向動畫需要一秒徘溢,而往返動畫需要2秒
- 使用LinearInterpolator吞琐,可以試試AnticipateOvershootInterpolator看看會發(fā)生什么!
最后
如果你比較喜歡view動畫然爆,那么可以使用MotionEvent嘗試更多的動畫站粟,https://youtu.be/S3FeIRKu_Z8?t=1275
這篇文章內(nèi)容真的挺豐富的,寫的很不錯曾雕,所以就簡單翻譯了一下奴烙,如果有朋友覺得哪些翻譯的不夠好的,歡迎給我評論剖张,我會及時糾正切诀。