ConstraintLayout 約束佈局 2.0

一 約束佈局2.x新特性簡介

What's New in ConstraintLayout (Google I/O'19)php

2020.06.28更新: Flow使用詳解android

約束佈局2.0未使用過約束佈局的,可先查看上一篇文章ConstraintLayout 約束佈局1.xgit

implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta3'github

約束佈局是一個容許你靈活定義view位置和大小的ViewGroup,具備多種輔助工具,如GuideLine、Barrier、Group等。在靈活地放置各類各樣的view時,並不會增長Layout層級。2.0版本出了優化佈局性能外,還增長了一些新特性,使得開發過程更加方便:app

  • ConstraintHelper輔助工具的增長:Layer,flow
  • ConstraintHelper的自定義開放
  • ConstraintLayoutStates 界面狀態切換控制
  • API使用優化
  • **MotionLayout **構建一個動態的佈局

開發實際發現,到如今居然還有些人看不上約束佈局,Google對於約束佈局的信心和野心,難道還不足以引發重視?? ide

img-092dc569072ff195402cad0b6341b62a.jpg

二 ConstraintHelper輔助工具

2.1 Layer

Layer功能上能夠理解爲包含它所引用的view的一個父佈局viewGroup,但並不會增長layout的層級。這點是很是好用的,在Layer以前,想往本身view統一加個背景限制,通常都是另外加個view來作純背景展現。工具

像開發過程當中這種帶圖標的dialog,能夠用layer方便圈出背景。 佈局

image.png

另外,Layer支持對裏面的 view 一塊兒作變換,可看待成一個日常的父佈局,一塊兒作變換,設置visibility等(Layer自己也是繼承自view)。post

...

	<androidx.constraintlayout.helper.widget.Layer android:id="@+id/mLayer" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/shape_pet_white_with_corner" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintBottom_toBottomOf="@id/mRecyclerView" app:constraint_referenced_ids="mTvTitle,mRecyclerView,guide_line" />

	...
複製代碼

2.2 自定義 ConstraintHelper

自定義ConstraintHelper(簡稱Helper),能夠用來封裝針對ui的一些固定行爲,方便之後複用。並且,一個view又能夠同時被多個helper所引用,能夠很便捷地組合出多種效果。性能

注:Helper自己也是繼承了view的

  1. Helper提供了getViews()方法獲取所引用的全部view
  2. Helper提供了view的onLayout()/onMeasure()等流程方法執行先後的回調,如:updatePostLayout(container: ConstraintLayout?),onLayout()後 ;updatePreLayout(container: ConstraintLayout?) ,onLayout()前; 可利用這些方法和得到的view,封裝一些通用操做
  3. 再應用到佈局文件中,聲明要包含的view的id:app:constraint_referenced_ids="xxx,xxx"
class EnterAnimationHelper @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : ConstraintHelper(context, attrs, defStyleAttr) {

    override fun updatePostLayout(container: ConstraintLayout?) {
        super.updatePostLayout(container)
        val views = getViews(container)
        views.forEach {
            startEnterAnimation(it)
        }
    }

    private fun startEnterAnimation(view: View) {
        val translationY = -100f


        view.translationY = translationY
        val translationYHolder = PropertyValuesHolder.ofFloat(
            View.TRANSLATION_Y,
            translationY,
            0f
        )

        val keyFrame1 =
            Keyframe.ofFloat(0f, -6f)
        val keyFrame2 =
            Keyframe.ofFloat(0.6f, 25f)
        val keyFrame3 =
            Keyframe.ofFloat(1f, 0f)
        val rotateHolder =
            PropertyValuesHolder.ofKeyframe(View.ROTATION, keyFrame1, keyFrame2, keyFrame3)

        ObjectAnimator.ofPropertyValuesHolder(
            view,
            translationYHolder,
            rotateHolder
        )
            .apply { interpolator = AnticipateOvershootInterpolator() }
            .setDuration(600L)
            .start()
    }
}
複製代碼
...

 <com.cyq.x622.constraintlayoutsample.widget.EnterAnimationHelper android:layout_width="wrap_content" android:layout_height="wrap_content" app:constraint_referenced_ids="xxx,xxx" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" />

