ConstraintLayout教程

原文地址:https://www.raywenderlich.com/9475-constraintlayout-tutorial-for-android-complex-layouts

  1. 轉換其他類型的布局到ConstraintLayout
  2. 相對于在屏幕上的其他元素動態(tài)定位UI元素
  3. 讓你的Views實現(xiàn)動畫

在這篇教程中豌汇,會構建一個星際旅行的app瓣颅,火箭將會繞著行星不停的轉動芋齿。

星際旅行

在這個app中有很多元素,你能夠學到如何使用復雜的ConstraintLayout來正常的顯示他們瞒津。

轉換一個布局到ConstraintLayout

Component Tree面板中,在最頂層的布局中右鍵并且選擇Convert LinearLayout to ConstraintLayout

image

接著會看到一個彈出框并且有一些選項:


image

閱讀完所有內(nèi)容后不改變默認的選中狀態(tài)携取,然后點擊ok關閉對話框太示,AndroidStudio接下來就會把你的布局轉換成ConstraintLayout。
轉換過后香浩,你的布局就變成下面的這個樣子:

image

如果此時所有的視圖都跑到了左上角类缤,請不要驚慌,

請確保關閉AutoConnect

image

移除推斷約束

執(zhí)行轉換的過程中邻吭,AndroidStudio會執(zhí)行好幾個步驟餐弱,最后一步就是進行推斷約束,但是結果并不是你想要的,此時膏蚓,你只需要轉到編輯菜單并選擇撤銷推斷約束瓢谢。


image

或者,執(zhí)行cmd+Z驮瞧,現(xiàn)在你的界面看起來如下:


image

你可以稍微拖動視圖氓扛,讓他看起來更像原始的樣子:


image

如果在拖動過程中Android Studio自動添加了任何的約束,可以點擊Clear All Constraints按鈕去清除他們论笔。

image

調整Image

通過點擊頂部的每個圖標采郎,spaceStationIconflightIconroverIcon來修復圖像的大小狂魔,接著在屬性面板蒜埋,將layout_widthlayout_heigthwrap_content改為30dp。

image

此時你將會在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ù)字并輸入新值來為邊距選擇新值称杨。


image

水平對齊頂部的三個圖標:使用Chains

接著肌毅,希望頂部的三個圖標在同一條水平線上并且平均分布,需要為每一個圖標添加一系列的約束姑原,這里有個更快速的方法就是使用chains悬而。

Chains

如果有雙向約束,就會出現(xiàn)鏈條锭汛,當你使用了菜單中的對齊約束時笨奠,Android Studio實際上就使用了鏈條袭蝗,你可以將不同的樣式、權重般婆、邊距應用到鏈條.

接著切換到設計面板到腥,同時選中頂部的三個圖標,右鍵然后選擇Center -> Horizontally,此時會自動創(chuàng)建一個鏈條并且生成約束蔚袍。

在設計面板你就能看到鏈與其他約束的不同乡范,其他的是波浪線表示,而鏈條是一條鏈页响。

image

探索鏈條

要探索某些鏈的模式篓足,選擇一個元素,單擊圖標底部顯示的循環(huán)鏈模式按鈕闰蚕。

image

模式有:

  1. Packed:元素會被壓縮到一起
  2. Spread:如上所示栈拖,元素被分布到可用空間上
  3. Spread inside: 與spread類似,但鏈的端點不會分散没陡。
image

確保以spread為鏈的模式涩哟,修改方法有兩種:

  1. 視圖將以實力屏幕截圖中的圖標間隔顯示
  2. 在其中一個圖標的xml中將會出現(xiàn)app:layout_constraintHorizontal_chainStyle="spread",更新該屬性盼玄,可以將鏈的模式改變成其他的贴彼。

對齊Views

接著,再次選中三個圖標埃儿,從tool bar中器仗,選擇Align -> Vertical Centers.Android Studio會添加約束用來使每一個view的底部和頂部與相鄰的view對齊。
布局看起來如下:

