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构建动态布局。下期再计划编写了

相关文章
相关标签/搜索