最强大的布局——约束布局(ConstraintLayout)的一些技巧

一.使用viewBinding
1.想开启那个模块的viewBinding就进入对应模块的build.gradle
在这里插入图片描述
2.进入app的build.gradle
在这里插入图片描述
开启viewbinding

viewBinding {
        enabled = true
    }

3.当你开启viewbinding后,app目录下的所有布局文件都会生成一个绑定类。这个类的类名是以xml布局文件名去掉下换线后,单词首字母大写加上Binding命名的。如activity_main.xml生成的绑定类为ActivityMainBinding.

如果你不想生成绑定类,也很简单,将tools:viewBindingIgnore=“true” 属性添加到相应布局文件的根视图中。
在这里插入图片描述
使用就更简单了,如下

package com.example.animationtest

import android.os.Bundle
import android.os.PersistableBundle
import androidx.appcompat.app.AppCompatActivity
import com.example.animationtest.databinding.ConstrainBinding

class Constraint:AppCompatActivity() {
    private val viewBinding=ConstrainBinding.inflate(layoutInflater)
    override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
        super.onCreate(savedInstanceState, persistentState)
        setContentView(viewBinding.root)
    }
}

viewBinding.root就是布局的根布局

二.开发中可能会遇到的场景

1.当A或者C宽度固定或者为wrap_content,你想要B的宽度自适应。

其实这个场景可以分解为:

B.width=屏幕宽度-A.width-C.width(视情况可能还会减去margin或padding)

在这里插入图片描述
如果用约束布局就要用android:layout_width="0dp"来解决这个问题,那就是把B的宽度设置为0dp。

如下

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <TextView
        android:id="@+id/text_a"
        android:layout_width="40dp"
        android:layout_height="wrap_content"
        android:text="A"
        android:background="@color/black"
        android:textColor="@color/white"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toStartOf="@id/text_b"
        tools:ignore="MissingConstraints" />

    <TextView
        android:id="@+id/text_b"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="B"
        android:textColor="@color/white"
        android:background="@color/design_default_color_primary_dark"
        app:layout_constraintStart_toEndOf="@id/text_a"
        app:layout_constraintEnd_toStartOf="@id/text_c"
        tools:ignore="MissingConstraints" />

    <TextView
        android:id="@+id/text_c"
        android:layout_width="60dp"
        android:layout_height="wrap_content"
        android:text="C"
        android:textColor="@color/teal_200"
        app:layout_constraintStart_toEndOf="@id/text_b"
        app:layout_constraintEnd_toEndOf="parent"
        tools:ignore="MissingConstraints" />

</androidx.constraintlayout.widget.ConstraintLayout>

布局很简单,就三个Textview,把三个控件相互约束,再把把B的宽度设置为0dp。

2.如果想要三个控件平分宽度
2.1 如果你没有背景,就把三个控件相互约束,但三个控件的宽度都必须是wrap_content或固定宽度(这个应该都能理解吧,为match_parent其中一个子布局就充满了父控件了,就别谈自适应了)。

在这里插入图片描述
2.2 你有背景就把三个控件的宽度都设置为0dp

其实这个场景可以分解为:

任意一个的宽度=(屏幕宽度-(视情况可能减去margin或padding))/3
在这里插入图片描述

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

    <TextView
        android:id="@+id/text_a"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:background="@color/black"
        android:text="A"
        android:textColor="@color/white"
        app:layout_constraintEnd_toStartOf="@id/text_b"
        app:layout_constraintStart_toStartOf="parent"
        tools:ignore="MissingConstraints" />

    <TextView
        android:id="@+id/text_b"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:background="@color/design_default_color_primary_dark"
        android:text="B"
        android:textColor="@color/white"
        app:layout_constraintEnd_toStartOf="@id/text_c"
        app:layout_constraintStart_toEndOf="@id/text_a"
        tools:ignore="MissingConstraints" />

    <TextView
        android:id="@+id/text_c"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="C"
        android:textColor="@color/teal_200"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/text_b"
        tools:ignore="MissingConstraints" />

</androidx.constraintlayout.widget.ConstraintLayout>

现在基本上都是用约束布局,这就像属性动画一样,它可以适用所有场景。

三.动态改变布局

1.获取屏幕宽度
可以去下面的博客去看,写的很好
Android 获取屏幕宽度和高度的几种方法
当你获取了屏幕宽度或高度,你就可以写一写适配Android的布局了。

val outSize = DisplayMetrics()
        windowManager.defaultDisplay.getRealMetrics(outSize)
        val width = outSize.widthPixels
        val height = outSize.widthPixels

        viewBinding.textA.updateLayoutParams<ConstraintLayout.LayoutParams> {
            topMargin=width
        }

2.动态改变布局

2.1 改变布局属性
1.viewBinding.textA.updateLayoutParams<ConstraintLayout.LayoutParams> { topMargin=width }
ConstraintLayout.LayoutParams 这里是父布局的LayoutParams,
比如我的text A的父布局就是ConstraintLayout

2.2 改变布局约束

 val constrainSet = ConstraintSet().apply { clone(viewBinding.root) }
        constrainSet.connect(
            viewBinding.textC.id,ConstraintSet.START,viewBinding.textA.id,ConstraintSet.END
        )
        constrainSet.applyTo(viewBinding.root)

val constrainSet = ConstraintSet().apply { clone(viewBinding.root) }
这段话就是复制一份父布局的约束,方法有三个如下:

set.clone(constraintLayout: ConstraintLayout);
set.clone(set: ConstraintSet);
set.clone(context: Context, constraintLayoutId: Int);

这里需要注意,在调用clone()方法的时候,必须保证这个父布局的所有子布局都设置了 id,不然会报错,这跟它的内部实现原理有关,有兴趣可以去看一下源码。

  1. 设置组件之间的约束,常用方法有:
set.connect(startID: Int, startSide: Int, endID: Int, endSide: Int, margin: Int)
 
set.connect(startID: Int, startSide: Int, endID: Int, endSide: Int)
constrainSet.connect(
            viewBinding.textC.id,ConstraintSet.START,viewBinding.textA.id,ConstraintSet.END,20
        )

这句话的意思就是: 修改一个id为textC的约束,依赖的约束组件为一个id为textA的控件,修改为textC的左边和textA的右边对其,边距margin为20px:

3.然后把新的布局约束设置到原来的布局

constrainSet.applyTo(viewBinding.root)

4.View.post()
这是为了解决布局还没执行View的meaure()而获取宽高为0的其中一个解决方案。

viewBinding.textB.post 
{ 
val width=viewBinding.textB.width
val width=viewBinding.textB.measuredWidth
val height=viewBinding.textB.height
        }

大家应该注意到我上面用了width和measuredWidth
这两者有何区别呢
很简单就是字母的区别(开个冷玩笑)
measuredWidth:它的值在measure方法运行后会确定
width:它的值在layout方法运行后会确定

这里引用下这个博主的一段话
Android是消息驱动的模式,View.post的Runnable任务会被加入任务队列,并且等待第一次TraversalRunnable执行结束后才执行,此时已经执行过一次measure,layout过程了,所以在后面执行post的Runnable时,已经有measure的结果,因此此时可以获取到View的宽高。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橘子先生z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值