image

此時三個圖標的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,只需要將頂部錨點拖動到圖標的底部錨點根蟹,即可在一個步驟中設置約束和邊距脓杉。


image

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

在最終的布局中简逮,雙箭頭圖像應居中并且與兩個綠色視圖重疊


image

設置水平和垂直的Guidelines

選中雙箭頭圖標并且設置寬高為60dp丽已,然后在該圖標上點擊右鍵,選擇Center -> Horizontally in Parent.

image

為每一個綠色的TextView設置寬為124dp买决,高為98dp沛婴。

確保雙箭頭圖片在兩個綠色的TextView之上,將左側TextView的右側約束到雙箭頭圖片的右側督赤,并將右邊距設置為40dp嘁灯。
同樣的將右側綠色的TextView的左側約束到雙箭頭圖標的左側,并將左側邊距設置為40dp躲舌。
最后丑婿,將兩個TextView的上下分別約束到雙箭頭圖標的上下。
[圖片上傳失敗...(image-5a6638-1550557283059)]

最后没卸,點擊Guidelines羹奉,選擇Add Horizontal Guideline

image

將會添加一條水平的虛線到布局中。

Conponent Tree中選中水平guideline约计,在Attributes 檢查器中诀拭,改變ID為guideline1,注意guideline屬性:layout_constraintGuide_beginlayout_constraintGuide_percent
對于水平的guideline1煤蚌,設置layout_constraintGuide_begin為200dp

image

最后耕挨,添加一個豎直的guideline,設置id為guideline2并且設置layout_constraintGuide_percent為0.05尉桩,這將guideline2定位到距離屏幕左側為屏幕寬度5%的位置筒占。

定位Guidelines

定位guideline使用下面三個屬性:

  1. layout_constraintGuide_begin: 從左側或其父級的頂部定位具有指定dp的guideline
  2. layout_constraintGuide_end: 從右側或其父級底部定位指定dp的guideline
  3. layout_constraintGuide_percent: 使用百分比來定位guideline

添加約束到Guidelines

現(xiàn)在guidelines已經(jīng)設置了,可以給他們添加一些約束蜘犁。
首先翰苫,對于雙箭頭圖標:

  1. 將底部約束到水平guideline
  2. 設置底部邊距為40dp

對于開關:

  1. 設置寬度為160dp
  2. 設置左側約束到垂直guideline
  3. 設置top約束到父布局
  4. 設置top margin為200dp

對于開關下面的標簽:

  1. 設置左邊的約束到垂直guideline
  2. 設置頂部約束到開關的底部

對于galaxy icon

  1. 設置寬高為90dp
  2. 設置top約束到水平guideline
  3. 約束bottom到父布局的底部,這樣就會在水平guideline和底部之間居中
  4. 在父視圖中將其水平居中

對于rocket icon:

  1. 設置寬和高為30dp
  2. 約束rocket icon的上下和右到galaxy圖標的上下和左邊

最后这橙,對于DEPART按鈕:

  1. 將寬度從wrap_content修改為match_parent
  2. 將底部約束到parent的底部

此時奏窑,你已經(jīng)設置完了所有的約束,在Component Tree中也沒有任何錯誤了析恋,布局此時看起來如下所示:

image

圓形位置約束

除了上面的良哲,還可以使用距離和角度來約束UI元素。允許你將他們防止到一個圓上助隧,其中一個元素位于圓的中心筑凫,另一個元素位于圓周上。


image

選擇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可以看到大小的改變丸升。

image

使用自定義過渡使動畫更簡單

創(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)
  }
  1. 動畫執(zhí)行過程中,Activity無法繪制任何內(nèi)容猴凹,onEnterAnimationComplete()方法表示動畫執(zhí)行完成夷狰,可以調用繪制代碼。
  2. 會將布局信息從最終布局拉入constraintSet2
  3. 創(chuàng)建一個自定義過渡郊霎,使用AutoTransition沼头,首先淡出要消失的目標,然后移動并調整現(xiàn)有目標的大小,最后淡出出現(xiàn)的目標进倍。
  4. 動畫執(zhí)行時長為1000毫秒
  5. 調用Transition Manager的beginDelayedTransition方法土至,但這次提供的是自定義過渡
  6. 應用一個新的ConstraintSet到當前消失的ConstraintLayout上。

