在android开发中我们常常需要对控件进行相关操作,虽然网上已有很多对控件酷炫的操作,但小编今天给大家分享的纯属实用出发。在查看了一些列安卓教程和文档后,发现了一位大牛分享的非常不错的有关android自定义控件增加状态的文章,分享给大家,学习、参考。

场景

View类是Android UI组件的基础构建模块,主要负责组件的绘制及事件的处理。我们在一些自定义控件的场合,可能需要在一个组件上画些东西,也是通过重写View的onDraw方法,通过其参数的Canvas对象进行绘制。

我们学习<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()

以上就是android自定义控件增加状态的方法,如果你还有更好的实现方法,欢迎补充分享。

相关文章:《如何实现自己的Android MVP框架?

Android中如何为自定义控件增加状态?的更多相关文章

  1. android中正确保存view的状态

    英文原文: http://trickyandroid.com/saving-android-view-state-correctly/ 转载此译文须注明出处. 今天我们聊一聊安卓中保存和恢复view状 ...

  2. Android中activity保存数据和状态在哪个方法实现

    以前只知道在Activity销毁之前,要把数据保存在 onSaveInstanceState(Bundle)方法中,后来学习了别人的微博,学到了很多细节问题,所以整理了一下,希望能帮到大家. 如果看官 ...

  3. Android中StateListDrawable的种类(状态的种类)

    <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="ht ...

  4. android 中如何获取camera当前状态

    /** * 测试当前摄像头能否被使用 * * @return */ public static boolean isCameraCanUse() { boolean canUse = true; Ca ...

  5. android中广播接收SD卡状态

    mReceiver = new BroadcastReceiver() { //add by mengmeng.chen begin public void onReceive(Context con ...

  6. Android中View绘制流程以及invalidate()等相关方法分析

    [原文]http://blog.csdn.net/qinjuning 整个View树的绘图流程是在ViewRoot.java类的performTraversals()函数展开的,该函数做的执行过程可简 ...

  7. Android中View绘制流程以及invalidate()等相关方法分析(转载的文章,出处在正文已表明)

    转载请注明出处:http://blog.csdn.net/qinjuning 前言: 本文是我读<Android内核剖析>第13章----View工作原理总结而成的,在此膜拜下作者 .同时 ...

  8. Android中View绘制流程以及invalidate()等相关方法分析(转)

    转自:http://blog.csdn.net/qinjuning 前言: 本文是我读<Android内核剖析>第13章----View工作原理总结而成的,在此膜拜下作者 .同时真挚地向渴 ...

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

    Android开发技巧--自定义控件之增加状态 题外话 这篇本该是上周四或上周五写的,无奈太久没写博客,前几段把我的兴头都用完了,就一拖再拖,直到今天.不想把这篇拖到下个月,所以还是先硬着头皮写了. ...

随机推荐

  1. block简介

    ios4.0系统已开始支持block,在编程过程中,blocks被Obj-C看成是对象,它封装了一段代码,这段代码可以在任何时候执行.Blocks可以作为函数参数或者函数的返回值,而其本身又可以带输入 ...

  2. tomcat8080端口占用解决办法

    打开控制台,在窗口中输入指令:netstat -ano | findstr 8080       指令的意思是找出占用8080端口的进程pid 上图中表示占用进程pid为23288,然后再次输入指令: ...

  3. Java web--过滤器

    本文引自:https://www.cnblogs.com/dudududu/p/8505177.html 参考博客:http://www.cnblogs.com/coderland/p/5902878 ...

  4. JAVA / MySql 编程——第六章 Mysql 创建账户的相关命令

    1.        创建普通用户: 语法: CREATE USER `user`@`host` [IDENTIFIED 'password'];   //user:用户名,host:主机名,passw ...

  5. 微信公众号支付java版本

    回调函数 @RequestMapping("/toPay") public String toPay(HttpServletRequest request, HttpServlet ...

  6. MySQL字段属性介绍

    引言 这次Qi号分享MySQL字段属性简介.下面资料是Qi号搜集大量资料与个人理解的整理. MySQL提供了一组可以赋给表中各个列的数据类型,每个类型都强制数据满足为该数据类型预先确定的一组规则,例如 ...

  7. Laravel系列 Web

    一.Homestead准备 上一篇文章已经对它的配置进行了说明,下面对Homestead.yaml进行说明 --- ip: "192.168.10.10" memory: 2048 ...

  8. SQL tp3.2 批量更新 saveAll

    /** * 批量更新数据 * @param [array] $datas [更新数据] * @param [string] $table_name [表名] */ public function sa ...

  9. PHP 防止 E-mail 注入( PHP 过滤器)

    PHP 防止 E-mail 注入 防止 e-mail 注入的最好方法是对输入进行验证. 下面的代码与上一节类似,不过我们已经增加了检测表单中 email 字段的输入验证程序: <html> ...

  10. 线程、进程、协程和GIL(三)

    上一篇文章介绍了:创建线程的两种方式.Event对象判断线程是否启动.利用信号量控制线程并发. 博客链接:线程.进程.协程和GIL(二) 这一篇来说说线程间通信的那些事儿: 一个线程向另一个线程发送数 ...