转载自新小梦 - 掘金Android-XXM - github,写得太好了。这里仅作备忘,排版不弄了,建议点击链接去原文观看给作者点个关注 :)

基于本文发表,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能找到是福气。

一、Flow流式虚拟布局(alpha 5增加)

正常情况下,列表显示一般采用ListView或者RecyclerView来实现,但其子Item布局是非常呆板的。想象一下,如果一部作品的详情页结束打上一堆标签,样式如下,该怎么实现?

img 这种布局采用Flow来实现特别的简单和方便,而且通过flow_wrapMode属性可以设置不同对齐方式。

下面布局代码简单示例:Flow布局通过constraint_referenced_ids属性将要约束的Viewid进行关联,这里简单关联AGTextView,由于TextView没有设置约束条件,所以Android Studio 4.0 会报红,给ConstraintLayout布局添加tools:ignore="MissingConstraints"忽略报红提示。

Flow布局的flow_horizontalGap属性表示水平之间两个View的水平间隔,flow_verticalGap则是垂直方向间隔。

  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不可见。

img

chian值:所引用的View形成一条链,超出部分会自动换行,同行的View会平分宽度。

img

aligned值:所引用的View形成一条链,但View会在同行同列。

img

即然是一条链,那么可以通过链的样式进行约束。

链约束

flow_wrapMode属性为alignedchian属性时,通过链进行约束。ConstraintLayout,看完一篇真的就够了么? 此文有谈到链约束(Chain)。

Flow布局添加以下属性进行不同chain约束:

  • flow_firstHorizontalStyle 约束第一条水平链,当有多条链(多行)时,只约束第一条链(第一行),其他链(其他行)不约束;
  • flow_lastHorizontalStyle 约束最后一条水平链,当有多条链(多行)时,只约束最后一条链(最后一行),其他链(其他行)不约束;
  • flow_horizontalStyle 约束所有水平链;
  • flow_firstVerticalStyle 同水平约束;
  • flow_lastVerticalStyle 同水平约束;
  • flow_verticalStyle 约束所有垂直链;

以上属性,取值有:spreadspread_insidepacked

效果:

spread值:

img 代码:

     <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值:

img 代码:

   <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值:

img 代码:

     <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属性为nonechain情况吧。

Flow布局添加以下属性进行不同Align约束:

  • flow_verticalAlign 垂直方向对齐,取值有:topbottomcenterbaseline;
  • flow_horizontalAlign 水平方向对齐,取值有:startendcenter;

对齐方向一般与链的方向相反才可生效,例如垂直链样式,一般对齐View的左右边和中间。

简单举个例子:垂直方向顶部对齐。

效果图:

img 可以看到EGF顶部对齐。

代码:

     <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" />

简单的理解alignedchiannone的定制版,通过添加不同的属性定制而成。由于Flow是虚拟布局,简单理解就是约束助手,它并不会增加布局层级,却可以像正常的布局一样使用。

其他属性

上文的XML的布局没有设置Flow对View的组织方式(水平or 垂直),可以通过orientation属性来设置水平horizontal和垂直vertical方向,例如改为垂直方向。

img


flow_wrapMode属性为alignedchian时,通过flow_maxElementsWrap属性控制每行最大的子View数量。例如:flow_maxElementsWrap=3

img


flow_wrapMode属性为none时,A和G被挡住了,看不到。

img 要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" />

效果图: img 设置flow_horizontalBias=1那么G就可以看到了。该属性还有其他类似ChainStyle的属性w玩法,具体可以实践体验。当然,也可以在flow_wrapMode属性为其他值生效。

通过不同的属性可以搭配很多不同的效果,再加上MotionLayout动画,那就更炫酷了。

二、Layer 层布局

Layer也是一个约束助手ConstraintHelper,相对Flow比较简单,常用来增加背景,或者共同动画。由于ConstraintHelper本身继承自View,跟我们自己通过View在ConstraintLayout布局中给多个View添加共同背景没什么区别,只是更方便而已。

1、添加背景

ImageViewTextView添加个共同背景:

效果:

img

代码:

  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添加通过动画效果。

效果:

img 代码:

 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

FlowLayer都是ConstraintHelper的子类,当两者不满足需求时,可以通过继承ConstraintHelper来实现想要的约束效果。

在某乎APP有这么个类似的动画广告:

img 那么通过自定义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

  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能做的还有更多。

看看如何实现圆角或圆形图片:

原图:

imgroundPercent属性设置为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" />

效果:

img 也可以通过设置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" />

其他属性:

altSrcsrc属性是一样的概念,altSrc提供的资源将会和src提供的资源通过crossfade属性形成交叉淡化效果。默认情况下,crossfade=0altSrc所引用的资源不可见,取值在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时,效果:

img crossfade=1时,效果:

img


接下来几个属性是对图片进行调节:

 warmth`色温:`1=neutral自然, 2=warm暖色, 0.5=cold冷色

img brightness亮度:0 = black暗色, 1 = original原始, 2 = twice as bright两倍亮度;这个效果不好贴图,大家自行验证;

saturation饱和度:0 = grayscale灰色, 1 = original原始, 2 = hyper saturated超饱和

img contrast对比:1 = unchanged原始, 0 = gray暗淡, 2 = high contrast高对比;

img

上面属性的取值都是0、1、2,不过大家可以取其他值,效果也是不一样的。 最后一个属性overlay,表示不知道怎么用,看不到没效果,大家看看评论跟我说声?

img

五、ImageFilterView

ImageFilterViewImageFilterButton的属性一模一样,只是它两继承的父类不一样,一些操作也就不一样。ImageFilterButton继承自AppCompatImageButton,也就是ImageButtion。而ImageFilterView继承自ImageView

img

六、MockView

还记得你家项目经理给你的UI原型图么?想不想回敬一下项目经理,是时候了~ img MockView能简单的帮助构建UI界面,通过对角线形成的矩形+标签。例如:

  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>

效果:

img 中间黑色显示的是MockViewid。通过MockView可以很好的构建一些UI思路。

七、MotionLayout

MitionLayou主要是用来实现动作动画,可以参考我的另一篇文章:Android MotionLayout动画:续写ConstraintLayout新篇章

八、边距问题的补充

ConstraintLayout实践经验的朋友应该知道margin设置负值在ConstraintLayout是没有效果的。例如下面布局:TextView B的layout_marginLeftlayout_marginTop属性是不会生效的。

  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>

效果: img 可以通过轻量级的Space来间接实现这种效果。

  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>

效果: img

2.0还增加了ConstraintProperties类用于通过api(代码)更新ConstraintLayout子视图;其他一些可以参考官方文档,估计也差不多了。

参考:

官方英文文档

最后修改:2020 年 10 月 16 日 04 : 53 PM
如果觉得我的文章对你有用,请随意赞赏