Android API<29自定义View如何从StateListDrawable获取对应状态Drawable?
Android API<29下自定义View使用StateListDrawable的方案
你已经在Android Q(API≥29)上实现了从StateListDrawable获取对应状态Drawable的逻辑,下面针对API<29的场景提供两种可行方案,同时说明标准SeekBar在低版本的实现逻辑:
一、API<29时获取对应状态Drawable的两种方法
方法1:反射访问内部属性
API29之前StateListDrawable未开放直接获取指定状态Drawable的API,可通过反射读取其内部存储的状态项列表,遍历匹配目标状态集:
private Drawable getStateDrawableFromStateList(StateListDrawable stateListDrawable, int[] targetState) { if (stateListDrawable == null || targetState == null) { return null; } try { // 获取StateListDrawable内部存储状态项的列表 Field stateListField = StateListDrawable.class.getDeclaredField("mStateList"); stateListField.setAccessible(true); List<?> stateItems = (List<?>) stateListField.get(stateListDrawable); // 按XML定义的顺序遍历,匹配目标状态集 for (Object item : stateItems) { Field stateSetField = item.getClass().getDeclaredField("mStateSet"); stateSetField.setAccessible(true); int[] stateSet = (int[]) stateSetField.get(item); if (Arrays.equals(stateSet, targetState)) { Field drawableField = item.getClass().getDeclaredField("mDrawable"); drawableField.setAccessible(true); return (Drawable) drawableField.get(item); } } } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); } // 无匹配项时返回默认Drawable(XML中最后定义的项) return stateListDrawable.getCurrent(); }
调用示例:
// 获取禁用状态的Drawable Drawable disabledDrawable = getStateDrawableFromStateList(mStateListDrawable, new int[]{-android.R.attr.state_enabled}); // 获取启用状态的Drawable Drawable enabledDrawable = getStateDrawableFromStateList(mStateListDrawable, new int[]{android.R.attr.state_enabled});
方法2:通过View状态切换间接获取
借助自定义View的状态切换,让StateListDrawable自动匹配对应状态的Drawable,之后恢复原状态:
private Drawable getDrawableBySettingState(View view, StateListDrawable stateListDrawable, int[] targetState) { if (view == null || stateListDrawable == null) { return null; } // 保存View原始状态 int[] originalState = view.getDrawableState(); // 设置目标状态触发Drawable切换 view.setState(targetState); // 获取当前匹配的Drawable Drawable targetDrawable = stateListDrawable.getCurrent(); // 恢复View原始状态 view.setState(originalState); return targetDrawable; }
调用示例:
// 在自定义View内部调用 Drawable disabledDrawable = getDrawableBySettingState(this, mStateListDrawable, new int[]{-android.R.attr.state_enabled});
二、标准SeekBar在API29前的实现逻辑
API29之前,SeekBar并未主动单独获取某一状态的Thumb Drawable,而是依赖StateListDrawable的自动状态匹配机制:
- 当SeekBar的状态(如启用/禁用)变化时,会调用
refreshDrawableState()更新View的状态数组; StateListDrawable会根据View的当前状态数组,自动匹配XML中定义的对应状态项,切换显示的Drawable;- SeekBar的
onDraw方法直接调用getThumb()获取当前状态对应的Drawable,无需手动单独获取特定状态的资源。
若需单独提取某个状态的Thumb,官方源码中也会用到类似反射的方式解析StateListDrawable内部项,但更多场景下都是依靠状态自动切换来实现UI变化。
内容的提问来源于stack exchange,提问作者Aleks QQQ
相关产品推荐
相关产品推荐

