上篇文章介绍了界面Activity的启动方式和生命周期,本篇将继续介绍在界面Activity中的内容是如何绘制展示给用户的。

在Android系统上运行新创建的界面Activtiy,给用户展示的是空白的。而得益于AndroidStudio的强大模板支持,新创建的界面Activity会自动重写onCreate()方法,并在该方法内自动创建以下两行类似默认代码。

super.onCreate(savedInstanceState)
setContentView(R.layout.xxx)

显然,setContentView()方法就是加载当前界面Activity的绘制内容,而名为R.layout.xxx的参数中的xxx,代指文件名,被称为布局文件。在系列文章首篇有提到res目录是存放应用资源的文件夹,该目录下有个名为layout的目录,就是专门存放布局文件的。

在AndroidStudio中,只要在layout目录下创建布局文件,就可以在java目录下源代码中通过调用R.layout中与布局文件同名的常量来使用,而其中的类名为R的文件,是在java(generate)目录的当前应用包名下自动生成的。

在AndroidStudio中新创建的布局文件默认生成如下模板,该文件符合xml文件格式,其中标签名只能是Android系统定义或我们应用内自定义的控件名,而其属性也只能使用已定义的属性。

<?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=".MainActivity"> </android.support.constraint.ConstraintLayout>

视图

View视图,是Android系统绘制图形的基本单元类,也是系统与用户交互的基本单元类。所有的视图类追踪其父类,都可以追溯到同一个父类android.view.View

如果直接在布局文件中添加视图使用,那么称这个视图是静态加载的。静态创建视图需要使用大量布局文件,在应用程序打包时占用大量存储空间,但是当应用程序安装之后,Android系统会更加快速的绘制这些视图。

相反地,也可以在源代码文件的当前界面Activity中,直接调用视图类的构造方法创建一个视图,并加载到当前界面中,那么称该视图是动态加载的。动态创建的视图是写入到源代码中,所以应用程序打包时不会占用太多存储空间,只是在应用程序安装运行后,Android系统需要更多时间去解析并绘制这些视图。

上面两种视图加载方式虽然有所差别,但是在不涉及大量视图加载的轻量级应用中差别不是很大。一般界面中的视图都是固定数量的,可以直接静态创建视图的,这样可以借助AndroidStudio的布局预览插件实时预览当前界面布局效果;只有在根据不同数据加载不同视图这种类似的动态改变大量界面时,可能动态加载视图的方式会更方便一些。

视图除了需要创建加载外,还可以修改其属性以便于Android系统区分绘制,同样地,既可以在静态加载时动态设置默认属性值,也可以在源代码中动态设置属性值。

注意:针对同一个属性,动态设置结果会优先覆盖静态属性值。

任何视图在创建加载后,有两个属性值是必须要指定的,那就是表示视图宽度和高度的尺寸,在布局文件中,静态加载的视图必须以标签中的android:layout_widthandroid:layout_height属性设置;在源代码中,动态加载的视图默认宽度和高度都是LayoutParams.WRAP_CONTENT,表示自适应内容尺寸,也可以调用视图类中的.layoutParams.width.layoutParams.height变量直接访问或修改。

根据视图负责的任务不同,可大致分为容器类视图、展示类视图、编辑类视图三种类型。

  1. 容器类视图,一般符合后缀为Layout的命名规范,可作为父视图,在其内部继续加载绘制子视图,包括展示类视图和编辑类视图。常用的有将子视图位置线性排列绘制的线性布局android.widget.LinearLayout,对子视图使用相对位置绘制的相对布局android.widget.RelativeLayout,集合上述两种布局优势更加灵活绘制子视图相对位置的约束布局androidx.constraintlayout.widget.ConstraintLayout等。
  2. 展示类视图,可理解为输出型视图,一般是容器类视图的子类,且符合后缀为View的命名规范,主要负责将数据资源以更直观的形式展示给用户。像是展示文本内容的android.widget.TextView,展示图片的android.widget.ImageView,展示视频的android.widget.VideoView等。
  3. 编辑类视图,可理解为输入型视图,一般是展示类视图或容器类视图的子类,且命名都不符合上述两种类型的命名规范,可以根据用户的控制实时改变自身视图的相关属性。比如允许用户文字输入并实时展示输入结果的android.widget.EditText,允许用户选择时间并展示的android.widget.TimePicker,在用户操作需要提示时展示的android.widget.Toast等。

