您的位置:首页 > 移动开发 > Android开发

Android开发技巧——自定义控件之增加状态

2015-11-30 14:39 393 查看

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: