当前位置: 首页 > news >正文

Android原生实现控件选择背景变色方案(API28及以上)

Android控件点击/选择后控件背景变色的实现方式有很多种,例如使用selector的xml文件实现。这里介绍一下另一种Android原生的点击/选择实现方案(API28及以上),也就是ColorStateListDrawable

ColorStateListDrawable是一个可根据不同状态显示不同颜色的Drawable。

实现效果,选择前/选择后:
在这里插入图片描述
这里我们利用继承LinearLayoutCompat的方式来实现:

属性

创建自定义属性:

    <attr name="carbon_chipStyle" format="reference" /><declare-styleable name="Chip"><attr name="android:text"/><attr name="android:background" /><attr name="pressed_color" format="color"/><attr name="checked_color" format="color"/><attr name="un_enable_color" format="color"/><attr name="carbon_icon" /><attr name="carbon_removable" format="boolean" /><attr name="android:checked" /></declare-styleable>

布局

创建布局文件

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="wrap_content"android:layout_height="match_parent"android:orientation="horizontal"><FrameLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_vertical"><FrameLayoutandroid:id="@+id/carbon_chipContent"android:layout_width="@dimen/carbon_iconSize"android:layout_height="@dimen/carbon_iconSize"android:layout_margin="@dimen/carbon_chipCloseMargin"tools:visibility="gone" /><ImageViewandroid:id="@+id/carbon_chipCheck"android:layout_width="@dimen/carbon_iconSize"android:layout_height="@dimen/carbon_iconSize"android:layout_margin="@dimen/carbon_chipCloseMargin"android:visibility="gone"android:src="@drawable/carbon_check"tools:visibility="visible" /></FrameLayout><TextViewandroid:id="@+id/carbon_chipText"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:gravity="center_vertical"android:layout_marginHorizontal="@dimen/carbon_chipPadding"tools:text="text" /><ImageViewandroid:id="@+id/carbon_chipClose"android:layout_width="@dimen/carbon_iconSize"android:layout_height="@dimen/carbon_iconSize"android:layout_gravity="center_vertical"android:layout_margin="@dimen/carbon_chipCloseMargin"android:scaleType="center"android:src="@drawable/carbon_remove" /></merge>
public class Chip extends LinearLayoutCompat implements Checkable {/*** Interface definition for a callback to be invoked when the checked state of a chip* changed.*/public interface OnCheckedChangeListener {/*** Called when the checked state of a chip has changed.** @param chip      The chip whose state has changed.* @param isChecked The new checked state of buttonView.*/void onCheckedChanged(Chip chip, boolean isChecked);}private FrameLayout content;private ImageView check;private TextView title;private ImageView close;private OnRemoveListener onRemoveListener;private boolean checkedState = false;private OnCheckedChangeListener onCheckedChangeListener;public interface OnRemoveListener {void onDismiss();}public Chip(Context context) {super(context, null, R.attr.carbon_chipStyle);initChip(null, R.attr.carbon_chipStyle, R.style.carbon_Chip);}public Chip(Context context, CharSequence text) {super(context, null, R.attr.carbon_chipStyle);initChip(null, R.attr.carbon_chipStyle, R.style.carbon_Chip);setText(text);}public Chip(Context context, AttributeSet attrs) {super(context, attrs, R.attr.carbon_chipStyle);initChip(attrs, R.attr.carbon_chipStyle, R.style.carbon_Chip);}public Chip(Context context, AttributeSet attrs, @AttrRes int defStyleAttr) {super(context, attrs, defStyleAttr);initChip(attrs, defStyleAttr, R.style.carbon_Chip);}private static int[] colorStateIds = new int[]{R.styleable.Chip_android_background,R.styleable.Chip_pressed_color,R.styleable.Chip_checked_color,R.styleable.Chip_un_enable_color};private void initChip(AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {inflate(getContext(), R.layout.carbon_chip, this);title = findViewById(R.id.carbon_chipText);content = findViewById(R.id.carbon_chipContent);check = findViewById(R.id.carbon_chipCheck);close = findViewById(R.id.carbon_chipClose);close.setOnClickListener(v -> {if (onRemoveListener != null)onRemoveListener.onDismiss();});TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.Chip, defStyleAttr, defStyleRes);// 初始化背景Carbon.initDefaultBackground(this, a, colorStateIds);// 初始化相关自定义属性setText(a.getString(R.styleable.Chip_android_text));setIcon(Carbon.getDrawable(this, a, R.styleable.Chip_carbon_icon, 0));setRemovable(a.getBoolean(R.styleable.Chip_carbon_removable, false));a.recycle();}@Deprecatedpublic void setText(String text) {setText((CharSequence) text);}public void setText(CharSequence text) {if (text != null) {title.setText(text);title.setVisibility(View.VISIBLE);} else {title.setVisibility(View.GONE);}}public void setText(int resId) {setText(getResources().getString(resId));}public String getText() {return (String) title.getText();}public View getTitleView() {return title;}public void setIcon(int iconRes) {content.removeAllViews();if (iconRes == 0) {content.setVisibility(GONE);return;}content.setVisibility(VISIBLE);ImageView icon = new ImageView(getContext());content.addView(icon);icon.setImageResource(iconRes);}public void setIcon(Drawable drawable) {content.removeAllViews();if (drawable == null) {content.setVisibility(GONE);return;}content.setVisibility(VISIBLE);ImageView icon = new ImageView(getContext());content.addView(icon);icon.setImageDrawable(drawable);}public void setIcon(Bitmap bitmap) {content.removeAllViews();if (bitmap == null) {content.setVisibility(GONE);return;}content.setVisibility(VISIBLE);ImageView icon = new ImageView(getContext());content.addView(icon);icon.setImageBitmap(bitmap);}@Deprecatedpublic Drawable getIcon() {if (content.getChildCount() > 0 && content.getChildAt(0) instanceof ImageView)return ((ImageView) content.getChildAt(0)).getDrawable();return null;}@Deprecatedpublic View getIconView() {if (content.getChildCount() > 0 && content.getChildAt(0) instanceof ImageView)return content.getChildAt(0);return null;}public View getContentView() {if (content.getChildCount() > 0)return content.getChildAt(0);return null;}public void setContentView(View view) {content.removeAllViews();if (view != null) {content.setVisibility(VISIBLE);content.addView(view);} else {content.setVisibility(GONE);}}public void setRemovable(boolean removable) {close.setVisibility(removable ? VISIBLE : GONE);}public boolean isRemovable() {return close.getVisibility() == VISIBLE;}public void setOnRemoveListener(OnRemoveListener onRemoveListener) {this.onRemoveListener = onRemoveListener;}}

重点在于为控件手动设置一个ColorListDrawable充当背景图片:

// 为控件设置一个背景图片public static void initDefaultBackground(View view, TypedArray a, int[] ids) {Drawable d = getDefaultColorDrawable(view, a, ids);if (d != null)view.setBackgroundDrawable(d);}// 根据我们提供的android:background,pressed_color,checked_color,un_enable_color的值生成一个ColorStateListDrawablepublic static Drawable getDefaultColorDrawable(View view, TypedArray a, int[] ids) {ColorStateList color = getDefaultColorStateList(view, a, ids);if (color != null) {Drawable d = null;if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {d = new ColorStateListDrawable(color);}return d;}return null;}public static ColorStateList getDefaultColorStateList(View view, TypedArray a, int[] ids) {Context context = view.getContext();int chip_bg = ids[0];int chip_pressed_bg = ids[1];int chip_checked_bg = ids[2];int chip_un_enable_bg = ids[3];if (!a.hasValue(chip_bg))return null;int backgroundColor = a.getColor(chip_bg, 0);int pressedBgColor = a.getColor(chip_pressed_bg,ContextCompat.getColor(context, R.color.carbon_colorControlPressed));int checkedBgColor = a.getColor(chip_checked_bg,ContextCompat.getColor(context,R.color.carbon_colorControlActivated));int unEnableBgColor = a.getColor(chip_un_enable_bg,ContextCompat.getColor(context,R.color.carbon_colorControlDisabled));return ColorStateListFactory.getInstance().make(context,backgroundColor,pressedBgColor,checkedBgColor,unEnableBgColor,getThemeColor(context,com.google.android.material.R.attr.colorError));}

ColorStateListFactory

状态和颜色一一对应

public ColorStateList make(Context context,int defaultColor,int pressed,int activated,int disabled,int invalid){return new ColorStateList(new int[][]{new int[]{-android.R.attr.state_enabled}, // unenablenew int[]{android.R.attr.state_pressed}, // pressednew int[]{android.R.attr.state_checked}, //checkednew int[]{android.R.attr.state_activated},//activatednew int[]{android.R.attr.state_selected},//selectednew int[]{android.R.attr.state_focused},//focusednew int[]{}},new int[]{disabled,pressed,activated,activated,activated,activated,defaultColor});}

这样,我们就实现了按下控件,控件的背景颜色就会改变。

但是,LinearCompact本身是没有check状态的,因此这就需要我们为它添加check状态。

Checkable接口

Chip实现Checkable接口:

public class Chip extends LinearLayoutCompat implements Checkable {// 定义状态集private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked};public interface OnCheckedChangeListener {/*** Called when the checked state of a chip has changed.** @param chip      The chip whose state has changed.* @param isChecked The new checked state of buttonView.*/void onCheckedChanged(Chip chip, boolean isChecked);}...public void toggle() {setChecked(!isChecked());}@Overridepublic boolean performClick() {toggle();if (onCheckedChangeListener != null)onCheckedChangeListener.onCheckedChanged(this, isChecked());final boolean handled = super.performClick();if (!handled) {// View only makes a sound effect if the onClickListener was// called, so we'll need to make one here instead.playSoundEffect(SoundEffectConstants.CLICK);}return handled;}@ViewDebug.ExportedPropertypublic boolean isChecked() {return checkedState;}/*** <p>Changes the checked state of this chip.</p>* 第二步* 在设置状态时却没有触发到这个状态。所以我们需要自己去触发这个check状态。* @param checked true to check the chip, false to uncheck it*/public void setChecked(boolean checked) {if (this.checkedState != checked) {checkedState = checked;check.setVisibility(checked ? VISIBLE : GONE);// 在状态改变时,调用refreshDrawableState()刷新状态。refreshDrawableState();}}// 第一步,我们要把状态给加进去。我们需要重写protected int[] onCreateDrawableState(int extraSpace)方法;/*** 先调用父类的onCreateDrawableState方法得到状态数组对象drawableState,但是参数extraSpace要加上1,因为我们要往里面增加一个状态。* 然后判断在代码逻辑中,是否为选中状态,如果是的话,调用mergeDrawableStates(drawableState, CHECKED_STATE_SET)方法把我们的状态值给加进去,* 最终返回drawableState。* @param extraSpace if non-zero, this is the number of extra entries you* would like in the returned array in which you can place your own* states.** @return*/@Overrideprotected int[] onCreateDrawableState(int extraSpace) {final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);if (isChecked()) {mergeDrawableStates(drawableState, CHECKED_STATE_SET);}return drawableState;}/*** Register a callback to be invoked when the checked state of this chip changes.** @param listener the callback to call on checked state change*/public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {onCheckedChangeListener = listener;}}

怎么使用

<com.chinatsp.shapebutton.chip.Chipandroid:id="@+id/chip"android:layout_width="100dp"android:layout_height="@dimen/carbon_chipHeight"android:layout_margin="16dp"android:text="HELLO"android:background="@color/carbon_defaultColorControl"android:clickable="true"android:checked="false"app:checked_color="@color/carbon_red_700"app:un_enable_color="@color/carbon_grey_700"/>

相关文章:

Android原生实现控件选择背景变色方案(API28及以上)

Android控件点击/选择后控件背景变色的实现方式有很多种&#xff0c;例如使用selector的xml文件实现。这里介绍一下另一种Android原生的点击/选择实现方案&#xff08;API28及以上&#xff09;&#xff0c;也就是ColorStateListDrawable。 ColorStateListDrawable是一个可根据不…...

为什么要学C语言及C语言存在的意义

为什么要学C语言及C语言存在的意义 汇编生C&#xff0c;C生万物。linus说自己最喜欢的语言就是C语言&#xff0c;因为看到写出的代码就能想到对应的汇编码。一方面说明C语言足够简洁&#xff0c;没有像C中一样的复杂概念封装&#xff0c;另一方面也说明C语言足够的底层&#xf…...

数据结构——空间复杂度

空间复杂度&#xff0c;与算法运行时所需的内存空间有关。 默认问题规模为n。 举例案例&#xff0c;具体分析。 1.全是普通变量 2.一维数组 3.二维数组 4.递归--变量 不递归的时候空间复杂度是O(1)&#xff0c;递归的话递归n次&#xff0c;乘以n&#xff0c;所以空间复杂度…...

uniapp:swiper-demo效果

单元格轮播 <swiper class"swiper1" :circular"true" :autoplay"true" interval"3000" previous-margin"195rpx" next-margin"195rpx"><swiper-item v-for"(item,index) in 5" :key"inde…...

Graphviz 作图工具

选择 Graphviz 作为作图工具&#xff0c;主要是想通过代码创建图标&#xff0c;按照 Graphviz 的代码规范就可以生成 svg 的图片。当然&#xff0c;这样的工具也有很多&#xff0c;有些 markdown 编辑器也做了集成&#xff0c;比如&#xff1a; flowchart.jsMermaid 了解 Gra…...

vue、vuex状态管理、vuex的核心概念state状态

每一个 Vuex 应用的核心就是 store&#xff08;仓库&#xff09;。“store”基本上就是一个容器&#xff0c;它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同&#xff1a; Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候&…...

【QT】Qt Application Manager启动应用源码分析

Qt Application Manager启动应用源码分析 Qt Application Manager&#xff08;以下简称QTAM&#xff09;是QT推出的一款应用管理程序&#xff0c;可以把它简单理解成Android的LauncherSystemUI。但是&#xff0c;QTAM又集成了Wayland功能&#xff0c;并且自身实现了一套Compos…...

MyBatisPlus(十)判空查询

说明 判空查询&#xff0c;对应SQL语句中的 IS NULL语句&#xff0c;查询对应字段为 NULL 的数据。 isNull /*** 查询用户列表&#xff0c; 查询条件&#xff1a;电子邮箱为 null 。*/Testvoid isNull() {LambdaQueryWrapper<User> wrapper new LambdaQueryWrapper<…...

AIGC(生成式AI)试用 8 -- 曾经的难题

长假&#xff0c;远离电脑、远离手机、远离社交。 阴雨连绵&#xff0c;望着窗外发呆&#xff0c;AIGC为何物&#xff1f;有什么问题要问AIGC&#xff1f;AIGC可以代替我来发呆&#xff0c;还是可是为我空出时间发呆&#xff1f; 如果可以替代我发呆&#xff0c;要我何…...

文化主题公园旅游景点3d全景VR交互体验加深了他们对历史文化的认知和印象

如今&#xff0c;沉浸式体验被广泛应用于文旅行业&#xff0c;尤其是在旅游演艺活动中。在许多城市&#xff0c;沉浸式旅游演艺活动已成为游客“必打卡”项目之一。因其独特体验和强互动性&#xff0c;这类演艺活动不仅吸引了外地游客&#xff0c;也吸引了本地观众。 随着信息化…...

京东数据分析平台:2023年8月京东奶粉行业品牌销售排行榜

鲸参谋监测的京东平台8月份奶粉市场销售数据已出炉&#xff01; 鲸参谋数据显示&#xff0c;8月份京东平台上奶粉的销售量将近700万件&#xff0c;环比增长约15%&#xff0c;同比则下滑约19%&#xff1b;销售额将近23亿元&#xff0c;环比增长约4%&#xff0c;同比则下滑约3%。…...

Java 21:虚拟线程介绍

Java 21 版本更新中最重要的功能之一就是虚拟线程 (JEP 444)。这些轻量级线程减少了编写、维护和观察高吞吐量并发应用程序所需的工作量。 正如我的许多其他文章一样&#xff0c;在推出新功能之前&#xff0c;让我们先看看 Java 21 版本更新前的现状&#xff0c;以便更好地了解…...

Redis-缓存穿透,缓存击穿,缓存雪崩

缓存穿透&#xff0c;缓存击穿&#xff0c;缓存雪崩 缓存穿透处理方案解决方案1 缓存空数据解决方案2 布隆过滤器 缓存击穿处理方案解决方案 1 互斥锁解决方案2 逻辑过期 缓存雪崩处理方案解决方案 1 给不同的key的过期时间设置添加一个随机值&#xff0c;降低同一个时段大量ke…...

如何使用Docker实现分布式Web自动化!

01、前言 顺着docker的发展&#xff0c;很多测试的同学也已经在测试工作上使用docker作为环境基础去进行一些自动化测试&#xff0c;这篇文章主要讲述在docker中使用浏览器进行自动化测试如果可以实现可视化&#xff0c;同时可以对浏览器进行相关的操作。 02、开篇 首先我们…...

从零开始:制作出色的产品原型图的详细教程

在设计产品的初始版本或模型时&#xff0c;产品原型起着非常重要的作用&#xff0c;可以帮助设计师和团队更好地了解产品需求和用户需求&#xff0c;优化和改进设计&#xff0c;确保设计最终满足用户的需求和期望。如果你不知道如何绘制产品原型图&#xff0c;绘制产品原型图的…...

美国访问学者签证如何申请加急办理?

许多中国学者梦想着前往美国深造&#xff0c;积累更多的学术经验和知识。然而&#xff0c;签证申请过程可能会变得复杂和繁琐&#xff0c;特别是如果你需要在紧急情况下前往美国。但别担心&#xff0c;本文知识人网小编将为您介绍美国访问学者签证加急办理的一些建议和步骤。 首…...

33 WEB漏洞-逻辑越权之水平垂直越权全解

目录 前言水平&#xff0c;垂直越权&#xff0c;未授权访问Pikachu-本地水平垂直越权演示(漏洞成因)墨者水平-身份认证失效漏洞实战(漏洞成因)原理越权检测-Burpsuite插件Authz安装测试(插件使用)修复防御方案 前言 越权漏洞文章分享&#xff1a;https://www.cnblogs.com/zhen…...

【FreeRTOS】【STM32】02 FreeRTOS 移植

基于 [野火]《FreeRTOS%20内核实现与应用开发实战—基于STM32》 正点原子《STM32F429FreeRTOS开发手册_V1.2》 准备 基础工程&#xff0c;例如点灯 FreeRTOS 系统源码 FreeRTOS 移植 上一章节已经说明了Free RTOS的源码文件在移植时所需要的&#xff0c;FreeRTOS 为我们提供…...

STM32F4X 内部FLASH使用

STM32F4X 内部FLASH使用 STM32F4X 内部FLASHSTM32F4X内部FLASH结构STM32F40X和STM32F41X内部FLASH结构STM32F42X和STM32F43X内部FLASH结构 STM32F4X内部FLASH操作例程internal_flash.hinternal_flash.cmain.c 在嵌入式开发中&#xff0c;经常需要实时保存一些数据。如果工程的代…...

减小windows或linux虚拟机导出ova体积大小

减小windows或linux虚拟机导出ova体积大小 删除无用的文件&#xff0c;比如日志或者命令&#xff0c;程序等&#xff1b;去除磁盘碎片将不用的内存空间填充为0&#xff0c;便于vmdk压缩。 例子&#xff1a; 日志文件置空&#xff1a; 批量置空 /sf/data/log/ 目录下的日志文…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

SpringTask-03.入门案例

一.入门案例 启动类&#xff1a; package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2

每日一言 今天的每一份坚持&#xff0c;都是在为未来积攒底气。 案例&#xff1a;OLED显示一个A 这边观察到一个点&#xff0c;怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 &#xff1a; 如果代码里信号切换太快&#xff08;比如 SDA 刚变&#xff0c;SCL 立刻变&#…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!

简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求&#xff0c;并检查收到的响应。它以以下模式之一…...

【Go语言基础【12】】指针:声明、取地址、解引用

文章目录 零、概述&#xff1a;指针 vs. 引用&#xff08;类比其他语言&#xff09;一、指针基础概念二、指针声明与初始化三、指针操作符1. &&#xff1a;取地址&#xff08;拿到内存地址&#xff09;2. *&#xff1a;解引用&#xff08;拿到值&#xff09; 四、空指针&am…...

Oracle11g安装包

Oracle 11g安装包 适用于windows系统&#xff0c;64位 下载路径 oracle 11g 安装包...

Python 训练营打卡 Day 47

注意力热力图可视化 在day 46代码的基础上&#xff0c;对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...

如何配置一个sql server使得其它用户可以通过excel odbc获取数据

要让其他用户通过 Excel 使用 ODBC 连接到 SQL Server 获取数据&#xff0c;你需要完成以下配置步骤&#xff1a; ✅ 一、在 SQL Server 端配置&#xff08;服务器设置&#xff09; 1. 启用 TCP/IP 协议 打开 “SQL Server 配置管理器”。导航到&#xff1a;SQL Server 网络配…...

快速排序算法改进:随机快排-荷兰国旗划分详解

随机快速排序-荷兰国旗划分算法详解 一、基础知识回顾1.1 快速排序简介1.2 荷兰国旗问题 二、随机快排 - 荷兰国旗划分原理2.1 随机化枢轴选择2.2 荷兰国旗划分过程2.3 结合随机快排与荷兰国旗划分 三、代码实现3.1 Python实现3.2 Java实现3.3 C实现 四、性能分析4.1 时间复杂度…...

未授权访问事件频发,我们应当如何应对?

在当下&#xff0c;数据已成为企业和组织的核心资产&#xff0c;是推动业务发展、决策制定以及创新的关键驱动力。然而&#xff0c;未授权访问这一隐匿的安全威胁&#xff0c;正如同高悬的达摩克利斯之剑&#xff0c;时刻威胁着数据的安全&#xff0c;一旦触发&#xff0c;便可…...