当前位置: 首页 > 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"/>
http://www.lryc.cn/news/186501.html

相关文章:

  • 为什么要学C语言及C语言存在的意义
  • 数据结构——空间复杂度
  • uniapp:swiper-demo效果
  • Graphviz 作图工具
  • vue、vuex状态管理、vuex的核心概念state状态
  • 【QT】Qt Application Manager启动应用源码分析
  • MyBatisPlus(十)判空查询
  • AIGC(生成式AI)试用 8 -- 曾经的难题
  • 文化主题公园旅游景点3d全景VR交互体验加深了他们对历史文化的认知和印象
  • 京东数据分析平台:2023年8月京东奶粉行业品牌销售排行榜
  • Java 21:虚拟线程介绍
  • Redis-缓存穿透,缓存击穿,缓存雪崩
  • 如何使用Docker实现分布式Web自动化!
  • 从零开始:制作出色的产品原型图的详细教程
  • 美国访问学者签证如何申请加急办理?
  • 33 WEB漏洞-逻辑越权之水平垂直越权全解
  • 【FreeRTOS】【STM32】02 FreeRTOS 移植
  • STM32F4X 内部FLASH使用
  • 减小windows或linux虚拟机导出ova体积大小
  • WPF livecharts 折线图遮挡数字问题
  • 电力系统数字化升级改造之配电室无人值守
  • 集合Set
  • TCP/IP(二)导论
  • Java之UDP,TCP的详细解析
  • 【总结】kubernates crd client-java 关于自定义资源的增删改查
  • 蓝牙主要知识,一文概览
  • Linux 守护进程
  • 自动驾驶技术的基础知识
  • 解决:yarn 无法加载文件 “C:\Users\XXXXX\AppData\Roaming\npm\yarn.ps1,因为在此系统上禁止运行脚本“ 的问题
  • 【JVM--StringTable字符串常量池】