...
複製代碼

效果以下(此處是封裝了一個,上下位移和左右旋轉的動畫效果):

helper.gif

三 ConstraintLayoutStates

ConstraintLayoutStates能夠建立具備不一樣狀態的佈局並在它們之間輕鬆切換。一般,一個界面包含有加載狀態,加載成功狀態以及加載失敗狀態。利用ConstraintLayoutStates,能夠很方便地在已定義好的狀態之間相互切換。

3.1 建立不一樣狀態的佈局文件

根據須要,建立不一樣狀態下的佈局文件。每個文件必須有相同的view,只有屬性值,如visibility,和定位方式等能夠不相同(簡單起見,demo就定義了加載狀態和成功狀態,都是隻含有一個ProgressBar和一個TextView,id都相同,只有visibility不一樣)

image.png

3.2 建立狀態聲明XML文件

在xml資源文件夾中,建立一份xml文件,定義了layout可擁有的全部狀態constraint_set_test.xml:

<?xml version="1.0" encoding="utf-8"?>
<ConstraintLayoutStates xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
    <State android:id="@+id/loading" app:constraints="@layout/activity_state_start"/>

    <State android:id="@+id/success" app:constraints="@layout/activity_state_end"/>
</ConstraintLayoutStates>
複製代碼

3.3 加載聲明的狀態文件並在不一樣狀態之間切換

在activity/fragment中,在要應用狀態的ConstraintLayout上 使用loadLayoutDescription(),加載定義好的狀態xml文件聲明。而後即可以直接調用constraintLayout.setState()來切換狀態.

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_state_loading)

        mStateConstraintLayout.loadLayoutDescription(R.xml.constraint_set_test)

        //簡單延時,模擬io操做
        mStateConstraintLayout.postDelayed({
            mTv.text = "加載完成"
            mStateConstraintLayout.setState(R.id.success,0,0) //xml中定義的id
        },4_000L)

    }
複製代碼

效果以下:

state.gif

四 流式API

2.0之後,對屬性的修改提供了流式API。

ConstraintProperties(mBtnLayer)
            .alpha(0.5f)
            .margin(ConstraintSet.TOP, 100)
            .apply()
複製代碼

這部分比較簡單,能夠直接查看官方文檔ConstraintProperties

五 簡易demo

附件:constraintLayout.zip

六 Flow

Flow是一個特別強大的佈局輔助工具,支持多種佈局模式,能夠快速構造多樣性佈局。

Flow部分的demo及文中結構大部分翻譯自於Medium:Awesomeness of ConstraintLayout Flow

儘管約束佈局已經特別強大,能夠快速創建約束關係展現佈局,以下面兩個水平關係view

1_IwFYf6yetL5KkD_t8sUN2Q.png

兩個水平view

但若是要構建8個不一樣行的view,且相互之間有約束關係:

1_splakOAa-xL4wGu6evuRJg.png

每行之間間隔均分

固然,使用三條約束鏈即可以實現,但寫法上太過於繁瑣,尤爲是當垂直方向上還要設置對應關係時,更加繁瑣。

這時候Flow就能夠派上用場了。

Flow能夠當作一個具備多種約束功能的流式佈局,當空間不足時,能按設定的方式自動換行對齊。

Flow在使用上,有多個屬性能夠控制佈局約束關係:

  • orientation: horizontal  或者 vertical
  • WrapMode
  • Gap
  • Styles
  • Bias
  • Alignment

6.1 orientation

佈局方向:水平horizonta l或 垂直vertical

setOrientation(int orientation) android:orientation="horizontal|vertical"

image.png

水平佈局

image.png

垂直佈局

6.2 WrapMode

WrapMode屬性決定了Flow將如何控制所引用的views的佈局方法:NONECHAINALIGN

