目前2.0只是新增了一些新功能和新玩法,對1.x版本無取代之意,所以1.x版本還是得學習的。好文推薦 2.0版本新增的內容在實踐開發也是非常實用的,建議可以上車了。
「由於無知與惰性,讓我們感覺摸到了技術的天花板」
「對你有用,幫忙點贊~」
基於本文發表,ConstraintLayout版本已經更新到2.0.0-beta8
,所以添加依賴的姿勢:
AndroidX:
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta8'
支持庫:
implementation 'com.android.support.constraint:constraint-layout:2.0.0-beta8'
版本說明:
alpha
:內部測試版,bug多多;
beta
:公開測試版本,bug少點,支持嚐鮮;
rc
:候選版本,功能不增,和發佈版本一致,修修小bug;
stable
:穩定版本,你儘管用,bug能找到是福氣。
目錄
- 1. 一、Flow流式虛擬佈局(alpha 5增加)
- 2. 二、Layer 層佈局
- 3. 三、自定義ConstraintHelper
- 4. 四、ImageFilterButton
- 5. 五、ImageFilterView
- 6. 六、MockView
- 7. 七、MotionLayout
- 8. 八、邊距問題的補充
- 9. 相關文章
一、Flow流式虛擬佈局(alpha 5增加)
正常情況下,列表顯示一般採用ListView或者RecyclerView來實現,但其子Item佈局是非常呆板的。想象一下,如果一部作品的詳情頁結束打上一堆標籤,樣式如下,該怎麼實現?
這種佈局採用Flow
來實現特別的簡單和方便,而且通過flow_wrapMode
屬性可以設置不同對齊方式。
下面佈局代碼簡單示例:Flow
佈局通過constraint_referenced_ids
屬性將要約束的View
的id
進行關聯,這裡簡單關聯A
到G
的TextView
,由於TextView
沒有設置約束條件,所以Android Studio 4.0 會報紅,給ConstraintLayout
佈局添加tools:ignore="MissingConstraints"
忽略報紅提示。
Flow
佈局的flow_horizontalGap
屬性表示水平之間兩個View的水平間隔,flow_verticalGap
則是垂直方向間隔。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:ignore="MissingConstraints"//忽略Android Studio 4.0報紅提示
tools:context=".MainActivity">
<androidx.constraintlayout.helper.widget.Flow
android:id="@+id/flow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="tvA,tvB,tvC,tvD,tvE,tvF,tvG"
app:flow_horizontalGap="30dp" //View水平間隔
app:flow_verticalGap="30dp" //垂直間隔
app:flow_wrapMode="none"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvA"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@color/colorPrimary"
android:gravity="center"
android:text="A"
android:textColor="#ffffff"
android:textSize="16sp"
/>
<TextView
android:id="@+id/tvB"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@color/colorPrimary"
android:gravity="center"
android:text="B"
android:textColor="#ffffff"
android:textSize="16sp"
/>
<TextView
android:id="@+id/tvC"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@color/colorPrimary"
android:gravity="center"
android:text="C"
android:textColor="#ffffff"
android:textSize="16sp"
/>
<TextView
android:id="@+id/tvD"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@color/colorPrimary"
android:gravity="center"
android:text="D"
android:textColor="#ffffff"
android:textSize="16sp"
/>
<TextView
android:id="@+id/tvE"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@color/colorPrimary"
android:gravity="center"
android:text="E"
android:textColor="#ffffff"
android:textSize="16sp"
/>
<TextView
android:id="@+id/tvF"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@color/colorPrimary"
android:gravity="center"
android:text="F"
android:textColor="#ffffff"
android:textSize="16sp"
/>
<TextView
android:id="@+id/tvG"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@color/colorPrimary"
android:gravity="center"
android:text="G"
android:textColor="#ffffff"
android:textSize="16sp"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
flow_wrapMode
屬性一共有三種值,在上面的佈局的基礎上,更換一下不同的值,看一下效果:
「none值」:所有引用的View形成一條鏈,水平居中,超出屏幕兩側的View
不可見。
「chian值」:所引用的View
形成一條鏈,超出部分會自動換行,同行的View
會平分寬度。
「aligned值」:所引用的View
形成一條鏈,但View
會在同行同列。
即然是一條鏈,那麼可以通過鏈的樣式進行約束。
鏈約束
當flow_wrapMode
屬性為aligned
和chian
屬性時,通過鏈進行約束。ConstraintLayout,看完一篇真的就夠了麼? 此文有談到鏈約束(Chain
)。
給Flow
佈局添加以下屬性進行不同chain
約束:
flow_firstHorizontalStyle
約束第一條水平鏈,當有多條鏈(多行)時,只約束第一條鏈(第一行),其他鏈(其他行)不約束;flow_lastHorizontalStyle
約束最後一條水平鏈,當有多條鏈(多行)時,只約束最後一條鏈(最後一行),其他鏈(其他行)不約束;flow_horizontalStyle
約束所有水平鏈;flow_firstVerticalStyle
同水平約束;flow_lastVerticalStyle
同水平約束;flow_verticalStyle
約束所有垂直鏈;
以上屬性,取值有:spread
、spread_inside
、packed
「效果:」
「spread值:
」
代碼:
<androidx.constraintlayout.helper.widget.Flow
android:id="@+id/flow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
app:constraint_referenced_ids="tvA,tvB,tvC,tvD,tvE,tvF,tvG"
app:flow_maxElementsWrap="4"
app:flow_horizontalGap="30dp"
app:flow_verticalGap="30dp"
app:flow_wrapMode="chain"
app:flow_horizontalStyle="spread"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
「spread_inside值:
」
代碼:
<androidx.constraintlayout.helper.widget.Flow
android:id="@+id/flow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
app:constraint_referenced_ids="tvA,tvB,tvC,tvD,tvE,tvF,tvG"
app:flow_maxElementsWrap="4"
app:flow_horizontalGap="30dp"
app:flow_verticalGap="30dp"
app:flow_wrapMode="chain"
app:flow_horizontalStyle="spread_inside"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
「packed值:
」
代碼:
<androidx.constraintlayout.helper.widget.Flow
android:id="@+id/flow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
app:constraint_referenced_ids="tvA,tvB,tvC,tvD,tvE,tvF,tvG"
app:flow_maxElementsWrap="4"
app:flow_horizontalGap="30dp"
app:flow_verticalGap="30dp"
app:flow_wrapMode="chain"
app:flow_horizontalStyle="packed"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
其他效果大家在實踐可以嘗試看看效果,建議「點贊」收藏本文,在使用不會可以翻閱一下,效率事半功倍,免得重新浪費時間谷歌搜索。
對齊
上文XML佈局中,所有TextView的寬高是一致的,所以看著整整齊齊,當寬高不一致時,可以進行對齊處理。個人試了一下app:flow_wrapMode="aligned"
下的對齊,沒啥效果,估計有默認值了吧。看看flow_wrapMode
屬性為none
和chain
情況吧。
給Flow
佈局添加以下屬性進行不同Align
約束:
flow_verticalAlign
垂直方向對齊,取值有:top
、bottom
、center
、baseline
;flow_horizontalAlign
水平方向對齊,取值有:start
、end
、center
;
對齊方向一般與鏈的方向相反才可生效,例如垂直鏈樣式,一般對齊View
的左右邊和中間。
簡單舉個例子:垂直方向頂部對齊。
「效果圖:」
可以看到E
和G
、F
頂部對齊。
代碼:
<androidx.constraintlayout.helper.widget.Flow
android:id="@+id/flow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
app:constraint_referenced_ids="tvA,tvB,tvC,tvD,tvE,tvF,tvG"
app:flow_maxElementsWrap="4"
app:flow_horizontalGap="30dp"
app:flow_verticalGap="30dp"
app:flow_wrapMode="chain"
app:flow_verticalAlign="top"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
簡單的理解aligned
和chian
是none
的定製版,通過添加不同的屬性定製而成。由於Flow
是虛擬佈局,簡單理解就是約束助手,它並不會增加布局層級,卻可以像正常的佈局一樣使用。
「其他屬性」
上文的XML的佈局沒有設置Flow對View的組織方式(水平or 垂直),可以通過orientation
屬性來設置水平horizontal
和垂直vertical
方向,例如改為垂直方向。
當flow_wrapMode
屬性為aligned
和chian
時,通過flow_maxElementsWrap
屬性控制每行最大的子View
數量。例如:flow_maxElementsWrap=3
。
當flow_wrapMode
屬性為none
時,A和G被擋住了,看不到。
要A或者G可見,通過設置flow_horizontalBias
屬性,取值在0-1
之間。前提條件是flow_horizontalStyle
屬性為packed
才會生效。
<androidx.constraintlayout.helper.widget.Flow
android:id="@+id/flow"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="tvA,tvB,tvC,tvD,tvE,tvF,tvG"
app:flow_horizontalGap="30dp"
app:flow_verticalGap="30dp"
app:flow_wrapMode="none"
app:flow_horizontalStyle="packed"
app:flow_horizontalBias="0"
android:layout_marginTop="10dp"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
「效果圖:」
設置flow_horizontalBias=1
那麼G就可以看到了。該屬性還有其他類似ChainStyle的屬性w玩法,具體可以實踐體驗。當然,也可以在flow_wrapMode
屬性為其他值生效。
通過不同的屬性可以搭配很多不同的效果,再加上MotionLayout動畫,那就更炫酷了。
二、Layer 層佈局
Layer也是一個約束助手ConstraintHelper
,相對Flow比較簡單,常用來增加背景,或者共同動畫。由於ConstraintHelper
本身繼承自View
,跟我們自己通過View在ConstraintLayout
佈局中給多個View添加共同背景沒什麼區別,只是更方便而已。
「1、添加背景」
給ImageView
和TextView
添加個共同背景:
「效果:」
「代碼:」
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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">
<androidx.constraintlayout.helper.widget.Layer
android:id="@+id/layer"
android:layout_marginTop="50dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
app:constraint_referenced_ids="ivImage,tvName"
app:layout_constraintLeft_toLeftOf="@id/ivImage"
app:layout_constraintRight_toRightOf="parent"
android:padding="10dp"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="MissingConstraints" />
<ImageView
android:id="@+id/ivImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:src="@mipmap/ic_launcher_round"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/layer" />
<TextView
android:id="@+id/tvName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="新小夢"
android:textColor="#FFFFFF"
android:paddingTop="5dp"
app:layout_constraintLeft_toLeftOf="@id/ivImage"
app:layout_constraintRight_toRightOf="@id/ivImage"
app:layout_constraintTop_toBottomOf="@id/ivImage" />
</androidx.constraintlayout.widget.ConstraintLayout>
「2、共同動畫」
通過屬性動畫給ImageView和TextView添加通過動畫效果。
「效果:」
「代碼:」
val animator = ValueAnimator.ofFloat( 0f, 360f)
animator.repeatMode=ValueAnimator.RESTART
animator.duration=2000
animator.interpolator=LinearInterpolator()
animator.repeatCount=ValueAnimator.INFINITE
animator.addUpdateListener {
layer.rotation= it.animatedValue as Float
}
layer.setOnClickListener {
animator.start()
}
對屬性動畫模糊的同學可以看看:Android屬性動畫,看完這篇夠用了吧
支持:旋轉、位移、縮放動畫。透明效果試了一下,是針對自身的,而不是約束的View。
三、自定義ConstraintHelper
Flow
和Layer
都是ConstraintHelper
的子類,當兩者不滿足需求時,可以通過繼承ConstraintHelper
來實現想要的約束效果。
在某乎APP有這麼個類似的動畫廣告:
那麼通過自定義ConstraintHelper來實現就非常簡單:
class AdHelper :
ConstraintHelper {
constructor(context: Context?) : super(context)
constructor(context: Context?,attributeSet: AttributeSet):super(context,attributeSet)
constructor(context: Context?,attributeSet: AttributeSet,defStyleAttr: Int):super(context,attributeSet,defStyleAttr)
override fun updatePostLayout(container: ConstraintLayout?) {
super.updatePostLayout(container)
val views = getViews(container)
views.forEach {
val anim = ViewAnimationUtils.createCircularReveal(it, 0, 0, 0f, it.width.toFloat())
anim.duration = 5000
anim.start()
}
}
}
佈局引用AdHleper
:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.constraint.AdHelper
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="ivLogo"
app:layout_constraintLeft_toLeftOf="@id/ivLogo"
app:layout_constraintRight_toRightOf="@id/ivLogo"
app:layout_constraintTop_toTopOf="@id/ivLogo" />
<ImageView
android:id="@+id/ivLogo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:adjustViewBounds="true"
android:scaleType="fitXY"
android:src="@mipmap/ic_logo"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
四、ImageFilterButton
圓角圖片,圓形圖片怎麼實現?自定義View?通過ImageFilterButton
,一個屬性就搞定;ImageFilterButto
能做的還有更多。
看看如何實現圓角或圓形圖片:
「原圖:」
將roundPercent
屬性設置為1
,取值在0-1
,由正方形向圓形過渡。
<androidx.constraintlayout.utils.widget.ImageFilterButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="100dp"
app:roundPercent="1"
android:src="@mipmap/ic_launcher"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
「效果:」
也可以通過設置round
屬性來實現:
<androidx.constraintlayout.utils.widget.ImageFilterButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="100dp"
android:src="@mipmap/ic_launcher"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:round="50dp" />
「其他屬性:」
altSrc
和src
屬性是一樣的概念,altSrc
提供的資源將會和src
提供的資源通過crossfade
屬性形成交叉淡化效果。默認情況下,crossfade=0
,altSrc
所引用的資源不可見,取值在0-1
。
例如:
<androidx.constraintlayout.utils.widget.ImageFilterButton
android:id="@+id/ivImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="100dp"
android:src="@mipmap/ic_launcher"
app:altSrc="@mipmap/ic_sun"
app:crossfade="0.5"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:round="50dp" />
crossfade=0.5時,效果:
crossfade=1時,效果:
接下來幾個屬性是對圖片進行調節:
warmth
色溫:1=neutral自然, 2=warm暖色, 0.5=cold冷色
brightness
亮度:0 = black暗色, 1 = original原始, 2 = twice as bright兩倍亮度
;這個效果不好貼圖,大家自行驗證;
saturation
飽和度:0 = grayscale灰色, 1 = original原始, 2 = hyper saturated超飽和
;
contrast
對比:1 = unchanged原始, 0 = gray暗淡, 2 = high contrast高對比
;
上面屬性的取值都是0、1、2,不過大家可以取其他值,效果也是不一樣的。
最後一個屬性overlay
,表示不知道怎麼用,看不到沒效果,大家看看評論跟我說聲?
五、ImageFilterView
ImageFilterView
與ImageFilterButton
的屬性一模一樣,只是它兩繼承的父類不一樣,一些操作也就不一樣。ImageFilterButton
繼承自AppCompatImageButton
,也就是ImageButtion
。而ImageFilterView
繼承自ImageView
。
六、MockView
還記得你家項目經理給你的UI原型圖麼?想不想回敬一下項目經理,是時候了~
MockView
能簡單的幫助構建UI界面,通過對角線形成的矩形+標籤。例如:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.utils.widget.MockView
android:id="@+id/first"
android:layout_width="100dp"
android:layout_height="100dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.utils.widget.MockView
android:id="@+id/second"
android:layout_width="100dp"
android:layout_height="100dp"
app:layout_constraintLeft_toRightOf="@id/first"
app:layout_constraintTop_toBottomOf="@id/first" />
</androidx.constraintlayout.widget.ConstraintLayout>
「效果:」
中間黑色顯示的是MockView
的id
。通過MockView
可以很好的構建一些UI思路。
七、MotionLayout
MitionLayou主要是用來實現動作動畫,可以參考我的另一篇文章:Android MotionLayout動畫:續寫ConstraintLayout新篇章
八、邊距問題的補充
有ConstraintLayout
實踐經驗的朋友應該知道margin
設置負值在ConstraintLayout
是沒有效果的。例如下面佈局:TextView B的layout_marginLeft
和layout_marginTop
屬性是不會生效的。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/vA"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginLeft="30dp"
android:layout_marginTop="30dp"
android:background="@color/colorPrimary"
android:gravity="center"
android:text="A"
android:textColor="#FFFFFF"
android:textSize="20sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/vB"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginLeft="-30dp"
android:layout_marginTop="-30dp"
android:background="@color/colorAccent"
android:gravity="center"
android:text="B"
android:textColor="#FFFFFF"
android:textSize="20sp"
app:layout_constraintLeft_toRightOf="@id/vA"
app:layout_constraintTop_toBottomOf="@id/vA" />
</androidx.constraintlayout.widget.ConstraintLayout>
「效果:」
可以通過輕量級的Space
來間接實現這種效果。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/vA"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginLeft="30dp"
android:layout_marginTop="30dp"
android:background="@color/colorPrimary"
android:gravity="center"
android:text="A"
android:textColor="#FFFFFF"
android:textSize="20sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Space
android:id="@+id/space"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginRight="30dp"
android:layout_marginBottom="30dp"
app:layout_constraintBottom_toBottomOf="@id/vA"
app:layout_constraintRight_toRightOf="@id/vA" />
<TextView
android:id="@+id/vB"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginLeft="-30dp"
android:layout_marginTop="-30dp"
android:background="@color/colorAccent"
android:gravity="center"
android:text="B"
android:textColor="#FFFFFF"
android:textSize="20sp"
app:layout_constraintLeft_toRightOf="@id/space"
app:layout_constraintTop_toBottomOf="@id/space" />
</androidx.constraintlayout.widget.ConstraintLayout>
「效果:」
2.0還增加了ConstraintProperties
類用於通過api(代碼)更新ConstraintLayout
子視圖;其他一些可以參考官方文檔,估計也差不多了。
「參考:」
能讀到末尾的小夥伴都是很棒,耐力很好。如果本文對你有用,幫忙「點個贊」,推薦好文。
最後的最後,個人能力有限,有理解錯誤或者錯誤的地方,希望大家幫忙糾正,非常感謝。
「歡迎star 歡迎點贊」:Github