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原生的弹出菜单已不能满足我们的需求,自定义菜单成了我们的唯一选择,在 ...
随机推荐
- Compass 更智能的搜索引擎(1)--入门
学完了前面的Lucene系列教程: 全文检索 Lucene(1)–入门 全文检索 Lucene(2)–进阶 全文检索 Lucene(3)–分页 全文检索 Lucene(4)–高亮 Lucene确实是个 ...
- JQuery 初探
放暑假了,终于有时间能学点前端的东西了.JQuery就是我第一个选择,锋利的JQuery.这本书真的很好.下面以一个ToggleButton形式的小例子开场吧. 引入JQuery库 在网页上引用JQu ...
- 21 FragmentTabHost +Fragment代码案例
注意头导航标签过多会被压缩并 结构 MainActivity.java package com.qf.day21_fragmenttabhost_demo1; import com.qf.day21_ ...
- 插件开发之360 DroidPlugin源码分析(五)Service预注册占坑
请尊重分享成果,转载请注明出处: http://blog.csdn.net/hejjunlin/article/details/52264977 在了解系统的activity,service,broa ...
- JRE System Library [JavaSE-1.7](unbound)
window > preferences > java > Install jars >如果没有jdk1.7 ,点击下面的search,会自动找到已经安装对jdk1.7,选择, ...
- oracle中动态SQL详解
部分内容参考网上资料 1.静态SQLSQL与动态SQL Oracle编译PL/SQL程序块分为两个种:其一为前期联编(early binding),即SQL语句在程序编译期间就已经确定,大多数的编译情 ...
- GDAL书籍中删除数据勘误(C#语言)
GDAL书籍中关于C#版本删除数据的时候,不能完全删除数据,由于我对C#不了解导致代码有点问题,非常感谢@Bingoyin指出并给出修改方案.此外对于栅格图像的删除.重命名,矢量数据的删除和重命名都有 ...
- iOS动画进阶 - 教你写 Slack 的 Loading 动画
(转载自:http://blog.csdn.net/wang631106979/article/details/52473985) 如果移动端访问不佳,可以访问我的个人博客 前几天看了一篇关于动画的博 ...
- [C++学习历程]基础部分 C++中的类型和声明
前面搭起了C++的VS环境,可以在VS中编写C++代码了,也运行了最简单的一个程序Helloworld.那么我们该怎么才能写出功能强大的程序,怎样才能随心所欲的应用呢,那就需要重新回头来,从C++基础 ...
- java开源项目之IQQ学习记录之单例模式与log4j日志记录
作者:sushengmiyan 本文地址:http://blog.csdn.net/sushengmiyan/article/details/18992741 打开IQQ项目,打开包iqq.app中的 ...