介绍App开发经常涉及的自定义控件相关技术,主要包括自定义视图的过程与步骤、自定义动画的原理与实现、自定义对话框的概念与示例、自定义通知栏的用法与定制,另外介绍四大组件之一的服务Service的生命周期与启停方式。
一、自定义视图
1. 声明属性
自定义属性的实现步骤
(1)在res\values目录下创建attrs.xml,在该资源文件的declare-styleable节点下面填写自定义属性的attr标签,标签名称分别是textColor和textSize。
(2)在代码的widget目录中创建CustomPagerTab.java,从布局文件中获取属性数组描述,得到自定义的属性字段并加以处理。
(3)在布局文件根节点增加命名空间声明xmlns:app="http://schemas.android.com/ apk/res-auto",并把android.support.v4.view.PagerTabStrip的节点名称改为自定义视图的全路径名称(如com.example.custom.widget.CustomPagerTab),在该节点下指定新增的两个属性——app:textColor与app:textSize。
属性类型的取值说明
2. 构造对象
自定义视图的编码主要由3部分组成:
(1)重写构造函数,初始化该视图的自有属性。
(2)重写测量函数onMeasure,计算该视图的宽与高。
(3)重写绘图函数onLayout、onDraw、dispatchDraw,视情况重写3个中的一个或多个。
视图的3个构造函数
自定义视图一般要重写3个构造函数。前面在演示新控件CustomPagerTab时,示例代码给出了3个构造函数(实际只实现了两个),分别是:
(1)只带一个参数的public CustomPagerTab (Context context)。在代码中声明对象时采用该构造函数。
(2)带两个参数的public CustomPagerTab (Context context,AttributeSet attrs)。在布局文件中引用自定义视图时采用该构造函数。
(3)带3个参数的public CustomPagerTab (Context context, AttributeSet attrs, int defStyleAttr)。
3. 测量尺寸
在布局文件中对视图的宽和高有3种赋值方式
WRAP_CONTENT时候的测量办法
Android提供了相关度量方法,可以在不同情况下进行尺寸测量。需要测量的对象主要有以下3种。
1). 文本尺寸测量 文本尺寸分为文本的宽度和高度,要根据文本大小分别进行计算。
2). 图形尺寸测量 如果图形是Bitmap格式,就通过getWidth和getHeight方法获取位图对象的宽度和高度; 如果图形是Drawable格式,就通过getIntrinsicWidth方法获取该图形的宽度,通过getIntrinsicHeight方法获取该图形的高度。
3). 布局尺寸测量
4. 宽高尺寸的动态调整
尽管ListView在多数场合的高度计算也不会出错,但是把它放到ScrollView之中便出现问题了。
ScrollView本身叫做滚动视图,而列表视图ListView也是可以滚动的,于是一个滚动视图嵌套另一个也能滚动的视图,那么在双方的重叠区域,上下滑动的手势究竟表示要滚动哪个视图?
Android目前的处理对策是:如果ListView的高度被设置为wrap_content,则此时列表视图只显示一行的高度,然后布局内部只支持滚动ScrollView。
不滚动的列表视图
改造列表视图的一个可行方案,便是重写它的测量函数onMeasure,不管布局文件中设定的视图高度为何,都把列表视图的高度改为最大高度,即所有列表项高度加起来的总高度。
onMeasure方法重写之后的代码如下:
// 重写onMeasure方法,以便自行设定视图的高度
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 将高度设为最大值,即所有项加起来的总高度
int expandSpec = MeasureSpec.makeMeasureSpec( Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec); }
两种列表视图的效果对比
5. 绘制视图
有3个函数用于视图的绘制,分别是onLayout、onDraw和dispatchDraw。这3个函数的执行顺序是onLayout→onDraw→dispatchDraw。
1. onLayout
该方法用于定位子视图在本布局视图中的位置。
2. onDraw
该方法的入参为Canvas画布对象,在画布上绘图相当于在屏幕上绘图。
3. dispatchDraw
onDraw的调用在绘制子视图之前,而dispatchDraw的调用在绘制子视图之后。
画布Canvas的用法
Canvas提供了下列3类方法:
(1)划定可绘制的区域 虽然本视图内的所有区域都是可以绘制的,但是有时候开发者只想在某个矩形区域内部画画,这时在绘图之前就得指定允许绘图的区域界限。
(2)在区域内部绘制图形 该类方法用来绘制各种基本几何图形。
(3)画布的控制操作 控制操作包括旋转、缩放、平移以及存取画布状态的操作。
画笔Paint的用法:
Paint类定义了画笔的颜色、样式、粗细、阴影等,常用方法说明如下:
setAntiAlias:设置是否使用抗锯齿功能。主要用于画圆圈等曲线。
setDither:设置是否使用防抖动功能。
setColor:设置画笔的颜色。
setShadowLayer: 设置画笔的阴影区域与颜色。
setStyle:设置画笔的样式。Style.STROKE表示线条,Style.FILL表示填充。
setStrokeWidth:设置画笔线条的宽度。
绘制简单几何形状的效果
onDraw和dispatchDraw的区别
二、 自定义动画
1. 任务Runnable
Runnable接口可声明一连串任务,定义了接下来要做的事情。简单地说,Runnable接口就是一个代码片段。
实际开发中经常利用Handler启动任务。下面是Handler处理Runnable任务的常见方法说明:
post:立即启动Runnable任务。
postDelayed:延迟若干时间后启动Runnable任务。
postAtTime:在指定时间启动Runnable任务。
removeCallbacks:移除指定的Runnable任务。
延迟处理除了可以避免资源冲突,还能用于动画界面的渲染。
2.下拉刷新动画
计时器引入下拉刷新中,每隔若干时间展示逐步加大的视图偏移,从而实现下拉刷新头部的下拉动画。
下拉刷新动画的实现步骤如下:
(1)计算下拉刷新头部的高度。
(2)计时器每隔若干时间为padding设置逐步加大的高度偏移。
(3)高度的偏移值达到头部布局的高度时,停止Runnable的刷新任务,下拉动画完成。
下拉刷新动画的效果
3. 圆弧进度动画
Windows系统常用细长的进度条表示,在手机上因为屏幕限制,习惯展示圆形或弧形的进度圈。
绘制圆弧动画的主要思路是在一段指定的时间内持续不断地绘制一个扇形或圆弧,连起来整个画面就会动起来。
为了区分处理动画的背景和前景,还要分别构造背景视图(用于衬托动画)和前景视图(用于展示圆弧)。
圆弧进度动画的效果
三、自定义对话框
1. 对话框Dialog
大至整个活动页面,小至Toast的提示窗,还有对话框Dialog,都建立在窗口上。
下面是Window的5个常用方法:
setContentView:设置内容视图。
setLayout:设置内容视图的宽、高尺寸。
setGravity:设置内容视图的对齐方式。
setBackgroundDrawable:设置内容视图的背景。
findViewById:根据资源ID获取该视图的对象。
对话框的操作方法
下面来看具体的对话框操作方法。
Dialog构造函数:可定义对话框的主题样式(样式在styles.xml中定义)。
getWindow:获取对话框的窗口对象。
show:显示对话框。
isShowing:判断对话框是否显示。
hide:隐藏对话框。
dismiss:关闭对话框。
setCancelable:设置对话框是否可取消。
setCanceledOnTouchOutside:点击对话框外部区域是否自动关闭对话框。
2. 改进的日期对话框
系统自带的对话框往往只能修改文字,无法调整界面布局,也无法定制按钮样式,甚至连文本的大小和颜色都无法修改。
自定义日期对话框的实现步骤如下:
(1)定义一个对话框布局文件,放置标题文字的TextView控件、选择日期的DatePicker控件、确定按钮的Button控件等。
(2)编写自定义日期对话框的代码,设置对话框的布局、样式、日期、标题,并处理确定按钮的点击事件、日期选择器的变更事件等。
(3)在Acitvity页面中使用自定义的日期对话框。
自定义日期对话框的效果
3. 自定义多级对话框
多级对话框的效果界面
四、自定义通知栏
通知栏的用法和如何自定义通知栏,包括通知推送Notification的设置、进度条ProgressBar的样式定制、远程视图RemoteViews的配置方法,并给出一个自定义通知栏的具体例子,以及通知文本的颜色设定。
1. 通知推送Notification
在手机屏幕的顶端下拉会弹出通知栏,里面存放的是App即时提醒用户的消息,消息内容由Notification产生并推送。
每条消息通知基本都有图标、标题、内容、时间等元素,参数通过Notification.Builder构建。
使用Notification只能生成通知内容,实际推送动作还需借助系统的通知服务实现。
NotificationManager是系统通知服务的管理类,有3个常用方法。
notify:推送指定消息到通知栏。
cancel:取消指定消息。
cancelAll:取消所有消息。
消息推送的通知栏效果
Android8.0新增的通知渠道
Android从8.0开始进一步规范通知栏的合理使用,要求每条通知都要分配通知渠道,否则就无法推送消息。
有了通知渠道之后,用户就能单独开关应用内部某个渠道的通知,从而实现更加精细化的通知栏管理。
引入通知渠道之后的编码步骤如下:
(1)根据渠道编号创建一个通知渠道NotificationChannel的实例,并调用通知管理器对象的createNotificationChannel方法创建该渠道。
(2)给每条通知分配对应的渠道,这样才能正常进行消息推送。
2. 进度条ProgressBar
消息通知Notification的setProgress方法是对内置进度条进行操作,不过很多时候进度条会单独使用。
下面来看进度条的常用属性。
style:指定进度条的形状样式。
max:指定进度条的最大值。
progress:指定进度条当前进度值。
secondaryProgress:指定进度条当前次要进度值。
progressDrawable:指定进度条的进度图形。
自定义进度条的层次图形
自定义进度图形的效果
3. 远程视图RemoteViews
setContent方法可以在设置定制的通知栏视图RemoteViews时取代Builder的默认视图模板。这表示通知栏允许自定 义,并且自定义通知栏需要采用远程视图RemoteViews。
虽然RemoteViews与Activity一样有自己的布局文件,但是RemoteViews的使用权限小了很多。两者的区别主要有: (1)RemoteViews主要用于通知栏部件和桌面部件,而Activity用于页面。
(2)RemoteViews只支持少数几种控件,如TextView、ImageView、Button、ImageButton、ProgressBar、 Chronometer和AnalogClock。
(3)RemoteViews不可直接获取和设置控件信息,只能通过该对象的set方法修改控件信息。
如何使用远程视图
利用远程视图自定义通知栏的效果
模拟音乐播放器的通知栏控制条效果
4. 自定义通知的文本颜色设定
手机厂商一般会定制通知栏的皮肤,有的手机通知栏背景是黑色的,有的手机通知栏背景是白色的。于是自定义通 知的文本颜色也要跟着变化。
(1)对Android4.*系统来说,通知栏的标题色取自系统的“?android:attr/textColorPrimary”;
(2)对于Android5.0及以上的系统,通知标题的文字风格android:textAppearance取自系统 的“@android:style/TextAppearance.Material.Notification.Title”。
五、 服务Service基础
介绍为何使用服务Service和如何使用服务,包括服务的生命周期和在3种启停方式下的生命周期过程,有普通启停、立 即绑定和延迟绑定。另外,还介绍了怎样结合通知推送Notification实现把服务推送到前台的功能。
1 .Service的生命周期
服务Service是Android的四大组件之一,常用在看不见页面的高级场合,如第5章定时器用到了系统的闹钟服务,6.4节 通知推送用到了系统的通知服务。 既然Android有系统服务, App也可以有自己的服务。服务的事务处理不依赖于某个具 体页面,可以悄悄地进行用户看不到的操作。 Service与Activity相比,不同之处在于没有对应的页面,相同之处在于有生 命周期。
Service的生命周期方法
下面是Service与生命周期有关的方法说明。
onCreate:创建服务。
onStart:开始服务,Android2.0以下版本使用,现已废弃。
onStartCommand:开始服务,Android2.0及以上版本使用。
onDestroy:销毁服务。
onBind:绑定服务。
onRebind:重新绑定。该方法只有当上次onUnbind返回true的时候才会被调用。
onUnbind:解除绑定。
Service的启停方式
服务存在3种启停方式,叙述如下:
1. 普通启停
普通启停是最简单的用法。服务运行于主进程。
2. 立即绑定
绑定方式的服务定义有所不同,因为绑定的服务可能运行于另一个进程,所以必须定义一个Binder对象用来进行进程间的通信。
3. 延迟绑定
延迟绑定与立即绑定的区别在于:延迟绑定是在页面上先通过startService方法启动服务,然后通过bindService方法绑定已存在的服务。而立即绑定只需bindService方法就一起完成启动和绑定的操作。
2. 推送服务到前台
服务不只能在外部进行启停或绑定,还能在内部模拟启停。
服务内部的启停方法也有对应的两个函数。
startForeground:把当前服务切换到前台运行。第一个参数表示通知的编号,第二个参数表示Notification对象,意味着切换到前台就是展示到通知栏。
stopForeground:停止前台运行。参数为true表示清除通知,参数为false表示不清除。
注意: stopForeground仅仅是不在通知栏显示,服务其实还在运行当中。要想让服务停止自身,得调用stopSelf方法。
服务在前台运行的应用
服务在前台运行的一个常见的应用是音乐播放器,即使用户离开了播放器页面,手机仍然能在后台继续播放音乐,同时还 能在通知栏查看播放进度。
下面是音乐播放服务展示到通知栏的效果。