selector的使用及执行流程

本文详细介绍了Android中的背景选择器(selector)的使用方法及其执行流程。selector可以根据View的不同状态显示不同的背景图片,如按钮的默认状态和按下状态。文章还深入解析了selector的工作原理。
selector是Android中的背景选择器。一个selector使用几个不同的drawable来表示相同的图形,根据对象的状态来决定使用哪一个drawable。比如,一个按钮可以有不同的状态,默认状态、被按下的状态。

官方文档:
https://developer.android.com/guide/topics/resources/drawable-resource.html

一、selector的使用
selector的使用语法如下。
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
    android:constantSize=["true" | "false"]
    android:dither=["true" | "false"]
    android:variablePadding=["true" | "false"] >
    <item
        android:drawable="@[package:]drawable/drawable_resource"
        android:state_pressed=["true" | "false"]
        android:state_focused=["true" | "false"]
        android:state_hovered=["true" | "false"]
        android:state_selected=["true" | "false"]
        android:state_checkable=["true" | "false"]
        android:state_checked=["true" | "false"]
        android:state_enabled=["true" | "false"]
        android:state_activated=["true" | "false"]
        android:state_window_focused=["true" | "false"] />
</selector>

其中会使用到的所有的状态如下。
android:state_enabled:是否可用状态(适用于所有View)。
android:state_pressed:被点击状态(如按钮被触摸或点击)。
android:state_focused:获取到焦点状态(如文本输入框获取到焦点)。
android:state_selected:被选中状态(使用view.setSelected()可触发)。
android:state_checkable:是否可勾选状态(适用于可勾选的控件,如CheckBox)。
android:state_checked:是否被勾选状态。
android:state_hovered:光标悬停状态。4.0版本以上支持。
android:state_activated:被激活状态(使用view.setActivated()可触发)。3.0版本以上支持。
android:state_window_focused:当前应用程序窗口是否在前台的状态(如通知栏被拉下或对话框弹出时,当前窗口失去焦点)。

典型案例,为按钮添加selector,按钮拥有默认状态和点击态。在res/drawable/路径下添加button_bg.xml。
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true" android:drawable="@drawable/button_pressed" /> <!-- pressed -->
    <item android:drawable="@drawable/button_normal" /> <!-- default -->
</selector>

将上面定义的selector添加到Button中。
<Button
    android:layout_height="wrap_content"
    android:layout_width="wrap_content"
    android:background="@drawable/button_bg" />

button.setBackgroundDrawable(context.getResources.getDrawable(R.drawable.button_bg));

注意:selector中从上到下第一个能够匹配当前状态的item会被使用。如果一个item没有定义任何状态,那么可以被任何一个状态所匹配。所以按钮的默认状态总是放到最后。

二、selector的执行流程
当我们定义好一个selector的xml文件,使用setBackgroundDrawable()方法传入该selector,系统内部会创建一个StateListDrawable类型的对象。StateListDrawable类由Drawable类派生而来。

那么,View是如何根据不同的状态来显示对应的背景图的呢。

当View的状态发生改变时,会执行它的refreshDrawableState()方法来刷新背景图的Drawable对象。比如,在View的setSelected()、setActivated()等方法中,都能看到当修改完状态后,会执行refreshDrawableState()方法。
public void refreshDrawableState() {
    ......
    drawableStateChanged();

    ViewParent parent = mParent;
    if (parent != null) {
        parent.childDrawableStateChanged(this);
    }
}

refreshDrawableState()方法内部,主要是调用了drawableStateChanged()。从方法中可以看到,其内部会调用Drawable类的setState()方法来完成。
protected void drawableStateChanged() {
    // 获取View当前状态的资源id数组
    final int[] state = getDrawableState();

    // 背景Drawable对象
    final Drawable bg = mBackground;
    if (bg != null && bg.isStateful()) {
        // 根据View当前状态,更新对应状态下的Drawable对象
        bg.setState(state);
    }

    ......
}

接下来进入Drawable类的setState()方法。当状态值发生改变时,会回调onStateChange()方法。
public boolean setState(final int[] stateSet) {
    if (!Arrays.equals(mStateSet, stateSet)) {
        mStateSet = stateSet;
        return onStateChange(stateSet);
    }
    return false;
}

Drawable类中的onStateChange()方法只有一行return false,StateListDrawable类对onStateChange()进行了重写。
我们进入StateListDrawable类的onStateChange()方法。
@Override
protected boolean onStateChange(int[] stateSet) {
    final boolean changed = super.onStateChange(stateSet);

    int idx = mStateListState.indexOfStateSet(stateSet);
    if (DEBUG) android.util.Log.i(TAG, "onStateChange " + this + " states "
            + Arrays.toString(stateSet) + " found " + idx);
    if (idx < 0) {
        idx = mStateListState.indexOfStateSet(StateSet.WILD_CARD);
    }

    return selectDrawable(idx) || changed;
}
在方法内部,调用indexOfStateSet()方法,根据传入的状态,从mStateListState对象中,找到第一个能够匹配该状态的索引(匹配算法的实现,在android.util.StateSet类中的stateSetMatches(int[] stateSpec, int[] stateSet)方法)。最后,调用selectDrawable()方法。

selectDrawable()方法在StateListDrawable类的直属父类DrawableContainer类中。在selectDrawable()方法中根据索引获取对应的Drawable对象,赋值给成员变量mCurrDrawable,最后调用invalidateSelf()方法。
public boolean selectDrawable(int idx) {
    ......

    if (idx >= 0 && idx < mDrawableContainerState.mNumChildren) {
        final Drawable d = mDrawableContainerState.getChild(idx);
        mCurrDrawable = d;
    }

    ......

    invalidateSelf();

    return true;
}