系统视图

正如上边举例的几个视图,都是在AndroidSDK中定义的视图,称为系统视图。系统视图都位于android.widget包下。

在日常开发中,只需要掌握如何使用系统视图即可。这里以动态加载的方式介绍几个常用视图案例。

首先是界面Activity对应的布局文件,以上文中AndroidStudio自动生成的模板布局文件为例,为了在源代码中获取到这个布局文件的根视图,在根视图<android.support.constraint.ConstraintLayout>标签中增加android:id 属性,属性值以@+id/开头表示新增属性值,可以定义任意值;而如果以@id/开头,只能使用已经定义过的属性值。所有新增的属性,都由AndroidStudio自动存储在前面提到的R文件中,在源代码中可以通过R.id.xxx常量获取,其中xxx即定义的属性值。

android:id="@+id/activity_main_root"

之后便可以在源代码的界面Activity中获取到该视图,并操作该视图例如增加子视图。

        val rootLayout = findViewById<ConstraintLayout>(R.id.activity_main_root)

        val button1=Button(this)
button1.text="按钮"
button1.id=R.id.button_1 val textView1=TextView(this)
textView1.text="文本展示区" val layoutParamsButton1 = ConstraintLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
layoutParamsButton1.topMargin = 500
layoutParamsButton1.topToTop=rootLayout.id
layoutParamsButton1.leftToLeft=rootLayout.id
layoutParamsButton1.rightToRight=rootLayout.id
button1.layoutParams=layoutParamsButton1 val layoutParamsTextView1 = ConstraintLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
layoutParamsTextView1.circleConstraint=button1.id
layoutParamsTextView1.circleAngle=30f
layoutParamsTextView1.circleRadius=300
textView1.layoutParams=layoutParamsTextView1 rootLayout.addView(button1)
rootLayout.addView(textView1)

在上边代码中有用到R.id.button1变量,由于其绑定视图并未在布局文件中静态加载,所以该视图对应的id值只能在资源文件中单独声明,可以在res资源目录下新建一个xml格式的资源文件,在其中增加如下代码以创建一个id供上边源代码中使用。

<resources>
<item name="button_1" type="id"></item>
</resources>

最终上述代码的运行效果如下图。

自定义视图

虽然AndroidSDK提供的系统视图可以满足大部分常见场景,但是开发的乐趣在于自由绘制视图及交互。Android系统同样允许开发者自由定义视图以满足他们的特殊爱好需求。想必不需强调也能理解,这种自定义视图只能在定义该视图的所属modle模块中使用,或者在定义了自定义视图的所属modle模块的被依赖子modle模块中使用。对于自定义视图,其相关属性可能因视图而异,但是使用方式与系统视图类似,所以主要掌握如何定义自定义视图。这里以自定义的输入框为例简单说明。

自定义视图首先要在源代码中定义一个视图类,该类必须继承android.view.View或其子类。在介绍视图分类时可以发现,容器类视图是另外两种视图的父类。所以一般在编辑类视图和展示类视图均不满足自定义视图的需求时,就直接选择容器类视图作为自定义视图的父类,而不是直接继承android.view.View。而如果自定义视图的需求与某个编辑类视图或展示类视图的功能重合,也就可以直接在该视图上修改,那么直接继承这个编辑类视图或展示类视图即可。

在继承视图类之后,Java语言的话,需要重写自定义视图的三个构造方法,而如果应用程序是运行在Android5.0及以上的系统上,还需要重写第四个构造方法。

Android版本号5.0,对应于Android API等级为21,对应Android版本简称为LOLLIPOP

    public MyEditText(Context context) {
//动态加载视图时调用该构造方法
this(context, null);
} public MyEditText(Context context, AttributeSet attrs) {
//静态加载视图时调用该构造方法
this(context, attrs, 0);
} public MyEditText(Context context, AttributeSet attrs, int defStyleAttr) {
//确保其他构造方法都最终调用该构造方法
super(context, attrs, defStyleAttr);
} @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public MyEditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}