app:flow_wrapMode = " none | chain | aligned "

flow.setWrapMode(  Flow.WRAP_NONE | Flow.WRAP_CHAIN | Flow.WRAP_ALIGNED )

NONE:默認值,空間不夠狀況下Views不會被自動換到另外一行/列,直接超出屏幕範圍。

1_hxvRedZkqouW6uWQH8ej4Q.png

Wrap Mode = NONE

CHAIN:該模式與約束佈局的鏈式chain佈局類似,不只能夠實現相同的效果,還會額外的自動換行/換列處理

【注】:與約束佈局鏈式佈局同樣,CHAIN模式也有鏈式style:SPREAD(默認值),PACKEDSPREAD_INSIDE

1_splakOAa-xL4wGu6evuRJg.png

Wrap Mode = CHIAN 且 style = SPREAD

ALIGNED:該模式與上面的CHAIN相同,但額外增長了對齊方式

【注】:與CHAIN模式同樣也有鏈式style:SPREAD(默認值),PACKEDSPREAD_INSIDE

1_jt4vaw3aJFEIl0RiO9JPOg.png

Wrap Mode = ALIGNED 且 style = SPREAD

6.3 Gap

Gap是放置views時的水平和垂直間隔。 app:flow_horizontalGap  app:flow_verticalGap flow.setVerticalGap flow.setHorizontalGap 由於比較簡單,就不放圖了,手動試試就知道了。

6.4 Styles

當wrapMode爲chain或ALIGNED時生效。Flow的styles跟約束佈局以前的基礎鏈式佈局style是一個概念,有SPREAD(默認值),PACKEDSPREAD_INSIDE。不瞭解的可先查看上一篇:約束佈局的鏈式佈局

app:flow_horizontalStyle = 「 spread | spread_inside | packed 」 app:flow_verticalStyle = 「 spread | spread_inside | packed 」

應用到Flow中,效果以下:

1_splakOAa-xL4wGu6evuRJg (1).png

spread

1_C9sSFE0H8P9rtvHfneJiGw.png

Spread Inside

1_Ds-oG-rUkhZTPZvKNI2gDw.png

packed

6.5 Bias

flow的bias偏移,只在style爲packed時生效,由於當style爲spread或者spread_inside時,views是均勻分佈的,bias沒法起到做用。float值,範圍爲 0-1

app:flow_horizontalBias = 「 float "

app:flow_verticalBias = 「 float "

flow.setHorizontalBias( float)

flow.setVerticalBias( float)

這裏只取兩個端點值,0和1,方便理解。

image.png

bias爲0,貼到最左邊

image.png

bias爲1,貼到最右邊

6.6 Alignment

Alignment對齊方式,一樣也有水平和垂直。Alignment的對齊方向,與flow的方向必須是相反的才能生效。好比當flow的方向是水平時,Alignment只有設爲垂直纔有效。views是水平放置,對齊是view與view之間在垂直方向上的對齊方式。關於這個屬性,能夠運行demo多試幾遍理解理解。

app:flow_verticalAlignment = 「 top | center | bottom | baseline 」

app:flow_horizontalAlignment = 「 start| end 」

flow.setVerticalAlignment(
Flow.VERTICAL_ALIGN_TOP | Flow.VERTICAL_ALIGN_CENTER | Flow.VERTICAL_ALIGN_BOTTOM | Flow.VERTICAL_ALIGN_BASELINE )

flow.setHorizontalAlignment(
Flow.HORIZONTAL_ALIGN_START | Flow.HORIZONTAL_ALIGN_END )
複製代碼

1_k0u9hxx6pTbmlycxXtLwsg.png

Vertical Alignment = Bottom

6.7 Flow demo

flow的demo並不是遠程,可參考ConstraintFlowPlayground

後期計劃MotionLayout

MotionLayout是2.x版本的一個重要的更新,尤爲是MotionLayout構建動態佈局。下期再計劃編寫了

相關文章
相關標籤/搜索