在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. 《阿里巴巴Java开发手册》阅读笔记

    1.抽象类命名使用 Abstract 或 Base 开头: 异常类命名使用 Exception 结尾: 测试类命名以它要测试的类的名称开始,以 Test 结尾. 2.POJO 类中布尔类型的变量,都不 ...

  2. LOJ#515. 「LibreOJ β Round #2」贪心只能过样例(bitset)

    内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: nzhtl1477 提交提交记录统计讨论测试数据   题目描述 一共有 nnn个数,第 iii ...

  3. Win10上Anaconda环境下python3.6安装和使用pyinstaller

    一.安装步骤 1. 电脑是win10,安装的Python3.6 2. 在Scripts文件夹下执行pip install pyinstaller, 安装成功后下载pyinstaller安装包,解压之后 ...

  4. LeetCode962. 最大宽度坡

    问题:最大宽度坡 给定一个整数数组 A,坡是元组 (i, j),其中  i < j 且 A[i] <= A[j].这样的坡的宽度为 j - i. 找出 A 中的坡的最大宽度,如果不存在,返 ...

  5. iPhone 横屏时默认会放大文字的问题

    有人说用 html { text-size-adjust: 100%; }我发现这个并不能解决问题.下面代码可以完美解决. 添加标签:<meta name="viewport" ...

  6. 中通快递股份有限公司.net高级面试题

    中通快递分布式技术开发   gc垃圾回收原理 .net中,托管代码的内存管理是自动的,由GC进行管理,而对于非托管代码,则需要.net手动处理 CLR运行时,内存分为:托管堆和栈,其中栈用于存储值类型 ...

  7. const用法总结(通俗易懂)

    const的意思可以概括为 “一个不能被改变的普通变量” ,使得const在一定程度上提高程序的安全性和可靠性. const的几种情况: 1. const的普通用法 int const size: c ...

  8. C# 禁止在textBox输入框输入非法字符

    首先添加下面代码 //禁止在textBox输入框输入非法字符 private void keypressed(Object o, KeyPressEventArgs e) { if (e.KeyCha ...

  9. strchr函数的用法

    原型: char *strchr(const char *s,char c); #include<string.h> 查找字符串s中首次出现字符c的位置,返回首次出现c的位置的指针,如果s ...

  10. 7-1 寻找大富翁 PTA 堆排序

    7-1 寻找大富翁 (25 分) 胡润研究院的调查显示,截至2017年底,中国个人资产超过1亿元的高净值人群达15万人.假设给出N个人的个人资产值,请快速找出资产排前M位的大富翁. 输入格式: 输入首 ...