而如果是Kotlin编写的话,只需要重写两个构造方法即可

	@JvmOverloads
constructor(context: Context?, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : super(
context,
attrs,
defStyleAttr
) {
} @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
constructor(
context: Context?,
attrs: AttributeSet?,
defStyleAttr: Int,
defStyleRes: Int
) : super(context, attrs, defStyleAttr, defStyleRes) {
}

之后可根据需求重写三个父类方法

  1. 视图布局,调用onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int)方法,确保该自定义视图在其父视图的位置。
  2. 视图测量,调用onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int)方法,确定该视图在绘制时的尺寸。
  3. 视图绘制,调用onDraw(canvas: Canvas)方法,将该视图中的属性信息绘制到屏幕上。

如果上述过程都没有问题,就可以像加载系统视图一样的加载自定义视图了。但是自定义视图往往也需要新增一些属性控制,如果简单的在该视图类中增加一些getter、setter方法以访问相关属性,这样就可以在源代码中像使用系统视图一样的动态加载自定义视图了。但是如果想在布局文件中静态加载视图,这就需要重写上述代码中的三参构造方法中了。

静态设置属性值,首先就要先创建固定的属性名,在res/values资源目录中创建xml格式的资源文件,并在其中定义属性值如下所示

    <declare-styleable name="StyleMyEditText">
<attr name="myDefaultText" format="string" />
</declare-styleable>

之后在上述重写构造方法中获取这些属性值并赋予其实际意义即可。

		val typedArray = context.obtainStyledAttributes(attrs, R.styleable.StyleMyEditText)
val defaultText = typedArray.getString(R.styleable.StyleMyEditText_myDefaultText)
setText(defaultText)
typedArray.recycle() //回收代码不能少,否则容易内存泄漏

如此,就可以在静态加载自定义视图的布局文件中正常设置其属性值了。

    <com.kotlin.helloword.MyEditText
android:layout_width="200dp"
android:layout_height="100dp"
app:myDefaultText="默认显示内容"
/>

自定义属性值通过app域使用,android域只能设置Android系统提供的固定属性值

内容有限,视图的简单使用先大致这些,详情案例可参考后续视频内容。而视图作为系统与用户交互的基本单元,其使用功能更加多元,下一章将继续介绍视图间的动画与交互。

Android系统编程入门系列之界面Activity绘制展示的更多相关文章

  1. Android系统编程入门系列之界面Activity交互响应

    在上篇文章中已经了解到界面Activity的绘制完全依赖其加载的视图组件View,不仅如此,用户的每次触摸操作都可以在界面Activity内接收并响应,也可以直接传递给其中的某个视图View响应.本文 ...

  2. Android系统编程入门系列之界面Activity响应丝滑的传统动画

    上篇文章介绍了应用程序内对用户操作响应的相关方法位置,简单的响应逻辑可以是从一个界面Activity跳转到另一个界面Activity,也可以是某些视图View的相对变化.然而不管是启动一个界面执行新界 ...

  3. Android系统编程入门系列之界面Activity响应多元的属性动画

    在响应丝滑动画一篇文章中,分别介绍了作用于普通视图.绘制视图的绘制对象.和界面这三种对象的动画效果,但是都有一些使用的局限性.比如这些动画都只是以屏幕上绘制更新的方式绘制动画,并没有真实改变作用对象的 ...

  4. Android系统编程入门系列之硬件交互——多媒体展示

    前两篇文章通过麦克风硬件和摄像头硬件分别采集音频和视频的多媒体数据,在得到的多媒体数据通常是以编码文件的格式存储,在用户需要展示时,可通过设备的内置扩音器或蓝牙耳机等硬件播放音频,通过设备的显示屏或外 ...

  5. Android系统编程入门系列之加载界面Activity

    上回说到应用初始化加载及其生命周期,在Android系统调用Applicaiton.onCreate()之后,继续创建并加载清单文件中注册的首个界面即主Activity,也可称之为入口界面.主Acti ...

  6. Android系统编程入门系列之应用环境及开发环境介绍

        作为移动端操作系统,目前最新的Android 11.0已经发展的比较完善了,现在也到了系统的整理一番的时间,接下来的系列文章将以Android开发者为中心,争取用归纳总结的态度对初级入门者所应 ...

  7. Android系统编程入门系列之硬件交互——多媒体摄像头

    多媒体系列硬件 多媒体包括图片.动画.音频.视频,这些多媒体素材的采集(输入)主要依靠摄像头和麦克风等硬件设备转化为基础数据,而他们的播放渲染(输出),则需要依靠具有相关功能的编解码软件.当然随着硬件 ...

  8. Android系统编程入门系列之加载服务Service

    之前几篇文章简单梳理了在Android系统的四大组件之一,最主要的界面Activity中,使应用程序与用户进行交互响应的相关知识点,那对于应用程序中不需要与用户交互的逻辑,又要用到哪些内容呢?本文开始 ...

  9. Android系统编程入门系列之服务Service齐头并进多线程任务

    在上篇文章中初步了解了Android系统的四大组件之一的服务Service,在服务内可以执行无用户交互的耗时操作任务,但是包括之前关于界面系列文章在内,生命周期方法都是在主线程内被系统回调的.如果直接 ...

