Android开发技巧——自定义控件之增加状态
Android开发技巧——自定义控件之增加状态
题外话
这篇本该是上周四或上周五写的,无奈太久没写博客,前几段把我的兴头都用完了,就一拖再拖,直到今天。不想把这篇拖到下个月,所以还是先硬着头皮写了。
《自定义控件》我也没想着要写多长或多短,只是介绍点知识,大部分都是自己积累和摸索的。网上有很多教如何自定义某某控件来做出很酷炫的效果的,但我想写的与这些不同。我平时所积累下来的知识,还是偏向于实用而匮乏于酷炫。而对于各种酷炫的效果,其实也是说不完写不尽的。掌握一定的数学及算法知识,再加上对API的了解,基本上你觉得能实现的你都能够去实现。而我这里,只是想着介绍些相关的基础知识。这些知识,若与只是使用控件来搭建界面相比,它们算是进阶,但对于想要进阶的开发者而言,它们都是基础。
写完这篇之后,我可能会写一两篇自定义控件来优化界面的具体例子,也兑现我在第一篇的最后所说的话,但不会对onDraw
讲得太细。
还有就是Gradle的翻译我也拖了好久了,现在都到2.9版本了。目前我个人采用的翻译方式是使用CAT辅助翻译,生成编译之后的文件。之前是再采用人工合并的方式合完之后提交到Github同时同步到七牛空间。昨天花了点时间,写了段代码来进行译文和原文的自动合并,生成中英对照的HTML,到时就能把从合并内容所节省下来的时间用于翻译了。
正文
场景
View
类是Android中的所有UI组件的基础构建模块,之前翻过某版本的源代码,大概有2万行左右的代码,它主要负责组件的绘制及事件的处理。我们在一些自定义控件的场合,可能需要在一个组件上画些东西,也是通过重写View
的onDraw
方法,通过其参数的Canvas
对象进行绘制。本篇先不谈onDraw
,而是说一下另外一个点——状态。
在我们学习<selector/>
的时候,就知道了关于一个视图组件会有许多种状态,比如按下(pressed),选择(selected),可用(enabled),正常状态,其他状态等等。View也处理了关于一个组件在不同状态下的显示的绘制逻辑,通常继承自View的组件都有着以上所说的这些状态。但是也有一些状态是View没有提供的,而我们可能正需要它们,所以就需要对状态进行扩展,增加我们的状态,比如增加checked
。
这里有一个具体的场景:
这是一个开关按钮,开关状态下背景不同,文字不同,文字旁边的图片也不同。状态我用checked
,文字我定义了两个属性:onText
以及offText
,文字旁边的图片我打算只用一个foreground
属性,但需要写一个selector
来定义正常状态(未锁)和checked
状态(锁定)下的图片。
实现
首先写一个类继承自TextView
,因为我打算用TextView
的setCompoundDrawables
来设定文字旁边的图片。
然后定义属性:
<declare-styleable name="ToggleView">
<attr name="android:foreground"/>
<attr name="pwOnText"/>
<attr name="pwOffText"/>
<attr name="pwColor"/>
<attr name="pwDrawableHeight"/>
</declare-styleable>
读取属性:
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ToggleView);
stateListDrawable = (StateListDrawable) a.getDrawable(R.styleable.ToggleView_android_foreground);
colorStateList = a.getColorStateList(R.styleable.ToggleView_pwColor);
offText = a.getString(R.styleable.ToggleView_pwOffText);
onText = a.getString(R.styleable.ToggleView_pwOnText);
drawableHeight = a.getDimensionPixelSize(R.styleable.ToggleView_pwDrawableHeight, 0);
a.recycle();
这些都是前几篇的内容。
我们通过在类中实现Checkable
接口,可以完成选中的逻辑,但是画出来的状态却没有更新,所以接下来的实现过程就是本篇的主要内容:
首先定义状态集:
private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked};
然后我们要把状态给加进去。我们需要重写protected int[] onCreateDrawableState(int extraSpace)
方法,如下:
@Override
protected int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (isChecked) {
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
}
return drawableState;
}
先调用父类的onCreateDrawableState
方法得到状态数组对象drawableState
,但是参数extraSpace
要加上1,因为我们要往里面增加一个状态。然后判断在代码逻辑中,是否为选中状态,如果是的话,调用mergeDrawableStates(drawableState, CHECKED_STATE_SET)
方法把我们的状态值给加进去,最终返回drawableState
。
但是我们虽然实现了Checkable
接口,在设置状态时却没有触发到这个状态。所以我们需要自己去触发这个状态。
@Override
public void setChecked(boolean checked) {
if (isChecked != checked) {
isChecked = checked;
refreshDrawableState();
}
}
在状态改变时,调用refreshDrawableState()
刷新状态。
最后,我们要重写drawableStateChanged()
方法,获取到当前状态的drawable,然后绘制出来。
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
if (stateListDrawable != null) {
int[] myDrawableState = getDrawableState();
stateListDrawable.setState(myDrawableState);
Drawable drawable = stateListDrawable.getCurrent();
if(drawableHeight != 0) {
drawable.setBounds(0, 0, drawable.getIntrinsicWidth() * drawableHeight / drawable.getIntrinsicHeight(), drawableHeight);
} else {
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
}
if (isChecked) {
setCompoundDrawables(drawable, null, null, null);
} else {
setCompoundDrawables(null, null, drawable, null);
}
}
setText(isChecked ? onText : offText);
}
这部分的逻辑其实没有我代码所写的那么复杂,代码中我需要设定drawable的大小,以及在不同的状态下设置drawable的位置,实际上逻辑只需要如下:
- 获取当前的drawableState状态
- 对stateListDrawable(带状态的drawable集)设置状态。
- 获取stateListDrawable的当前状态的drawable
- 进行你所想要的绘制。
这样就完成了。
总结
从上面可知,增加状态的过程如下:
- 定义状态数组
- 重写
protected int[] onCreateDrawableState(int extraSpace)
- 调用
refreshDrawableState()
- 重写
protected void drawableStateChanged()
本篇的内容不多。另外,关于onCreateDrawableState
方法的调用,我理解并不深入,如果描述有误,敬请指正。
本文原创,转载请注明CSDN博客上的出处: http://blog.csdn.net/maosidiaoxian/article/details/50112191
Android开发技巧——自定义控件之增加状态的更多相关文章
- Android开发技巧——自定义控件之使用style
Android开发技巧--自定义控件之使用style 回顾 在上一篇<Android开发技巧--自定义控件之自定义属性>中,我讲到了如何定义属性以及在自定义控件中获取这些属性的值,也提到了 ...
- Android开发技巧——自定义控件之自定义属性
Android开发技巧--自定义控件之自定义属性 掌握自定义控件是很重要的,因为通过自定义控件,能够:解决UI问题,优化布局性能,简化布局代码. 上一篇讲了如何通过xml把几个控件组织起来,并继承某个 ...
- Android开发技巧——自定义控件之组合控件
Android开发技巧--自定义控件之组合控件 我准备在接下来一段时间,写一系列有关Android自定义控件的博客,包括如何进行各种自定义,并分享一下我所知道的其中的技巧,注意点等. 还是那句老话,尽 ...
- Android开发技巧——写一个StepView
在我们的应用开发中,有些业务流程会涉及到多个步骤,或者是多个状态的转化,因此,会需要有相关的设计来展示该业务流程.比如<停车王>应用里的添加车牌的步骤. 通常,我们会把这类控件称为&quo ...
- Android开发技巧——实现可复用的ActionSheet菜单
在上一篇<Android开发技巧--使用Dialog实现仿QQ的ActionSheet菜单>中,讲了这种菜单的实现过程,接下来将把它改成一个可复用的控件库. 本文原创,转载请注明出处: h ...
- Android开发技巧——高亮的用户操作指南
Android开发技巧--高亮的用户操作指南 2015-12-15补记: 发现使用PopupWindow进行遮罩层的显示,在华为P7上会有问题.具体表现为:画出来的高亮部分会偏下.原因为:通过view ...
- 50个android开发技巧
50个android开发技巧 http://blog.csdn.net/column/details/androidhacks.html
- Android开发技巧——大图裁剪
本篇内容是接上篇<Android开发技巧--定制仿微信图片裁剪控件> 的,先简单介绍对上篇所封装的裁剪控件的使用,再详细说明如何使用它进行大图裁剪,包括对旋转图片的裁剪. 裁剪控件的简单使 ...
- Android开发技巧——使用PopupWindow实现弹出菜单
在本文当中,我将会与大家分享一个封装了PopupWindow实现弹出菜单的类,并说明它的实现与使用. 因对界面的需求,android原生的弹出菜单已不能满足我们的需求,自定义菜单成了我们的唯一选择,在 ...
随机推荐
- makefile的命令包定义及使用
下面以\build\core\product.mk下面的内容为例介绍: define _find-android-products-files $(shell test -d device & ...
- Eclipse开发C/C++ 安装配置
1. jdk环境配置 2. eclipse 下载 3. MinGW 下载安装 4. 编写Hello Word jdk环境配置 环境配置我就不多说了,网上 ...
- 开源项目——小Q聊天机器人V1.4
小Q聊天机器人V1.0 http://blog.csdn.net/baiyuliang2013/article/details/51386281 小Q聊天机器人V1.1 http://blog.csd ...
- Android:Field can be converted to a local varible.
背景 使用 Android Studio 开发 Android 有一段时间了,偶尔会碰到 AS 在一些私有变量上有黄色高亮提示Field can be converted to a local var ...
- Java进阶(三十五)java int与integer的区别
Java进阶(三十五)java int与Integer的区别 前言 int与Integer的区别从大的方面来说就是基本数据类型与其包装类的区别: int 是基本类型,直接存数值,而Integer是对象 ...
- Android读取网络图片到本地的简约的实现
今天在网上看到了一个关于读取网络文件的小视频,觉得不错,拿来与大家分享 思路 具体的思路比较的简单,但是思想非常的单纯.那就是输入一个网址,点击按钮,将从网络上获取的一张图片显示到一个ImageVie ...
- 怎么避免在类实现的cpp文件中不要多次进行类声明
方法1: 类的头文件中写 #pragma once class MyTeacher { public: MyTeacher(); ~MyTeacher(); }; 方法2: 类的头文件中写 #ifnd ...
- Spark-SQL连接MySql关系型数据库
本文主要分析Spark SQL官方文档中有关于JDBC To Other Databases部分,以MySQL数据库为例,结合数据读写操作的实例代码进行详细的分析.本文中的代码需要使用到Mysql J ...
- 尚学堂马士兵struts2 课堂笔记(四)
27 结果类型 主要就四种种 dispatch和rediret chain和drdirectaction <package name="resultTypes" namesp ...
- 【翻译】Ext JS 5:为不同设备设置不同的主题
原文:Sencha Ext JS 5: Supporting Different Themes for Different Devices 步骤1创建一个应用程序 步骤2定义主题 步骤3编辑Appjs ...