StateListDrawable实现机制

一.使用
<selector xmlns:android="http://schemas.android.com/apk/res/android">

<item android:drawable="@drawable/button_pressed" android:state_pressed="true"/>
<!-- pressed -->
<item android:drawable="@drawable/button_focused" android:state_focused="true"/>
<!-- focused -->
<item android:drawable="@drawable/button_focused" android:state_hovered="true"/>
<!-- hovered -->
<item android:drawable="@drawable/button_normal"/>
<!-- default -->


</selector>


二.实现
问1:view如何绘制drawable到自己身上?
View.java
public void draw(Canvas canvas) {
...
// Step 1, draw the background, if needed
final Drawable background = mBackground;
if (background != null) {
final int scrollX = mScrollX;
final int scrollY = mScrollY;


if (mBackgroundSizeChanged) {
background.setBounds(0, 0,  mRight - mLeft, mBottom - mTop);
mBackgroundSizeChanged = false;
}


if ((scrollX | scrollY) == 0) {
background.draw(canvas);
} else {
canvas.translate(scrollX, scrollY);
background.draw(canvas); // 核心代码:调用Drawable的draw方法
canvas.translate(-scrollX, -scrollY);
}
}
}
总结:当View画图到自己身上,是调用Drawable的draw方法,把如何画图委托给Drawable去绘制


问2:StateListDrawable本质是Drawable的链表,那么当view的draw画背景,当选择哪个Drawable去绘制?
StateListDrawable继承自DrawableContainer继承自Drawable implements Drawable.Callback
思考1:根据问1,调用Drawable.draw实现绘制工作
DrawableContainer.java
@Override
public void draw(Canvas canvas) {
if (mCurrDrawable != null) {
mCurrDrawable.draw(canvas);
}
}
那么谁去设置mCurrDrawable?(猜测:只要知道当前状态就能设置对应的Drawable,故StateListDrawable可完成)
DrawableContainer.selectDrawable(int idx)<--StateListDrawable.onStateChange
StateListDrawable.java
@Override
protected boolean onStateChange(int[] stateSet) {
// 根据一堆的状态,找到StateListDrawable多个Drawable对应的那个(遍历查找)
int idx = mStateListState.indexOfStateSet(stateSet);
if (selectDrawable(idx)) { // 设置当前选中的Drawable
return true;
}
return super.onStateChange(stateSet);
}
总结:onStateChange->selectDrawable->设置mCurrDrawable
那么又是谁来设置onStateChange?(状态改变只有View才知道(已知在View中),所以View才能设置状态)
1.查找View中哪里调用onStateChange,没找到
2.那么肯定是StateListDrawable自己调用的,在StateListDrawable没找到,找父类Drawable,因为View只知道Drawable,不知道StateListDrawable,找到setState==>现在可以查找View,谁调用setState
public boolean setState(final int[] stateSet) {
if (!Arrays.equals(mStateSet, stateSet)) {
mStateSet = stateSet;
return onStateChange(stateSet);
}
return false;
}

View.java
protected void drawableStateChanged() {
Drawable d = mBackground;
if (d != null && d.isStateful()) {
d.setState(getDrawableState());
}
}

   public final int[] getDrawableState() {
mDrawableState = onCreateDrawableState(0);
}
// 查询当前一堆状态信息,拼凑出当前状态
protected int[] onCreateDrawableState(int extraSpace) {
int[] drawableState;


int privateFlags = mPrivateFlags;


int viewStateIndex = 0;
if ((privateFlags & PFLAG_PRESSED) != 0) viewStateIndex |= VIEW_STATE_PRESSED;
if ((mViewFlags & ENABLED_MASK) == ENABLED) viewStateIndex |= VIEW_STATE_ENABLED;
if (isFocused()) viewStateIndex |= VIEW_STATE_FOCUSED;
if ((privateFlags & PFLAG_SELECTED) != 0) viewStateIndex |= VIEW_STATE_SELECTED;
if (hasWindowFocus()) viewStateIndex |= VIEW_STATE_WINDOW_FOCUSED;
if ((privateFlags & PFLAG_ACTIVATED) != 0) viewStateIndex |= VIEW_STATE_ACTIVATED;
if (mAttachInfo != null && mAttachInfo.mHardwareAccelerationRequested &&
HardwareRenderer.isAvailable()) {
// This is set if HW acceleration is requested, even if the current
// process doesn't allow it.  This is just to allow app preview
// windows to better match their app.
viewStateIndex |= VIEW_STATE_ACCELERATED;
}
if ((privateFlags & PFLAG_HOVERED) != 0) viewStateIndex |= VIEW_STATE_HOVERED;


final int privateFlags2 = mPrivateFlags2;
if ((privateFlags2 & PFLAG2_DRAG_CAN_ACCEPT) != 0) viewStateIndex |= VIEW_STATE_DRAG_CAN_ACCEPT;
if ((privateFlags2 & PFLAG2_DRAG_HOVERED) != 0) viewStateIndex |= VIEW_STATE_DRAG_HOVERED;


drawableState = VIEW_STATE_SETS[viewStateIndex];



final int[] fullState;
if (drawableState != null) {
fullState = new int[drawableState.length + extraSpace];
System.arraycopy(drawableState, 0, fullState, 0, drawableState.length);



return fullState;
}

public void refreshDrawableState() {
mPrivateFlags |= PFLAG_DRAWABLE_STATE_DIRTY;
drawableStateChanged();
}

...
// 在onFocusChange,等状态改变时会调用refreshDrawableState
总结:View.java:refreshDrawableState->drawableStateChanged->
Drawable.java:Drawable.setState(View.getDrawableState())->Drawable.onStateChange->selectDrawable(idx)(选中多个Drawable中的一个)


这样当View调用draw的时候,就回到问1





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值