美文网首页
JetPack知识点实战系列一:初识ConstraintLayo

JetPack知识点实战系列一:初识ConstraintLayo

作者: chonglingliu | 来源:发表于2020-09-01 00:19 被阅读0次

    我将通过一系列教程讲解利用JetPack的各个知识点,熟悉和巩固各个知识点的同时,我们将实现网易云音乐的功能。

    请注意:系列教程的假设前提是希望您有入门级别的Android开发技能,譬如Kotlin语法,基本UI的控件的使用,常见的Android API的使用等。

    第一节实战我们首来了解ConstraintLayout的知识,并且用ConstraintLayout的知识点来实现登录页面的布局。最终的界面效果如下。

    登录页面

    本节教程您主要将学习到如下内容:

    • 如何给View添加Constraint,从而实现界面布局需求?
    • 如何实现沉浸式布局来实现全屏显示?
    • 如何通过富文本来优化UI性能?
    • 如何通过属性动画实现抖动效果?

    创建项目

    首先打开Android Studio 4.0, 点击 Start a new Android Studio project 来创建一个 Android studio 项目。

    创建项目步骤1

    接下来选择 Empty Activity 模板

    创建项目步骤2

    然后填写项目的相关信息,Name为项目名称,Language选择Kotlin, Minimum SDK可以选择Android 5.0

    创建项目步骤3

    最后点击Finish, 项目就创建好了。等Gradle构建完成就可以进行Android开发了。开发之前我们可以看一下我们的constraintlayout的依赖版本,目前是2.0.1.

    创建项目步骤4
    项目基本配置

    进入res > values > styles, 修改AppTheme主题样式为Theme.AppCompat.Light.NoActionBar

    进入res > values > colors, 修改主题颜色

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <color name="colorPrimary">#FFFFFF</color>
        <color name="colorPrimaryDark">#FFFFFF</color>
        <color name="colorAccent">#DB2D25</color>
    </resources>
    

    ConstraintLayout初识

    打开activity_main.xml的布局文件,我们能看到如下的界面内容。界面的每部分的功能在图中有标识。

    请注意:打开的时候界面可能不会完全和图示内容一致,因为界面显示的内容是可以调整的,如何调整可以参阅相关资料。

    图形设计界面总览.jpg

    通过上图,我们可以看到默认根布局是ConstraintLayout,一个TextView居中显示。您脑海中可能会闪现如下一些问题:

    • TextView能居中显示是如何设置的?
    • 如何改变它的位置?

    这一系列问题我们就来进行一一介绍。

    去掉默认的Constraint

    去掉Constraints可以用下面两种方法来实现:

    1. 通过点击Constaint设置区域的Clear all Constraints按钮
    Clear all Constraints
    1. 选中TextView,右击选择Clear Constraints of Selection选项
    Clear Constraint of Selection

    去掉TextViewConstrains后会报错,因为安卓系统无法准确定位TextView的位置。

    Constraint Error

    提示:这个错误不会造成程序无法运行。因为UI控件没有设置任何Constraints,系统会把没有设置Constraints的UI控件放置在左上角。

    设置TextView居中显示

    设置居中显示有如下两种方法:

    1. 点击Constaint设置区域的Infer Constraints按钮
    Infer Constraints
    1. TextView上下左右四个约束锚点(Constraint anchor)连接到Constraint Layout上。

    点击TextView时会上下左右会出现四个圆圈,每个圆圈就是约束锚点(Constraint anchor)

    Constraint anchor

    点击连接锚点然后拖拽至Constraint Layout的每条边界上,就能居中布局了。

    拖拽
    调整TextView的位置

    TextView居中显示是因为左右上下两对约束的值是一样的,没有 垂直约束偏移(layout_constraintVertical_bias)水平约束偏移(layout_constraintHorizontal_bias)

    所以如果需要修改TextView的位置,只需要在Attribute -> Layout -> Constraint Widget 里面拖动或者拖动后在 Attribute -> Declared Attribute 手动改变这两个值就可以了。

    具体操作如下:

    修改位置

    ConstraintLayout实现登录页面

    有了初步的ConstraintLayout知识后,我们来实现下网易云音乐的登录界面,来了解更多的ConstraintLayout布局技巧。

    UI元素准备

    先将UI元素大致放置在布局文件中,包括logo,手机号登录按钮,四个第三方登录按钮,一个同意协议的TextView。

    提示:UI拖入放置的位置可以比较随意,但是最好放置大致位置和最后的布局文件相似,方便进行约束设置。

    13.jpg
    Logo布局调整

    我们将Logo的位置设置为水平居中,垂直中心位于20%的高度处。

    水平居中比较好设置,前面有介绍,就是将左右约束锚点(Constraint anchor)连接到Constraint Layout边界上。

    水平居中

    实现垂直中心位于20%的高度处,需要借助辅助标线中的水平辅助线(Horizontal Guideline).

    点击Constaint设置区域 -> Guidelines -> Add Horizontal Guideline 设置水平辅助线。

    添加水平辅助线

    水平辅助线默认是距离顶部固定的距(我选择的模拟器视图是20dp),可以点击辅助标线侧边的蓝色小圆圈,点击一下,辅助标线会变成固定距离底部的距离,再点击一下就变成了百分比。把百分比的值改为0.2,就实现了水平辅助线位于20%的高度的位置。

    水平辅助线设置

    接下来,将logo的上下约束锚点链接到这条水平辅助线就完成了。

    基线对齐
    同意协议的TextView的布局调整

    TextView的左边距里为屏幕宽度的10%,右边距为屏幕宽度的90%,底部距离为32dp

    添加两条垂直的辅助线(Vertical Guideline),第一条设置为10%,第二条设置为90%。

    垂直辅助线

    TextView的左约束锚点连接到10%的垂直辅助线,将TextView的右约束锚点连接到右边90%的垂直辅助线。将TextView的下约束锚点连接到ConstraintLayout。然后设置MarginBottom为32dp。

    设置约束

    注意:如果仅仅这么设置会有一点小问题,如果屏幕的宽度较小,文字可能会超出左右辅助线。

    譬如我们把文字改成24sp, 就会出现我们上面所说的问题。

    超出边界
    • 为什么呢?

    我们看一下Attributes -> Constraint Widget, 我们把鼠标悬停在正方形内的左右相对的">>"和"<<"符号上面,会提示约束为"Wrap Content"。这样表示目前TextView约束是左右居中,且宽度为包含内容,所以会出现超出左右辅助线的现象。

    • 解决办法:将TextView的左右约束改为"Match Constraints"。

    点击一次左右相对的">>"和"<<"其中一个图标按钮,此时左右约束变为左右距离为固定值,再点击一次图标按钮约束则变成了"Match Constraints"。就实现了不会超出左右垂直辅助线,高度包裹内容的目的。

    gif1.gif
    四个三方登录按钮布局调整

    微信登录按钮左侧距离为10%,网易登录按钮右侧距离为10%,其余两个按钮和这两个按钮水平均匀分布,且垂直居中对齐。距离底部的TextView间距为32dp。

    这个水平均匀分布需求用LinearLayout的weight能方便的实现,但是会增加布局的层级,不利于UI的优化。其实这个需求用ConstraintLayout也可以很方便的实现。

    实现方法如下:

    1. 四个按钮水平均匀分布于两个辅助线之间
    • 鼠标移动框住四个按钮,选中一个按钮右键在弹出列表中选择Chains,然后选择Create Horizontal Chain。这样会生成一个链接四个按钮的水平约束链条。
    • 点击上步生成的约束链(Chain), 右键在弹出列表中选择Chains,然后选择Horizontal Chain Style,选择spread inside。这样修改后的样式就是中间的按钮会平均分配距离,而最边上的两个按钮会贴边。
    • 微信易登录按钮的左侧约束锚点和右侧辅助线链接
    • 网易登录按钮的右侧约束锚点和右侧辅助线链接
    水平均匀分布
    1. 四个按钮垂直居中
    • 鼠标移动框住四个按钮,选中一个按钮右键在弹出列表中选择Align,然后选择Vertical Centers。这样四个按钮就在垂直方向上是居中显示的了。
    垂直居中
    1. 四个按钮和底部的文本距离32dp
    • 连接任意一个按钮的底部约束锚点和文本的顶部锚点,并设置间距为32dp
    底部距离
    手机登录按钮布局调整

    经过前面的练习,手机登录按钮的调整就驾轻就熟了。这里不做过多解释了。

    目前为止,界面布局没有写一行代码,这种感觉要不要太爽。

    沉浸式的布局

    我们运行下程序,布局和我们预期的一致。但是细心的你可能发现了一个问题,顶部状态栏是白色的,且状态栏上显示的信息是白色造成无法被看到,这个显示肯定无法接受!

    效果

    我们期望是全屏显示最好了。这个效果就是沉浸式的效果,Android 从4.4开始支持沉浸式的效果,Android5.0后设置沉浸式需要代码设置下。

    • MainActivity这个类里面添加一个immersive方法
    /* 代码实现沉浸式 */
    private fun immersive() {
    
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            // 5.0 +
            window.apply {
                // 1. 
                clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
                // 2. 
                addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
                // 3. 
                addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
                // 4. 
                statusBarColor = Color.TRANSPARENT
                }
    
            findViewById<ViewGroup>(android.R.id.content).apply {
                // 5. 
                for (index in 0 until childCount) {
                    val child = getChildAt(index) as? ViewGroup
                    child?.let {
                        // 6. 
                        it.fitsSystemWindows = true
                        it.clipToPadding = true
                    }
                }
            }
    
        } else {
            // 7 
            window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
        }
    }
    

    代码的具体作用为:

    1. 清除透明状态栏标识
    2. 添加绘制状态栏标识
    3. 添加透明导航标识
    4. 设置状态栏的颜色为透明
    5. 遍历根布局的子布局
    6. 让布局根据系统窗口来调整自己的布局
    7. Android4.4这个设置就能实现沉浸式效果
    • MainActivity类的onCreate里面调用immersive方法
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // 1
        immersive()
    }
    
    沉浸式效果

    富文本

    我们来看一下同意协议的部分,其实这个段文本是分为四部分的,每个部分都能点击,且点击后的效果是不一样的,第一部分点击切换是否同意协议,后面三部分点击效果是跳转到展示对应的协议内容的页面。

    21.jpeg

    初起来看着稍微有点复杂,其实用Android 提供的富文本就能很好的实现这个功能,并且具有较好的UI性能。

    • MainActivity这个类里面添加一个createSpannableString方法 和 是否同意协议checked这个属性
    private var checked = false
    
    private fun createSpannableString() {
    
        // 1
        SpannableString("  同意《用户协议》《隐私政策》《儿童隐私政策》").also {
            // 2
            it.setSpan(ForegroundColorSpan(Color.parseColor("#CDCDCD")), 2, 4, Spanned.SPAN_EXCLUSIVE_INCLUSIVE)
    
            // 3
            it.setSpan(object : ClickableSpan() {
                override fun onClick(widget: View) {
                    // 4
                    Toast.makeText(this@MainActivity, "点击用户协议",Toast.LENGTH_LONG).show()
                }
                override fun updateDrawState(ds: TextPaint) {
                    // 5
                    ds.color = Color.WHITE
                    ds.isUnderlineText = false
                }
            }, 4, 10, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
    
            it.setSpan(object : ClickableSpan(){
                override fun onClick(widget: View) {
                    Toast.makeText(this@MainActivity, "点击隐私政策", Toast.LENGTH_LONG).show()
                }
    
                override fun updateDrawState(ds: TextPaint) {
                    ds.color = Color.WHITE
                    ds.isUnderlineText = false
                }
            }, 10, 16, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
    
            it.setSpan(object : ClickableSpan() {
                override fun onClick(widget: View) {
                    Toast.makeText(this@MainActivity, "点击儿童用户协议", Toast.LENGTH_LONG).show()
                }
    
                override fun updateDrawState(ds: TextPaint) {
                    ds.color = Color.WHITE
                    ds.isUnderlineText = false
                }
            }, 16, 24, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
    
            // 6
            textView.apply {
                text = it
                // 7
                movementMethod = LinkMovementMethod.getInstance()
                // 8
                highlightColor = Color.TRANSPARENT
                // 9
                setOnClickListener {
                    if (selectionStart == -1 && selectionEnd == -1) {
                        // 10
                        checked = !checked
                        if (checked) {
                            setCompoundDrawablesWithIntrinsicBounds(
                                resources.getDrawable(R.mipmap.login_checked),
                                null,
                                null,
                                null
                            )
                        } else {
                            setCompoundDrawablesWithIntrinsicBounds(
                            resources.getDrawable(R.mipmap.login_unchecked),
                                null,
                                null,
                                null
                            )
                        }
                    }
                }
            }
    
        }
    
    }
    

    这段代码的含义是:

    1. 构造一个文本为" 同意《用户协议》《隐私政策》《儿童隐私政策》"的SpannableString
    2. 把" 同意"的文字颜色设置为"#CDCDCD"
    3. 把"《用户协议》"设置成可点击的ClickableSpan
    4. 点击"《用户协议》"这个ClickableSpan,会弹出一个toast,toast文本为"点击用户协议"
    5. 覆写"《用户协议》"这个ClickableSpan的默认样式,文字颜色为白色,去掉下划线
    6. 找到TextView,设置这个TextView的文本为刚才构造的SpannableString
    7. 设置成可点击,否则前面虽然设置了ClickableSpan,仍然无法响应点击效果
    8. 设置高亮为透明,意思就是不需要高亮显示
    9. TextView设置点击事件,这样" 同意"就能响应点击事件了
    10. 进入这个方法说明点击的是" 同意"这块区域,就记录下checked,并且修改drawableStart的图片
    • MainActivity类的onCreate里面调用createSpannableString方法
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        // 2
        createSpannableString()
    }
    
    点击效果

    属性动画

    登录页面有一个需求就是,如果没有勾选“同意协议”,则点击“手机号码登录”按钮时候,“同意协议”这段文字需要抖动一下,提示用户去勾选“用户协议”。

    这个抖动效果用属性动画就能很好的实现。

    • MainActivity类的onCreate里面加上如下代码
    login_btn.setOnClickListener {
        if (!checked) {
            ObjectAnimator.ofFloat(textView, "translationX", 0f, 25f, -25f, 25f, -25f, 15f, -15f, 6f, -6f, 0f).also {
                it.duration = 1000
            }.start()
        }
    }
    
    抖动效果

    更多精彩内容,敬请期待

    相关文章

      网友评论

          本文标题:JetPack知识点实战系列一:初识ConstraintLayo

          本文链接:https://www.haomeiwen.com/subject/hgrmsktx.html