ConstraintLayout 2.0 新特性详解及实战

栏目: Android · 发布时间: 4年前

内容简介:ConstraintLayout在 1.0 的时候提供了 GuideLine 辅助布局,在 1.1 时提供了 Group 和 Barrier,在 2.0 时候提供了Layer以及放开了限制,开发者可以自定义 Helper 了。Group可以用来控制一组view的可见性可以通过控制 group 的 hide/show 来直接控制一组 view(button4,button9) 的可见性。

ConstraintLayout在 1.0 的时候提供了 GuideLine 辅助布局,在 1.1 时提供了 Group 和 Barrier,在 2.0 时候提供了Layer以及放开了限制,开发者可以自定义 Helper 了。

Group (Added in 1.1)

Group可以用来控制一组view的可见性

<android.support.constraint.Group
              android:id="@+id/group"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:visibility="visible"
              app:constraint_referenced_ids="button4,button9" />
复制代码

可以通过控制 group 的 hide/show 来直接控制一组 view(button4,button9) 的可见性。

Barrier (Added in 1.1)

来看一个场景,下面是一个表单,Email 和 Password 左对齐 中间的虚线为 GuideLine,具体字段都和GuideLine左对齐。

ConstraintLayout 2.0 新特性详解及实战

现在如果需要做多语言,翻译为德文后变成了下面的效果

ConstraintLayout 2.0 新特性详解及实战

这时候就需要Barrier出场了,Barrier是栅栏的意思,可以理解为挡着不让超过。

ConstraintLayout 2.0 新特性详解及实战

改进方法

  • 把中间的虚线GuideLine换成Barrier
  • 把①和②加入到Barrier的referenced_ids中
  • 指定barrierDirection为right(右侧不超过)
  • 把③和④左边对齐到Barrier的右边

这样 Email 和 Password就不会超出Barrier,大致代码如下(有删减, 完整代码参考这里 )

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout>
    <TextView
        android:id="@+id/tv_email"
        app:layout_constraintBottom_toTopOf="@+id/tv_password"
        app:layout_constraintStart_toStartOf="@+id/tv_password"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="E-mail Addresse" />

    <EditText
        android:id="@+id/et_email"
        android:text="me@gmail.com"
        app:layout_constraintBaseline_toBaselineOf="@+id/tv_email"
        app:layout_constraintStart_toEndOf="@+id/barrier" />

    <TextView
        android:id="@+id/tv_password"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tv_email" />

    <EditText
        android:id="@+id/et_password"
        android:inputType="textPassword"
        android:text="2321321"
        app:layout_constraintBaseline_toBaselineOf="@+id/tv_password"
        app:layout_constraintStart_toEndOf="@+id/barrier" />

    <android.support.constraint.Barrier
        android:id="@+id/barrier"
        app:barrierDirection="right"
        app:constraint_referenced_ids="tv_email,tv_password" />

</android.support.constraint.ConstraintLayout>
复制代码

Layer (Added in 2.0)

Layer 可以看作是它引用的 view 的边界(可以理解为包含这些 view 的一个 ViewGroup,但是Layer并不是ViewGroup,Layer并不会增加 view 的层级)。另外Layer支持对里面的 view 一起做变换。

考虑这么一个场景,如果一个页面里面有部分 view 需要加个背景,使用Layer引用这几个 view,然后给Layer设置背景就可以了。如果不用Layer,只能另外加个 ViewGroup 包住这几个 View 了,这样会增加 view 的层级,不利于性能。

看一个示例( 完整代码 ):

ConstraintLayout 2.0 新特性详解及实战

图中Layer包住了中间的 6 个按钮,绿色边线白色填充是通过 Layer设置背景完成的。另外对Layer里面的所有按钮一起做动画,出来的效果就是这样子

ConstraintLayout2.0 除了提供几个默认实现的ConstraintHelper外,还提供开发者自定义ConstraintHelper的方式。

自定义 Helper

为什么需要自定义?

  • 保持 view 的层级不变,不像 ViewGroup 会增加 view 的层级
  • 封装一些特定的行为,方便复用
  • 一个 View 可以被多个 Helper引用,可以很方便组合出一些复杂的效果出来

如何自定义?

  • Helper持有 view 的引用,所以可以获取 view (getViews)然后操作 view
  • 提供了 onLayout 前后的 callback(updatePreLayout/updatePreLayout)
  • Helper 继承了 view,所以Helper本身也是 view

CircularRevealHelper

ConstraintLayout 2.0 新特性详解及实战

对一张图片作出CircularReveal的效果 ViewAnimationUtils给我们提供了createCircularReveal这个函数

