自定义视图一:扩展现有的视图,添加新的XML属性
这个系列是老外写的,干货!翻译出来一起学习。如有不妥,不吝赐教!
简介
这个系列详细的介绍了如何穿件Android自定义视图。主要涉及的内容有如何绘制内容,layout和measure的原理,如何继承实现view group以及如何给其子视图添加动画。第一篇主要讲述如何扩展和使用现有的视图,以及如何添加特有的XML属性。
特定的任务使用特定的视图
Android提供的view都是比较通用的,哪里都可以用。但是在开发应用的过程中需要对这些通用的view加以修改。很多时候这些代码都添加到了Activity中,这样是的Activity的代码杂乱,影响维护。
假设你在开发一个最用用户训练数据的应用。比如用户的总运动时长,总运动距离以及不同的运动类型等。为了友好的把数据展现给用户,你需要根据用户运动的时间长度做不同的处理。比如他运动了2378秒,那么显示的肯定是48分钟。18550秒,那显示的肯定是5小时9分钟。
创建一个自定义视图
处理上面的问题最好就是定义一个view。这个view里包含了处理上面时间的功能。我们来创建一个自定义view:DurationTextView
。
class DurationTextView : TextView {
constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet) {
}
}
TextView
和其他的view一样有三个构造函数:一个是只需要Context
的,一个是上面给出的需要两个参数Context
和AttributeSet
,还有一个需要这两个参数之外的关于style的参数。一般来说,上面给出的构造方法就可以满足。下面在布局中使用上面定义的view。
<demo.customview.customviewdemo.Views.DurationTextView
android:id="@+id/duration_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
注意,这里需要给出自定义视图的全名称。
添加展示逻辑
现在这个视图和标准的TextView
没什么太大的区别。我们为这个view添加一个duration
属性来设置和读取duration值。
var duration: Float
get() = _duration
set(value) {
_duration = value
var durationInMinutes: Int = Math.round(_duration / 60)
var hours: Int = durationInMinutes / 60
var minutes: Int = durationInMinutes % 60
var hourText: String = ""
var minuteText: String = ""
if (hours > 0) {
hourText = "$hours ${if (hours == 1) "hour" else "hours"}"
}
if (minutes > 0) {
minuteText = "$minutes ${if (minutes == 1) "minute" else "minutes"}"
}
if (hours == 0 && minutes == 0) {
minuteText = "Less than 1 minute"
}
var durationText = TEXT_TEMPLATE.format(hourText, minuteText)
text = durationText
}
companion object {
val TEXT_TEMPLATE = "Duration: %s %s"
}
这个方法接受一个Float
型的参数,训练时间的秒数。然后通过上面的转换逻辑把这个秒数转成用户友好的文字值。最后赋值给TextView
的text。转换的逻辑非常简单,就是把秒数转为分钟数,最后转为小时和分钟的值。分钟和小时数如果大于1的时候单位就显示为minutes和hours,等于1的时候为minute和hour。
用起来
现在就可以试试效果了。在Activity的onCreate()
方法里添加下面的代码:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var durationTextView1 = findViewById(R.id.duration_view1) as DurationTextView
durationTextView1.duration = 2378.0f
var durationTextView2 = findViewById(R.id.duration_view2) as DurationTextView
durationTextView2.duration = 3670.0f
var durationTextView3 = findViewById(R.id.duration_view3) as DurationTextView
durationTextView3.duration = 18550.0f
}
运行结果如下图:
看起来还不错吧。
添加XML属性
如果我们能够这个view添加一个方法来设置文本展示的模板,就像设定duration一样。但是,这个template其实并不会想duration一样需要经常的设置,更多的是一个类似于常数一样的存在。所以对于添加一个方法来说,添加一个XML属性更加合适。
首先添加values/attrs.xml文件,XML属性就在这个文件中定义。我们只需要添加一个字符串类型的,名称为template的属性。添加之后attrs.xml文件看起来是这样的:
<resources>
<declare-styleable name="TemplateTextView">
<attr name="template" format="string" />
</declare-styleable>
</resources>
我们声明了一个名称为TemplateTextView的declare-styleable节点。名字可以任意起,不过最好还是在哪个view里使用就叫做什么。这里叫做TemplateTextView是因为后面这个XML属性会用与很多其他的自定义view中。来看看是怎么使用这个属性的。
<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:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="demo.customview.customviewdemo.MainActivity">
<demo.customview.customviewdemo.Views.DurationTextView
android:id="@+id/duration_view1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Customized"
android:textSize="20sp"
app:template="%s was spent running" />
<!-- ...... -->
</LinearLayout>
在格式化代码(快捷键Ctrl+Alt+L,或者Cmd+Alt+L)后在LinearLayout
中会增加一行xmlns:app="http://schemas.android.com/apk/res-auto"
这一句声明了一个namespace,这样APP就可以理解我们刚刚在自定义view的布局中添加的属性了。当然需要使用这条声明里指定的app为前缀:app:template="%s was spent running"
。
现在就需要我们在代码里读取并应用新添加的属性值了。首先添加一个var template:String? = null
的属性,准备接收解析的字符串模板。
var attributes = context.obtainStyledAttributes(attributeSet, R.styleable.TemplateTextView)
template = attributes.getString(R.styleable.TemplateTextView_template)
if (template == null || !(template?.contains("%s", ignoreCase = true) ?: false)) {
template = TEXT_TEMPLATE
}
attributes.recycle()
第一行使用了AttributeSet
类型的参数attributeSet
,这个参数包含了在attrs.xml文件中定义的全部的属性。 方法obtainStyledAttributes()
主要做了两件事:
- 过滤全部的自定义属性,并把这些属性的定义和给定的值关联起来。
- 返回你在第二个参数所指定的属性组合。
R.styleable.TemplateTextView
就是我们自己定义的属性数组,这里只有一个元素。R.styleable.TemplateTextView_template
是我们自定义属性数组里的那个叫做template的元素。然后我们使用typed array来获取这个属性的值。如果没有设置模板,或者模板中不包含处理字符串的%s的话就是用我们之前定义的默认的文字模板来代替。
但是有一点需要格外注意:不管有没有获取到属性值,都要回收TypedArray对象,attributes.recycle()
。
上下两个分别设定了不同的template,中间的不设定,运行一下看看修改后的效果如何:
对于大多数的Android视图XML属性都有对应的方法来通过代码的方式设置对应的值。对于自定义视图是否需要这么做,主要取决于你打算怎么用。添加一个方法也没什么问题。
下一篇主要简述如何绘制视图的内容,并自定义一个显示折线图的视图。
自定义视图一:扩展现有的视图,添加新的XML属性的更多相关文章
- Android自定义视图一:扩展现有的视图,添加新的XML属性
这个系列是老外写的,干货!翻译出来一起学习.如有不妥,不吝赐教! Android自定义视图一:扩展现有的视图,添加新的XML属性 Android自定义视图二:如何绘制内容 Android自定义视图三: ...
- 向SQL Server 现有表中添加新列并添加描述.
注: sql server 2005 及以上支持. 版本估计是不支持(工作环境2005,2008). 工作需要, 需要向SQL Server 现有表中添加新列并添加描述. 从而有个如下存储过程. (先 ...
- UML建模语言入门 -- 用例视图详解 用例视图建模实战
. 作者 :万境绝尘 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/18964835 . 一. 用例视图概述 用例视图表述哪些 ...
- 【UML 建模】UML建模语言入门 -- 用例视图详解 用例视图建模实战
. 作者 :万境绝尘 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/18964835 . 一. 用例视图概述 用例视图表述哪些 ...
- ASP.NET MVC 5 - 给电影表和模型添加新字段
在本节中,您将使用Entity Framework Code First来实现模型类上的操作.从而使得这些操作和变更,可以应用到数据库中. 默认情况下,就像您在之前的教程中所作的那样,使用 Entit ...
- Asp.Net MVC4入门指南(7):给电影表和模型添加新字段
在本节中,您将使用Entity Framework Code First来实现模型类上的操作.从而使得这些操作和变更,可以应用到数据库中. 默认情况下,就像您在之前的教程中所作的那样,使用 Entit ...
- [转]ASP.NET MVC 5 - 给电影表和模型添加新字段
在本节中,您将使用Entity Framework Code First来实现模型类上的操作.从而使得这些操作和变更,可以应用到数据库中. 默认情况下,就像您在之前的教程中所作的那样,使用 Entit ...
- Linux 使用fdisk添加新分区
Linux系统由于数据累计增长.前期存储规划不合理等诸多因素,出现存储不够用的情况时,此时就需要扩展逻辑分区或添加新的逻辑分区.下面介绍一下通过使用fdsik添加新的逻辑分区. 首先使用df命令检查文 ...
- spring AOP Bean添加新方法
目的:为studentAdditionalDetails中添加Student的showDetails()和ExtraShowDetails()两个方法 spring 中AOP能够为现有的方法添加额外 ...
随机推荐
- Good Bye 2015 A. New Year and Days 签到
A. New Year and Days Today is Wednesday, the third day of the week. What's more interesting is tha ...
- 使用ADO连接oracle数据库“未找到提供程序。该程序可能未正确安装”解决方案
问题描述:VS2010开发的C++程序,在一台Win7旗舰版的已安装Oracle客户端的PC上连接不上Oracle,提示“未找到提供程序.该程序可能未正确安装”,其他语言编写的程序比如C#是可以成功连 ...
- centOS学习part2:安装JDK及tomcat
0 上一篇(http://www.cnblogs.com/souvenir/p/3875424.html)给大家介绍了centOS操作系统的安装,接下来我们来介绍centOS常用软件的安装以及配置,希 ...
- java连接access数据库
完整代码: package odbcj; import java.sql.Connection; import java.sql.DriverManager; import java.sql.Prep ...
- java基本数据类型存储范围
数据类型可以分为两大类: 1)基本类型: 2)扩展类型. 先来看一下Java语言的基本数据类型.它包括 类型 描述 取值范围 Boolean 布尔型 只有两个值true.false Char ...
- SPRING IN ACTION 第4版笔记-第十一章Persisting data with object-relational mapping-006Spring-Data的运行规则(@EnableJpaRepositories、<jpa:repositories>)
一.JpaRepository 1.要使Spring自动生成实现类的步骤 (1)配置文件xml <?xml version="1.0" encoding="UTF- ...
- WPF之通过EventTrigger修改模板中元素的属性
前言:对于此操作,我只想说是微软的神经,还是我的笨蛋.为什么EventTrigger就不能像Trigger那样直接设置Property以及Value就对属性进行操作,而必须要放一个Action,而默认 ...
- MyBatis学习总结_05_实现关联表查询
一.一对一关联 1.1.提出需求 根据班级id查询班级信息(带老师的信息) 1.2.创建表和数据 创建一张教师表和班级表,这里我们假设一个老师只负责教一个班,那么老师和班级之间的关系就是一种一对一的关 ...
- 在 MapPath 的 Path 参数中不允许出现“..”字符。
找到IIS应用程序池,“设置应用程序池默认属性”->“常规”->”启用 32 位应用程序”,设置为 True. 这样我的问题就解决了..
- Android 代码监控apk安装,卸载,替换
public class GetBroadcast extends BroadcastReceiver { private static GetBroadcast mReceiver = new Ge ...