Android开发笔记[2]-传统XML方式界面布局

发布时间 2023-07-24 22:36:56作者: qsBye

摘要

使用传统的XML方式对Android app界面进行布局.

平台信息

  • Android Studio: Electric Eel | 2022.1.1 Patch 2
  • Gradle:distributionUrl=https://services.gradle.org/distributions/gradle-7.5-bin.zip
  • jvmTarget = '1.8'
  • minSdk 24
  • targetSdk 34
  • compileSdk 34
  • 开发语言:Kotlin

安卓布局方式

  1. 传统XML方式
  2. Compose

Android工程的UI界面是哪一个文件定义的?

在Android工程中,UI界面是通过XML文件来定义的。每个Activity或Fragment都可以有一个对应的XML布局文件,用于描述该界面的UI组件和布局。这些XML文件通常存储在工程的res/layout目录下,并以.xml为文件扩展名。在XML文件中,可以使用各种布局和UI组件来构建界面,例如LinearLayout、RelativeLayout、TextView、Button等。通过在Java代码中调用setContentView()方法,可以将XML布局文件与Activity或Fragment关联起来,从而实现界面的显示。

Android界面布局方法

[https://blog.csdn.net/qq_42257666/article/details/105669319]
[https://blog.csdn.net/weixin_64582565/article/details/123511203]
使用布局编辑器构建界面
安卓官方布局示例

在布局编辑器中,您可以通过将界面元素拖动到可视化设计编辑器中(而不是手动编写布局 XML),快速构建布局。设计编辑器支持在不同的 Android 设备和版本上预览布局,并且您可以动态调整布局大小,以确保它能够很好地适应不同的屏幕尺寸。

安卓界面布局-Compose

安卓官方布局示例
[https://developer.android.google.cn/jetpack/compose/tutorial?hl=zh-cn]
[https://developer.android.google.cn/courses/pathways/compose?hl=zh-cn]
[https://developer.android.google.cn/codelabs/jetpack-compose-basics?hl=zh-cn&continue=https%3A%2F%2Fdeveloper.android.google.cn%2Fcourses%2Fpathways%2Fcompose%3Fhl%3Dzh-cn%23codelab-https%3A%2F%2Fdeveloper.android.com%2Fcodelabs%2Fjetpack-compose-basics#0]
如何使用 Compose 替换传统布局方式
Jetpack Compose之线性布局和帧布局
Jetpack Compose 是用于构建原生 Android 界面的新工具包。它使用更少的代码、强大的工具和直观的 Kotlin API,可以帮助您简化并加快 Android 界面开发。

在本教程中,您将使用声明性的函数构建一个简单的界面组件。您无需修改任何 XML 布局,也不需要使用布局编辑器。相反,您只需调用可组合函数来定义所需的元素,Compose 编译器即会完成后面的所有工作。
第 1 课:可组合函数
Jetpack Compose 是围绕可组合函数构建的。这些函数可让您以程序化方式定义应用的界面,只需描述应用界面的外观并提供数据依赖项,而不必关注界面的构建过程(初始化元素、将其附加到父项等)。如需创建可组合函数,只需将 @Composable 注解添加到函数名称中即可。
第 2 课:布局
界面元素采用多层次结构,元素中又包含其他元素。在 Compose 中,您可以通过从可组合函数中调用其他可组合函数来构建界面层次结构。
第 3 课:Material Design
Compose 旨在支持 Material Design 原则。它的许多界面元素都原生支持 Material Design。在本课中,您将使用 Material Design widget 来设置应用的样式。
第 4 课:列表和动画
列表和动画在应用内随处可见。在本课中,您将学习如何利用 Compose 轻松创建列表并添加有趣的动画效果。

传统的 Android 布局方式是使用 XML 文件来定义布局,然后在 Activity 或 Fragment 中通过 findViewById() 方法来找到对应的 View,并进行相关的操作。而在 Compose 中,布局的定义和渲染是在同一个地方完成的,即在 Composable 函数中定义 UI 布局结构,然后在需要使用该布局的地方直接调用该 Composable 函数即可。
然后在需要使用该布局的地方直接调用 MyLayout() 函数即可。
除了使用 Composable 函数来定义布局外,Compose 还提供了一些内置的布局组件,比如 Box、Row、Column、Stack、ConstraintLayout 等,这些组件可以方便地实现复杂的 UI 布局效果。
除了使用 Composable 函数和内置布局组件来定义 UI 布局外,Compose 还支持自定义布局组件,这样可以方便地实现特定的 UI 布局效果,提高代码复用性。
总之,使用 Compose 可以更加简洁、易于维护和扩展,是一种比传统布局方式更加优秀的布局方式。

安卓界面布局-XML

<?xml version="1.0" encoding="utf-8"?>

<!--开始main界面-->
<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:id="@+id/constraintLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

解释如上代码;
这段代码是一个XML布局文件,用于定义Android应用程序的主界面。让我为你解释一下每个部分的含义:

  • <?xml version="1.0" encoding="utf-8"?>:这是XML文件的声明,指定了XML的版本和编码方式。

  • <androidx.constraintlayout.widget.ConstraintLayout>:这是根元素,表示使用ConstraintLayout布局容器。ConstraintLayout是一种灵活的布局容器,可以帮助开发者创建复杂的界面。

  • xmlns:android="http://schemas.android.com/apk/res/android":这是一个命名空间声明,将"android"命名空间与"http://schemas.android.com/apk/res/android"关联起来。它允许在XML文件中使用Android特定的属性和元素。

  • xmlns:app="http://schemas.android.com/res-auto":这是另一个命名空间声明,将"app"命名空间与"http://schemas.android.com/res-auto"关联起来。它允许在XML文件中使用应用程序特定的属性和元素。

  • xmlns:tools="http://schemas.android.com/tools":这是第三个命名空间声明,将"tools"命名空间与"http://schemas.android.com/tools"关联起来。它允许在XML文件中使用用于开发工具的属性和元素。

  • android:id="@+id/constraintLayout":这是给ConstraintLayout元素分配一个唯一的ID,以便在代码中引用它。

  • android:layout_width="match_parent":这是设置ConstraintLayout的宽度为与父容器相匹配。

  • android:layout_height="match_parent":这是设置ConstraintLayout的高度为与父容器相匹配。

  • tools:context=".MainActivity":这是指定与此布局文件关联的Activity的上下文。在这个例子中,它指定了MainActivity作为上下文。

这只是布局文件的开头部分,还有更多的代码来定义界面的其他部分。
添加按钮:

<Button
android:id="@+id/click"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="点击变色"
android:background="@drawable/btn_st"
android:gravity="center"/>

android:height与android:width属性

Formats: dimension, enum Values: fill_parent, match_parent, wrap_content Specifies the basic height of the view. This is a required attribute for any view inside of a containing layout manager. Its value may be a dimension (such as "12dip") for a constant height or one of the special constants.
[https://www.cnblogs.com/xxNote/p/5427521.html]

layout_height
layout_width

一直以来对android:layout_height、android:layout_width、android:height、android:width这几个属性的关系有些不理解,既然有了android:layout_height和android:layout_width为什么还要有android:height、android:width呢?网上搜了好久发现都是重复一句扯淡的话,今天下午抽时间好好研究了下发现只有android:layout_height和android:layout_width的值是wrap_content的时候相应的android:height、android:width才起作用,即:android:layout_height和android:layout_width的值是wrap_content的时候,控件的最终大小是由android:height、android:width和控件的最小高度android:minHeight和最小宽度android:minWidth两者的最大值决定的。假设android:height为20px,而控件的最小高度android:minHeight为30px,那么当android:layout_height为wrap_content时,控件的高度是30px。

当android:layout_height和android:layout_width的值是match_parent或具体值(例如50dp、60px、30sp等)的时候,android:height、android:width是不起作用的。

android:gravity布局关键词

[https://www.jianshu.com/p/c8a00330a1b1]

android:gravity

简单的说法:

  • android:gravity :view里面的内容在这个view中的位置
  • android:layout_gravity :这个view相对于它父view的位置

其实关键还是“android:layout_gravity”属性

例如一个按钮,里面的内容因为已经限制在一个框框里面了,
那么android:gravity一般也就是往中间发散的九个方向放了。

而android:layout_gravity因为涉及到布局与控件各种相互关系,
因此设置之前,要仔细考虑才行。
那么平时用的,也就是LinearLayoutRelativeLayoutFrameLayout三种,
把上面那个“简单的做法”的LinearLayout替换为其它两个,
就是发现RelativeLayout对android:layout_gravity不起作用。
查资料也是,LinearLayout和FrameLayout支持android:layout_gravity。

记住这些:

  • 对于horizontal的LinearLayout,把android:layout_gravity设为top、center、bottom、center_vertical才有意义;
  • 对于vertical 的LinearLayout,把android:layout_gravity设为left、center、right、center_horizontal才有意义;
    RelativeLayout对android:layout_gravity不起作用
    center已经包含了center_vertical和center_horizontal两种意义了,用的时候不要忘了

Android布局的单位

  1. px:pixel,像素
  2. pt:印刷行业常用单位,iOS系统的设计单位
  3. dp:Android 系统的设计单位
    dp全称Density-independent pixel,可以理解为是一种独立于px之外的设计单位,是Andriod系统用来给设计师做基础设计使用的,也可以根据公式变换成 px。
    同pt一样,1dp在任何设备上的大小都应该是一样的,但是代表几个px却是不固定的,以此来应对不同ppi的设备。
  4. sp:安卓系统的字体单位
    Scale-independent Pixels - This is like the dp unit, but it is also scaled by the user's font size preference. It is recommend you use this unit when specifying font sizes, so they will be adjusted for both the screen density and the user's preference. 以中文的逻辑,与缩放无关,问题是缩放指的是什么?无关的意思是,不管参照物有多大,都是按照没有缩放的情况下一样.
    但是....事实恰好相反,反而与缩放有关.参照物是什么呢?原来android系统是可以设置字体大小的,参照物就是设置的字体大小.然而,系统字体放大了一倍,那么,如果使用sp为单位的字就会放大一倍显示,如果以dp为单位的字体就不会放大.所以说,sp根本就是和系统字体大小有关的单位.

安卓的内置Widget

[https://developer.android.google.cn/reference/android/widget/Button?hl=en]

androidx.appcompat.widget.AppCompatTextView
androidx.appcompat.widget.AppCompatButton

按钮和应用程序按钮之间的区别

AppCompatButton只是一个按钮,它支持旧版本平台上的兼容功能,包括:

允许通过ViewCompat中的背景着色方法对其背景进行动态着色。
允许使用R.attr.backgroundTint和R.attr.backgroundTint模式设置背景色调。
允许使用R.attr.fontFamily设置字体系列
当您在布局中使用按钮并且top-level活动/对话框由appcompat提供时,将自动使用此按钮。编写自定义视图时,应该只需要手动使用此类。

去除状态栏

<style name="Theme.AndroidKotlinVirtualJoystick" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<style name="Theme.AndroidKotlinVirtualJoystick" parent="Theme.MaterialComponents.DayNight.NoActionBar">

三色马卡龙配色

[https://blog.csdn.net/Ranger_AA/article/details/106478773]
马卡龙,又称作玛卡龙、法式小圆饼,是一种用蛋白、杏仁粉、白砂糖和糖霜制作,并夹有水果酱或奶油的法式甜点。口感丰富,外脆内柔,外观五彩缤纷,精致小巧。Macaron一词本是法语,实际发音较接近“马卡红”。“马卡龙”是使用西班牙语发音音译的结果。马卡龙是意大利人发明的。而马卡龙具体的起源,有多个版本。
卡龙色的效果及十六进制代码如下:

马卡龙草莓奶霜      /#f1707d
马卡龙玫瑰粉红      /#f155369
马卡龙基尔Anperiaru      /#ef5767
大巧克力杏仁饼      /#ae716e
巧克力马卡龙      /#cb8e85
杏仁饼,果仁糖,巧克力      /#cf8878
覆盆子马卡龙      /#c86f67
盐焦糖杏仁饼      /#f1ccb8
杏仁饼香草奶油      /#f2debd
马卡龙抹茶奶霜      /#b8d38f
开心果杏仁饼      /#ddff95
牛奶咖啡马卡龙      /#ff9b6a
马卡龙粉      /#f1b8f1
杏仁饼紫      /#d9b8f1
杏仁饼橙      /#f1ccb8
杏仁饼黄      /#f1f1b8
杏仁饼海洋蓝      /#b8f1ed
杏仁饼绿      /#b8f1cc
马卡龙枫武      /#e7dbca
杏仁饼蔓藤      /#e26538
杏仁饼,柚子      /#f3d751
马卡龙玫瑰果      /#fd803a
马卡龙覆盆子乳酪蛋糕      /#fe997b
马卡龙枫薰衣草      /#c490a0
巧克力马卡龙      /#f28a63
马卡龙达米安      /#df7a30
马卡龙,果仁巧克力,充满异国情调      /#e96d29
马卡龙黑醋栗      /#cb7799
马卡龙激情巧克力      /#e3a04f
马卡龙Powaru      /#edc02f
马卡龙芒果激情      /#fecf45
马卡龙柚子、覆盆子      /#f9b747
马卡龙让JA斗      /#c28164
杏仁饼、牛轧糖、蒙特利马尔      /#ed987b
马卡龙可可凤梨      /#ffe647
马卡龙伯爵茶      /#e49f5e
马卡龙巧克力、橙      /#ff8444
马卡龙、巧克力、樱桃色      /#ac5e74
巧克力马卡龙 Bananu      /#f0b735
马卡龙Kurakkure      /#d08a8a

实现

布局草图

代码

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>

<!-- 整个页面的布局 -->
<LinearLayout
    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:id="@+id/constraintLayout"
    android:orientation="vertical"
    app:layout_constraintTop_toTopOf="parent"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    tools:context=".MainActivity">

    <!-- 顶部导航栏 -->
    <LinearLayout
        android:id="@+id/top_info_layout"
        android:layout_width="match_parent"
        android:layout_height="36dp"
        android:gravity="top"
        android:orientation="horizontal"
        android:background="@color/top_info_layout"
        app:layout_constraintTop_toTopOf="parent">
        <!-- 导向另一个界面 -->
        <androidx.appcompat.widget.AppCompatButton
            android:id="@+id/btn_goto"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/color1"
            android:layout_gravity="start"
            android:textSize="18dp"
            android:textColor="@color/white"
            android:text="进入设置" />

        <!-- APP标题 -->
        <androidx.appcompat.widget.AppCompatTextView
            android:id="@+id/app_name_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/app_name"
            android:textAlignment="inherit"
            android:layout_gravity="center"
            android:textColor="@color/black"
            android:textSize="10sp" />

        <!-- 连接蓝牙 -->
        <androidx.appcompat.widget.AppCompatTextView
            android:id="@+id/connect_bluetooth"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="连接蓝牙"
            android:textColor="@color/material_dynamic_neutral20"
            android:textAlignment="inherit"
            android:layout_gravity="center"
            android:textSize="18dp" />

        <!-- 关于 -->
        <androidx.appcompat.widget.AppCompatTextView
            android:id="@+id/app_about"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="关于本应用"
            android:textColor="@color/bottom_info_layout"
            android:textAlignment="inherit"
            android:layout_gravity="center"
            android:textSize="18dp" />

    </LinearLayout>
    <!--end 顶部导航栏 -->


    <!-- 中部信息显示区 -->
    <LinearLayout
        android:id="@+id/middle_info_layout"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:orientation="horizontal"
        android:gravity="bottom"
        android:background="@color/middle_info_layout"
        app:layout_constraintTop_toTopOf="parent">

        <!-- 摄像头画面 -->
        <androidx.appcompat.widget.AppCompatImageView
            android:id="@+id/camera_view"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:adjustViewBounds="true"
            android:scaleType="centerCrop"
            android:src="@mipmap/no_signal" />

        <!-- 状态显示区 -->
        <LinearLayout
            android:id="@+id/status_area"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            android:gravity="end"
            android:background="@color/white">

            <!-- 电量状态 -->
            <LinearLayout
                android:id="@+id/battery_status_layout"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">

                <androidx.appcompat.widget.AppCompatTextView
                    android:id="@+id/battery_status_title"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="@string/battery_status_text"
                    android:textAlignment="inherit"
                    android:textColor="@color/black"
                    android:textSize="18sp" />

                <androidx.appcompat.widget.AppCompatTextView
                    android:id="@+id/battery_status_value"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="@color/black"
                    android:text="100%"
                    android:textSize="18sp" />

            </LinearLayout>

            <!-- 速度,加速度 -->
            <LinearLayout
                android:id="@+id/speed_acceleration_layout"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">

                <androidx.appcompat.widget.AppCompatTextView
                    android:id="@+id/speed_acceleration_title"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="@string/speed_acceleration_text"
                    android:textColor="@color/black"
                    android:textAlignment="inherit"
                    android:textSize="18sp" />

                <androidx.appcompat.widget.AppCompatTextView
                    android:id="@+id/speed_acceleration_value"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="@color/black"
                    android:text=""
                    android:textSize="18sp" />

            </LinearLayout>

            <!-- 机身旋转角度 -->
            <LinearLayout
                android:id="@+id/rotation_angle_layout"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">

                <androidx.appcompat.widget.AppCompatTextView
                    android:id="@+id/rotation_angle_title"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="@string/rotation_angle_text"
                    android:textColor="@color/black"
                    android:textAlignment="inherit"
                    android:textSize="18sp" />

                <androidx.appcompat.widget.AppCompatTextView
                    android:id="@+id/rotation_angle_value"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="@color/black"
                    android:text=""
                    android:textSize="18sp" />
            </LinearLayout>

            <!-- 指示摄像头区域 -->
            <androidx.appcompat.widget.AppCompatTextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textAlignment="inherit"
                android:layout_gravity="start"
                android:textColor="@color/black"
                android:text="?摄像头"
                android:textSize="18sp" />

        </LinearLayout>
        <!-- end 状态显示区 -->

    </LinearLayout>
    <!-- end 中部信息显示区 -->

    <!-- 底部控制区 -->
    <LinearLayout
        android:id="@+id/bottom_info_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal"
        android:gravity="bottom"
        android:background="@color/bottom_info_layout"
        app:layout_constraintTop_toTopOf="parent">

        <!-- 日志显示区 -->
        <LinearLayout
            android:id="@+id/log_area"
            android:layout_width="150dp"
            android:layout_height="match_parent"
            android:background="@color/white">

            <!-- 文本 -->
            <androidx.appcompat.widget.AppCompatTextView
                android:id="@+id/log_area_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textAlignment="inherit"
                android:layout_gravity="start"
                android:textColor="@color/black"
                android:text="无日志"
                android:textSize="10sp" />

        </LinearLayout>
        <!-- end 日志显示区 -->

        <!-- 控制区 -->
        <LinearLayout
            android:id="@+id/control_area"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <!-- 控制手柄 -->
            <com.mbeddev.androidkotlinvirtualjoystick.JoystickView
                android:id="@+id/my_joystick"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                />
            <!-- end 控制手柄 -->

        </LinearLayout>
        <!-- end 控制区 -->

    </LinearLayout>
    <!--end 底部控制区-->

</LinearLayout>
<!-- 结束整个页面布局 -->

效果