public static Animator createCircularReveal(View view,
            int centerX,  int centerY, float startRadius, float endRadius) 
复制代码

借助这个函数只需要计算出中心点(centerX,centerY)和 endRadius(半径)就可以很方便实现CircularReveal的效果

class CircularRevealHelper @JvmOverloads constructor(
        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : ConstraintHelper(context, attrs, defStyleAttr) {

    override fun updatePostLayout(container: ConstraintLayout) {
        super.updatePostLayout(container)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            val views = getViews(container)
            for (view in views) {
                val anim = ViewAnimationUtils.createCircularReveal(view, view.width / 2,
                        view.height / 2, 0f,
                        Math.hypot((view.height / 2).toDouble(), (view.width / 2).toDouble()).toFloat())
                anim.duration = 3000
                anim.start()
            }
        }
    }
}
复制代码

updatePostLayout会在 onLayout 之后调用,在这里做动画就可以。

有了CircularRevealHelper之后可以直接在 xml 里面使用,在CircularRevealHelper的constraint_referenced_ids里面指定需要做动画 view。

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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">


    <ImageView
        android:id="@+id/img_mario"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@drawable/mario" />

    <cn.feng.constraintLayout2.helps.CircularRevealHelper
        android:id="@+id/helper"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:constraint_referenced_ids="img_mario"
        tools:ignore="MissingConstraints" />

</android.support.constraint.ConstraintLayout>
复制代码

后面如果要对 view 做CircularReveal直接在 xml 里面指定就可以了,做到了很好的复用。

FlyinHelper

ConstraintLayout 2.0 新特性详解及实战

再来看看这个 Flyin 的飞入效果,view 从四周飞入到各自的位置。

这个动画的关键在于计算出每个 view 该从什么方向飞入。

ConstraintLayout 2.0 新特性详解及实战

红色边框的位置可以借助前面介绍的的Layer找到(当然也可以 不借助Layer,自己算 ,稍显复杂),从而计算出红色框框部分的中间点位置, 再和图中每个 view 的中间点比较(图中每个白点的位置)从而得出每个 view 该从哪个方向飞入。

计算每个view 的初始位置代码如下,借助上面的图形应该很好理解。

for (view in views) {

            val viewCenterX = (view.left + view.right) / 2
            val viewCenterY = (view.top + view.bottom) / 2


            val startTranslationX = if (viewCenterX < centerPoint.x) -2000f else 2000f
            val startTranslationY = if (viewCenterY < centerPoint.y) -2000f else 2000f


            view.translationX = (1 - animatedFraction) * startTranslationX
            view.translationY = (1 - animatedFraction) * startTranslationY
        }
复制代码

FlyinHelper 的完整代码 参考这里

ComposeMultipleHelper

每个 view 不但可以接受一个ConstraintHelper,还可以同时接受多个ConstraintHelper。

ConstraintLayout 2.0 新特性详解及实战

左边的四个 ImageView 和右下的 FloatingActionButton 都有 Flyin 的效果,同时左边的四个ImageView还在绕 Y 轴做 3D 旋转。上方的 Seekbar的背景在做CircularReveal的效果。有了前面编写的CircularRevealHelper以及 FlyInHelper 我们可以很方便做到这样的效果。

代码参考这里

Flow (VirtualLayout)

Flow 是 VirtualLayout,Flow 可以像 Chain 那样帮助快速横向/纵向布局constraint_referenced_ids里面的元素。 通过flow_wrapMode可以指定具体的排列方式,有三种模式

  • wrap none : 简单地把constraint_referenced_ids里面的元素组成chain,即使空间不够
ConstraintLayout 2.0 新特性详解及实战
  • wrap chain : 根据空间的大小和元素的大小组成一条或者多条 chain
ConstraintLayout 2.0 新特性详解及实战
  • wrap aligned : wrap chain类似,但是会对齐
ConstraintLayout 2.0 新特性详解及实战

下面看下如何实现这个计算器布局:

ConstraintLayout 2.0 新特性详解及实战
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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:context=".activity.MainActivity">


    <android.support.constraint.helper.Flow
        android:id="@+id/flow"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#FFC107"
        android:padding="20dp"
        app:constraint_referenced_ids="tv_num_7,tv_num_8,tv_num_9,tv_num_4,tv_num_5,tv_num_6,tv_num_1,tv_num_2,tv_num_3,tv_num_0,tv_operator_div,tv_dot,tv_operator_times"
        app:flow_horizontalGap="10dp"
        app:flow_maxElementsWrap="3"
        app:flow_verticalGap="10dp"
        app:flow_wrapMode="aligned"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <TextView
        android:id="@+id/tv_num_7"
        style="@style/text_view_style"
        android:text="7" />

    <TextView
        android:id="@+id/tv_num_8"
        style="@style/text_view_style"
        android:text="8" />

    <TextView
        android:id="@+id/tv_num_9"
        style="@style/text_view_style"
        android:text="9" />


    <TextView
        android:id="@+id/tv_num_4"
        style="@style/text_view_style"
        android:text="4" />

    <TextView
        android:id="@+id/tv_num_5"
        style="@style/text_view_style"
        android:text="5" />

    <TextView
        android:id="@+id/tv_num_6"
        style="@style/text_view_style"
        android:text="6" />


    <TextView
        android:id="@+id/tv_num_1"
        style="@style/text_view_style"
        android:text="1" />

    <TextView
        android:id="@+id/tv_num_2"
        style="@style/text_view_style"
        android:text="2" />

    <TextView
        android:id="@+id/tv_num_3"
        style="@style/text_view_style"
        android:text="3" />

    <TextView
        android:id="@+id/tv_num_0"
        style="@style/text_view_style"
        android:text="0" />

    <TextView
        android:id="@+id/tv_operator_div"
        style="@style/text_view_style"
        android:text="/"
        tools:layout_editor_absoluteX="156dp"
        tools:layout_editor_absoluteY="501dp" />

    <TextView
        android:id="@+id/tv_operator_times"
        style="@style/text_view_style"
        android:text="*" />

    <TextView
        android:id="@+id/tv_dot"
        style="@style/text_view_style"
        android:text="."
        tools:layout_editor_absoluteX="278dp"
        tools:layout_editor_absoluteY="501dp" />

    <TextView
        android:id="@+id/KE"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#00BCD4"
        android:gravity="center"
        android:text="Compute"
        android:textColor="@android:color/white"
        android:textSize="24sp"
        app:layout_constraintBottom_toBottomOf="@+id/tv_operator_times"
        app:layout_constraintEnd_toEndOf="@+id/tv_dot"
        app:layout_constraintHorizontal_bias="1.0"
        app:layout_constraintStart_toStartOf="@+id/tv_operator_div"
        app:layout_constraintTop_toTopOf="@+id/tv_operator_times" />

    <TextView
        android:id="@+id/KR"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#03A9F4"
        android:gravity="right|center_vertical"
        android:paddingEnd="16dp"
        android:text="0"
        android:textColor="@android:color/white"
        android:textSize="58sp"
        app:layout_constraintBottom_toTopOf="@+id/flow"
        app:layout_constraintEnd_toEndOf="@+id/flow"
        app:layout_constraintStart_toStartOf="@+id/flow"
        app:layout_constraintTop_toTopOf="parent" />


</android.support.constraint.ConstraintLayout>
复制代码

借助 flow 很快可以布局出来,这里flow_wrapMode使用的是aligned,id 为KE的TextView可以对齐到 Flow 里面的 view,id 为KR的TextView可以对齐到 Flow,另外 Flow 也是ConstraintHelper,所以Flow 也是个 View,可以设置背景,padding等元素。 那么这样布局有什么优势? 这样的布局 view 都在一个层级,不使用 ViewGroup,减少层级。

流式 APIs

1.1 之前需要这样修改属性

val set = ConstraintSet()
        set.clone(constraintLayout)
        set.setTranslationZ(R.id.image, 32f)
        set.setMargin(R.id.image, ConstraintSet.START, 43)
        set.applyTo(constraintLayout)
复制代码

2.0 提供了ConstraintProperties 可以使用流式 API 修改属性

val properties = ConstraintProperties(findViewById(R.id.image))
        properties.translationZ(32f)
                .margin(ConstraintSet.START, 43)
                .apply()
复制代码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

世界因你不同:李开复自传(纪念版)

世界因你不同:李开复自传(纪念版)

李开复,范海涛 著作 / 中信出版社 / 2015-7-10 / 39.00

编辑推荐 1.李开复唯一一部描写全面生平事迹的传记:《世界因你不同:李开复自传》书中讲述了家庭教育培育的“天才少年”;学校教育塑造的“创新青年”,走入世界顶级大公司,苹果、微软、谷歌等亲历的风云内幕,岁月30载不懈奋斗、追求事业成功的辉煌历程。 2.娓娓道来、字字珠玑、可读性和故事性皆佳。李开复博士是青少年成长成才的励志偶像,年轻家长、学校教师阅读后也能从中得到感悟和启发。 3.......一起来看看 《世界因你不同:李开复自传(纪念版)》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具