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

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

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

题外话

这篇本该是上周四或上周五写的,无奈太久没写博客,前几段把我的兴头都用完了,就一拖再拖,直到今天。不想把这篇拖到下个月,所以还是先硬着头皮写了。

《自定义控件》我也没想着要写多长或多短,只是介绍点知识,大部分都是自己积累和摸索的。网上有很多教如何自定义某某控件来做出很酷炫的效果的,但我想写的与这些不同。我平时所积累下来的知识,还是偏向于实用而匮乏于酷炫。而对于各种酷炫的效果,其实也是说不完写不尽的。掌握一定的数学及算法知识,再加上对API的了解,基本上你觉得能实现的你都能够去实现。而我这里,只是想着介绍些相关的基础知识。这些知识,若与只是使用控件来搭建界面相比,它们算是进阶,但对于想要进阶的开发者而言,它们都是基础。
写完这篇之后,我可能会写一两篇自定义控件来优化界面的具体例子,也兑现我在第一篇的最后所说的话,但不会对onDraw讲得太细。

还有就是Gradle的翻译我也拖了好久了,现在都到2.9版本了。目前我个人采用的翻译方式是使用CAT辅助翻译,生成编译之后的文件。之前是再采用人工合并的方式合完之后提交到Github同时同步到七牛空间。昨天花了点时间,写了段代码来进行译文和原文的自动合并,生成中英对照的HTML,到时就能把从合并内容所节省下来的时间用于翻译了。

正文

场景

View类是Android中的所有UI组件的基础构建模块,之前翻过某版本的源代码,大概有2万行左右的代码,它主要负责组件的绘制及事件的处理。我们在一些自定义控件的场合,可能需要在一个组件上画些东西,也是通过重写ViewonDraw方法,通过其参数的Canvas对象进行绘制。本篇先不谈onDraw,而是说一下另外一个点——状态。
在我们学习<selector/>的时候,就知道了关于一个视图组件会有许多种状态,比如按下(pressed),选择(selected),可用(enabled),正常状态,其他状态等等。View也处理了关于一个组件在不同状态下的显示的绘制逻辑,通常继承自View的组件都有着以上所说的这些状态。但是也有一些状态是View没有提供的,而我们可能正需要它们,所以就需要对状态进行扩展,增加我们的状态,比如增加checked
这里有一个具体的场景:
这里写图片描述
这是一个开关按钮,开关状态下背景不同,文字不同,文字旁边的图片也不同。状态我用checked,文字我定义了两个属性:onText以及offText,文字旁边的图片我打算只用一个foreground属性,但需要写一个selector来定义正常状态(未锁)和checked状态(锁定)下的图片。

实现

首先写一个类继承自TextView,因为我打算用TextViewsetCompoundDrawables来设定文字旁边的图片。
然后定义属性:

    <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的位置,实际上逻辑只需要如下:

  1. 获取当前的drawableState状态
  2. 对stateListDrawable(带状态的drawable集)设置状态。
  3. 获取stateListDrawable的当前状态的drawable
  4. 进行你所想要的绘制。

这样就完成了。

总结

从上面可知,增加状态的过程如下:

  • 定义状态数组
  • 重写protected int[] onCreateDrawableState(int extraSpace)
  • 调用refreshDrawableState()
  • 重写protected void drawableStateChanged()

本篇的内容不多。另外,关于onCreateDrawableState方法的调用,我理解并不深入,如果描述有误,敬请指正。

本文原创,转载请注明CSDN博客上的出处: http://blog.csdn.net/maosidiaoxian/article/details/50112191

目录
相关文章
|
2月前
|
安全 数据库 Android开发
在Android开发中实现两个Intent跳转及数据交换的方法
总结上述内容,在Android开发中,Intent不仅是活动跳转的桥梁,也是两个活动之间进行数据交换的媒介。运用Intent传递数据时需注意数据类型、传输大小限制以及安全性问题的处理,以确保应用的健壯性和安全性。
127 11
|
6月前
|
JavaScript Linux 网络安全
Termux安卓终端美化与开发实战:从下载到插件优化,小白也能玩转Linux
Termux是一款安卓平台上的开源终端模拟器,支持apt包管理、SSH连接及Python/Node.js/C++开发环境搭建,被誉为“手机上的Linux系统”。其特点包括零ROOT权限、跨平台开发和强大扩展性。本文详细介绍其安装准备、基础与高级环境配置、必备插件推荐、常见问题解决方法以及延伸学习资源,帮助用户充分利用Termux进行开发与学习。适用于Android 7+设备,原创内容转载请注明来源。
1163 77
|
3月前
|
安全 Java Android开发
为什么大厂要求安卓开发者掌握Kotlin和Jetpack?深度解析现代Android开发生态优雅草卓伊凡
为什么大厂要求安卓开发者掌握Kotlin和Jetpack?深度解析现代Android开发生态优雅草卓伊凡
160 0
为什么大厂要求安卓开发者掌握Kotlin和Jetpack?深度解析现代Android开发生态优雅草卓伊凡
|
7月前
|
前端开发 Java Shell
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
403 20
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
7月前
|
JavaScript 搜索推荐 Android开发
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
194 8
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
|
7月前
|
Dart 前端开发 Android开发
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
162 4
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
8月前
|
缓存 前端开发 Android开发
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
358 12
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
|
7月前
|
安全 Android开发 iOS开发
escrcpy:【技术党必看】Android开发,Escrcpy 让你无线投屏新体验!图形界面掌控 Android,30-120fps 超流畅!🔥
escrcpy 是一款基于 Scrcpy 的开源项目,使用 Electron 构建,提供图形化界面来显示和控制 Android 设备。它支持 USB 和 Wi-Fi 连接,帧率可达 30-120fps,延迟低至 35-70ms,启动迅速且画质清晰。escrcpy 拥有丰富的功能,包括自动化任务、多设备管理、反向网络共享、批量操作等,无需注册账号或广告干扰。适用于游戏直播、办公协作和教育演示等多种场景,是一款轻量级、高性能的 Android 控制工具。
482 1
|
8月前
|
Dart 前端开发 Android开发
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
184 1
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
|
9月前
|
搜索推荐 前端开发 API
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
149 19