效果如下:


image

使圓形約束動起來

要在火星周圍制作火箭動畫背捌,必須改變兩個屬性:圓形約束的角度毙籽,他將火箭的位置移動到圓周,以及火箭的旋轉來完成動畫毡庆,你還可以檢查單向/往返開關值以確定火箭是飛行半圈還是一整圈。

替換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()
        }
  1. 在動畫開始之前烙如,將火箭的startAngle設置為火箭的當前角度么抗,依賴one way/ Round Trip切換,endAngle在startAngle的之上添加180或360
  2. 使用startAngle和endAngle創(chuàng)建一個ValueAnimator
  3. 在動畫監(jiān)聽器中亚铁,獲取動畫值并將其設置給rocket的layoutParams中的circleAngle屬性
  4. 用動畫值旋轉火箭蝇刀,將使火箭飛的更加自然。
  5. 單向動畫需要一秒徘溢,而往返動畫需要2秒
  6. 使用LinearInterpolator吞琐,可以試試AnticipateOvershootInterpolator看看會發(fā)生什么!
image

最后

如果你比較喜歡view動畫然爆,那么可以使用MotionEvent嘗試更多的動畫站粟,https://youtu.be/S3FeIRKu_Z8?t=1275

這篇文章內(nèi)容真的挺豐富的,寫的很不錯曾雕,所以就簡單翻譯了一下奴烙,如果有朋友覺得哪些翻譯的不夠好的,歡迎給我評論剖张,我會及時糾正切诀。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市搔弄,隨后出現(xiàn)的幾起案子幅虑,更是在濱河造成了極大的恐慌,老刑警劉巖顾犹,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件倒庵,死亡現(xiàn)場離奇詭異,居然都是意外死亡蹦渣,警方通過查閱死者的電腦和手機哄芜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來柬唯,“玉大人认臊,你說我怎么就攤上這事〕荩” “怎么了失晴?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵剧腻,是天一觀的道長。 經(jīng)常有香客問我涂屁,道長书在,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任拆又,我火速辦了婚禮儒旬,結果婚禮上,老公的妹妹穿的比我還像新娘帖族。我一直安慰自己栈源,他們只是感情好,可當我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布竖般。 她就那樣靜靜地躺著甚垦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪涣雕。 梳的紋絲不亂的頭發(fā)上艰亮,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天,我揣著相機與錄音挣郭,去河邊找鬼迄埃。 笑死,一個胖子當著我的面吹牛丈屹,可吹牛的內(nèi)容都是我干的调俘。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼旺垒,長吁一口氣:“原來是場噩夢啊……” “哼彩库!你這毒婦竟也來了?” 一聲冷哼從身側響起先蒋,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤骇钦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后竞漾,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體眯搭,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年业岁,在試婚紗的時候發(fā)現(xiàn)自己被綠了鳞仙。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡笔时,死狀恐怖棍好,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤借笙,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布扒怖,位于F島的核電站,受9級特大地震影響业稼,放射性物質發(fā)生泄漏盗痒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一低散、第九天 我趴在偏房一處隱蔽的房頂上張望俯邓。 院中可真熱鬧,春花似錦熔号、人聲如沸看成。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至吃嘿,卻和暖如春祠乃,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背兑燥。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工亮瓷, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人降瞳。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓嘱支,卻偏偏與公主長得像,于是被迫代替她去往敵國和親挣饥。 傳聞我的和親對象是個殘疾皇子除师,可洞房花燭夜當晚...
    茶點故事閱讀 45,060評論 2 355

推薦閱讀更多精彩內(nèi)容