演员的智能衣橱系统之Selector选择器

让我们像拆解一台精密的主题公园游乐设施那样,揭开Android选择器(Selector)实现动态效果的神秘面纱!想象一下:你的按钮(Button)是一位演员,而Selector就是它的智能衣橱系统------不同舞台场景(点击、聚焦等)下自动切换戏服(Drawable)。下面我们从源码角度,结合这个比喻深入解析其魔法原理。


🎪 一、主题公园的比喻:Selector如何工作?

  1. 演员(View)与舞台状态

    • 当按钮被按下(state_pressed=true),就像演员突然被聚光灯照射(场景变化)。
    • 此时,智能衣橱(StateListDrawable) 立刻扫描标签规则:"若在聚光灯下(pressed),穿红色戏服(@drawable/button_pressed)"。
  2. 衣橱的规则表(selector.xml)

    规则定义示例:

    xml 复制代码
    <selector>
      <item android:state_pressed="true" android:drawable="@drawable/button_pressed" />
      <item android:drawable="@drawable/button_normal" /> <!-- 默认戏服 -->
    </selector>

    关键点:规则按顺序匹配!第一个符合条件的戏服直接生效,类似游乐设施的快速通道规则46。


🔧 二、源码层:StateListDrawable的魔法引擎

1. 核心类关系

  • StateListDrawable:智能衣橱总管,管理状态与Drawable的映射。
  • DrawableContainer:衣橱的基架,负责切换当前显示的Drawable(戏服)2。
  • StateListState:内部规则记录本,存储所有状态组合(如pressed+focused)对应的Drawable2。

2. 状态切换的触发流程

当按钮被按下时:

  • Step 1 : View调用refreshDrawableState(),更新自身状态数组(如[state_pressed])。

  • Step 2 : View通知背景Drawable(即StateListDrawable):"状态变了!" → 调用setState(int[] stateSet)

  • Step 3 : StateListDrawable.onStateChange()启动规则匹配引擎2:

    java 复制代码
    protected boolean onStateChange(int[] stateSet) {
        int idx = mStateListState.indexOfStateSet(stateSet); // 扫描规则表
        if (idx < 0) {
            idx = mStateListState.indexOfStateSet(StateSet.WILD_CARD); // 匹配默认项
        }
        return selectDrawable(idx); // 切换Drawable!
    }
  • Step 4 : selectDrawable(idx)调用父类DrawableContainer的方法,将当前显示的Drawable切换到索引idx对应的戏服2。

3. 匹配规则的秘密

  • 规则表(mStateSets)是一个int[][]数组,存储所有状态组合(如{ state_pressed, state_enabled })。
  • 匹配优先级 :完全匹配 → 通配符匹配(WILD_CARD)。若未定义默认项,可能"裸奔"(无Drawable)!26

🎨 三、性能优化:衣橱的缓存与陷阱

  1. 为什么顺序重要?

    假设规则如下:

    xml 复制代码
    <item android:state_pressed="true" ... />
    <item android:state_focused="true" ... />

    当按钮既被按下(pressed)又获得焦点(focused),因pressed在前,它直接匹配第一条规则,focused规则被忽略!6

  2. 避免过度绘制

    • 问题:Selector嵌套多层或使用大图导致内存飙升。

    • 解法:用单图+ColorFilter动态变色替代多图3:

      java 复制代码
      // 动态设置按下时的颜色
      drawable.setColorFilter(new PorterDuffColorFilter(Color.RED, Mode.SRC_IN));
  3. Compose新时代的"Selector"

    Jetpack Compose用InteractionSource监听状态,逻辑更简洁:

    kotlin 复制代码
    val isPressed by interactionSource.collectIsPressedAsState()
    Box(modifier = Modifier.background(if (isPressed) Color.Red else Color.Green))

    原理类似,但告别了XML5。


⚙️ 四、动态创建Selector:代码造衣橱

当戏服需动态生成(如从网络下载颜色),可用代码构建:

java 复制代码
// 1. 创建智能衣橱
StateListDrawable selector = new StateListDrawable();

// 2. 添加规则:按下时=红色戏服,默认=灰色
selector.addState(new int[]{android.R.attr.state_pressed}, new ColorDrawable(Color.RED));
selector.addState(new int[]{}, new ColorDrawable(Color.GRAY)); // 默认项

// 3. 绑定给按钮
button.setBackground(selector);

此方式避免了XML的静态限制,适合动态主题切换710。


💎 总结:Selector的魔法本质

  • 规则表驱动StateListDrawable是状态→Drawable的映射表,通过onStateChange实时匹配。
  • 绘制责任委托 :匹配后,由DrawableContainer将当前Drawable绘制到View上(本质是代理模式)。
  • 性能核心:减少图片数量(用Shape/Tint替代),规则表层级扁平化。

🎭 终极比喻

你的按钮是舞台演员,StateListDrawable是它的AI造型师,DrawableContainer是更衣室。演员一喊"灯光师就位!(state_pressed=true)",造型师秒查规则表,从更衣室抽出对应戏服------观众看到的,就是丝滑的动态效果!

通过此剖析,下次写Selector时,你就是在导演一场视觉魔法秀🎩✨。

相关推荐
2601_949833393 小时前
flutter_for_openharmony口腔护理app实战+预约管理实现
android·javascript·flutter
2603_949462105 小时前
Flutter for OpenHarmony社团管理App实战:预算管理实现
android·javascript·flutter
王泰虎7 小时前
安卓开发日记,因为JCenter 关闭导致加载不了三方库应该怎么办
android
2601_9495430111 小时前
Flutter for OpenHarmony垃圾分类指南App实战:主题配置实现
android·flutter
2601_9498333912 小时前
flutter_for_openharmony口腔护理app实战+知识实现
android·javascript·flutter
晚霞的不甘12 小时前
Flutter for OpenHarmony从基础到专业:深度解析新版番茄钟的倒计时优化
android·flutter·ui·正则表达式·前端框架·鸿蒙
鸟儿不吃草13 小时前
android的Retrofit请求https://192.168.43.73:8080/报错:Handshake failed
android·retrofit
Minilinux201813 小时前
Android音频系列(09)-AudioPolicyManager代码解析
android·音视频·apm·audiopolicy·音频策略
李子红了时13 小时前
【无标题】
android
Android系统攻城狮14 小时前
Android tinyalsa深度解析之pcm_close调用流程与实战(一百零四)
android·pcm·tinyalsa·音频进阶·音频性能实战·android hal