随机推荐

  1. Jenkins远程代码执行漏洞

    于一个月前,进行服务器巡检时,发现服务器存在不明进程,并且以Jenkins用户身份来运行.当时进行了处理并修复了漏洞.在此补上修复过程 第一反应是Jenkins存在漏洞,于是Google Jenkin ...

  2. 物联网技术nbiot与LoRa的区别有哪些

    http://zixun.258.com/1870021.html 物联网技术nbiot与LoRa的区别有哪些 万物物联是大趋势,在中国nbiot与LoRa是热门的低功耗广域网技术,这两者作为最典型的 ...

  3. 10.8 ss:查看网络状态

    ss命令 是类似并将取代netstat的工具,它能用来查看网络状态信息,包括TCP.UDP连接.端口等.它的优点是能够显示更多更详细的有关网络连接状态的信息,而且比netstat更快速更高效.    ...

  4. descriptor 'decode' requires a 'bytes' object but received a 'NoneType'

    记录在使用python过程中踩的坑------ 使用xlwt库对excel文件进行保存时报错 descriptor 'decode' requires a 'bytes' object but rec ...

  5. 使用ONNX将模型转移至Caffe2和移动端

    使用ONNX将模型转移至Caffe2和移动端 本文介绍如何使用 ONNX 将 PyTorch 中定义的模型转换为 ONNX 格式,然后将其加载到 Caffe2 中.一旦进入 Caffe2, 就可以运行 ...

  6. Python_Selenium 之PO模式的思想、优化思路

    一.PO模式思想 PO模式是一种自动化测试设计模式,将页面定位和业务操作分开,也就是把对象的定位和测试脚本分开,从而提供可维护性. PO设计模式基础(页面作为类.元素对象作为属性.元素操作作为方法) ...

  7. 编译原理-DFA的化简(最小化)

    对于给定的DFA    M,寻找一个状态数比M小的DFA    M'使得L(M)=L(M') 1.状态的等价性: 假设s和t为M的两个状态 ①若分别从状态s和状态t出发都能读出某个字α而停止于终态,则 ...

  8. 深入 Go 中各个高性能 JSON 解析库

    转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com/archives/535 其实本来我是没打算去看 JSON 库的性能问题的,但是最近我对 ...

  9. 对标 Spring Boot & Cloud ,轻量框架 Solon 1.4.14 发布

    Solon 是一个轻量的Java基础开发框架.强调,克制 + 简洁 + 开放的原则:力求,更小.更快.更自由的体验.支持:RPC.REST API.MVC.Job.Micro service.WebS ...

  10. .Net Redis实战指南——常用命令

    本问主要介绍rabbitmqctl工具的常用命令. vhost 一个RabbitMQ服务器可以创建多个虚拟的消息服务器,称之为虚拟主机(virtual host),简称为vhost.vhost之间是绝 ...