invalidateSelf()方法在父类Drawable类中。在方法内部,获取Callback对象,调用Callback的invalidateDrawable()方法,参数传入this即当前的Drawable对象。
public void invalidateSelf() {
    final Callback callback = getCallback();
    if (callback != null) {
        callback.invalidateDrawable(this);
    }
}

接下来需要弄明白的是上面方法中的Callback对象是谁。在Drawable类中找到getCallback()方法,方法中返回的是成员变量mCallback。mCallback通过setCallback()方法赋值。
private WeakReference<Callback> mCallback = null;

public Callback getCallback() {
    if (mCallback != null) {
        return mCallback.get();
    }
    return null;
}

public final void setCallback(Callback cb) {
    mCallback = new WeakReference<Callback>(cb);
}

其实View类实现了Callback接口。我们再次回到最初的方法setBackgroundDrawable()中。
public class View implements Drawable.Callback {
    ......

    public void setBackgroundDrawable(Drawable background) {
        ......

        if (background != null) {
            ......

            background.setCallback(this);

            mBackground = background;

            ......
        }
    }

    ......
}
关键代码:background.setCallback(this),也就是说View将自己赋值给了Drawable的成员变量mCallback。再结合上面invalidateSelf()方法中的关键代码:callback.invalidateDrawable(this),我们可以知道这里的callback就是View对象。好了,接着invalidateSelf()方法往下走,执行View类的invalidateDrawable()方法。

View类的invalidateDrawable()方法如下。
public void invalidateDrawable(@NonNull Drawable drawable) {
    if (verifyDrawable(drawable)) {
        final Rect dirty = drawable.getDirtyBounds();
        final int scrollX = mScrollX;
        final int scrollY = mScrollY;

        invalidate(dirty.left + scrollX, dirty.top + scrollY,
                dirty.right + scrollX, dirty.bottom + scrollY);
        rebuildOutline();
    }
}
View在invalidateDrawable()方法中,调用了invalidate()方法重新绘制。至此,View的背景drawable就发生了改变。

Netty框架的执行流程和工作原理围绕其核心组件展开,包括Reactor(EventLoop)、Handler、NIO和Channel等,采用异步非阻塞I/O处理模型,通过事件驱动机制实现高效的并发连接处理[^1]。 ### 执行流程 Netty的执行流程主要由`NioEventLoop`负责,它是一个事件循环线程,负责监听和处理I/O事件。`NioEventLoop`的每次循环包含以下步骤: 1. **时间轮询(select)**:通过Java NIO的Selector机制,轮询注册在其上的Channel是否有I/O事件发生,如读、写、连接和接受连接等事件。 2. **事件处理(processSelectedKeys)**:处理在select阶段检测到的I/O事件。这些事件会被封装为任务提交到任务队列中,等待后续处理。 3. **任务处理(runAllTasks)**:执行任务队列中的任务,这些任务可以是用户提交的普通任务或定时任务[^3]。 此外,Netty提供了一个参数`ioRatio`,用于调整I/O事件处理和任务处理的时间比例,从而优化系统性能。 ### 事件处理机制 Netty的事件处理机制基于责任链模式,将网络通信的底层细节与业务逻辑分离。通过`ChannelHandler`和`ChannelPipeline`的协作,Netty实现了灵活的事件传递和处理机制。 - **ChannelHandler**:负责处理I/O事件或拦截I/O操作。它可以是入站处理器(`ChannelInboundHandler`)或出站处理器(`ChannelOutboundHandler`)。开发者可以自定义`ChannelHandler`来实现业务逻辑。 - **ChannelPipeline**:是一个`ChannelHandler`的链表结构,负责将`ChannelHandler`按顺序连接起来,形成一个完整的事件处理流水线。当I/O事件发生时,事件会沿着`ChannelPipeline`依次传递,每个`ChannelHandler`可以选择处理事件或将其传递给下一个处理器[^2]。 ### 核心组件 - **Reactor(EventLoop)**:`NioEventLoop`是Netty中Reactor模式的具体实现,它负责监听I/O事件并调度执行相应的处理逻辑。 - **Channel**:代表一个网络连接,封装了底层的Socket操作。Netty支持多种类型的Channel,如`NioSocketChannel`(用于TCP连接)和`NioServerSocketChannel`(用于服务器端监听)。 - **Handler**:定义了处理I/O事件的接口,开发者可以通过实现`ChannelHandler`来处理各种事件。 - **NIO**:Netty基于Java NIO构建,利用非阻塞I/O模型提高并发性能。 ### 示例代码 以下是一个简单的Netty服务器端代码示例,展示了如何创建一个基于Netty的TCP服务器: ```java import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; public class NettyServer { public static void main(String[] args) throws Exception { // 创建两个EventLoopGroup实例,分别用于处理客户端连接和数据传输 EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { // 创建ServerBootstrap实例,用于配置服务器 ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) // 指定Channel类型 .childHandler(new ChannelInitializer<SocketChannel>() { // 设置ChannelHandler @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new MyServerHandler()); // 添加自定义Handler } }) .option(ChannelOption.SO_BACKLOG, 128) // 设置服务器端接受连接的队列大小 .childOption(ChannelOption.SO_KEEPALIVE, true); // 设置保持连接活跃 // 绑定端口并启动服务器 ChannelFuture f = b.bind(8080).sync(); // 等待服务器Socket关闭 f.channel().closeFuture().sync(); } finally { // 优雅关闭EventLoopGroup workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } } } ``` 在这个示例中,`MyServerHandler`是一个自定义的`ChannelHandler`,用于处理客户端发送的数据。通过`ChannelPipeline`,Netty会将接收到的数据传递给`MyServerHandler`进行处理。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值