有多种因素会导致触发refreshDrawableState,当前只考虑其中一种setPressed即设置视图是否处于被按下状态。其他会触发此方法的有focusChanged等。
原理是定义不同状态的图片,系统进行状态监听例如在onTouchEvent中判断当前在什么状态,再根据之前提供的图片进行设置并重绘显示效果。
以下是根据代码一个具体的流程,其中解释的并不是太多,当前也都比较简单都是直接的方法调用顺序。
先来看看View.setPressed源码
public void setPressed(boolean pressed) {
if (pressed) {
mPrivateFlags |= PRESSED ;
} else {
mPrivateFlags &= ~PRESSED;
}
refreshDrawableState();
dispatchSetPressed(pressed);
}
其中调用了View.refreshDrawableState,下面查看一下相关代码View.refreshDrawableState与 View.drawableStateChanged:
一
public void refreshDrawableState() {
// 用于判断是否发生状态变化
mPrivateFlags |= DRAWABLE_STATE_DIRTY ;
// 状态改变后进行绘制
drawableStateChanged();
ViewParent parent = mParent;
if (parent != null) {
// 父视图如果存在的话执行
parent.childDrawableStateChanged( this);
}
}
// 仅在ViewGroup覆写
protected void drawableStateChanged() {
// 当前视图的背景图
Drawable d = mBGDrawable;
if (d != null && d.isStateful()) {
// 为当前drawable设置当前drawable状态索引
d.setState(getDrawableState());
}
}
// 获取视图的当前状态
public final int[] getDrawableState() {
if ((mDrawableState != null) && ((mPrivateFlags & PFLAG_DRAWABLE_STATE_DIRTY) == 0)) {
return mDrawableState;
} else {
// 将视图状态转换为一个int[]数组
// DrawableState可以识别此数组
mDrawableState = onCreateDrawableState(0);
mPrivateFlags &= ~PFLAG_DRAWABLE_STATE_DIRTY;
return mDrawableState;
}
}
二 以上在View中调用Drawable相应方法Drawable.setState 和 Drawable.onStateChange
public boolean setState(final int[] stateSet) {
if (!Arrays.equals( mStateSet, stateSet)) {
mStateSet = stateSet;
return onStateChange(stateSet);
}
return false ;
}
protected boolean onStateChange (int[] state) { return false; }
三 步骤二中的调用的方法是一个回调,其具体实现在其子类中,此处仅罗列针对其子类StateListDrawable的具体实现代码StateListDrawable.onStateChange和 StateListDrawable.selectDrawable
@Override
protected boolean onStateChange( int[] stateSet) {
int idx = mStateListState.indexOfStateSet(stateSet);
if (idx < 0) {
idx = mStateListState.indexOfStateSet(StateSet.WILD_CARD);
}
if (selectDrawable(idx)) {
return true ;
}
return super .onStateChange(stateSet);
}
public boolean selectDrawable( int idx)
{
......
// 触发绘制请求
invalidateSelf();
return true ;
}
四 StateListDrawable以上方法流程中调用的方法invalidateSelf,由其父类中进行具体实现Drawable.Callback.invalidateSelfe和Drawable.Callback.invalidateDrawable
public void invalidateSelf()
{
// mCallback是在何处赋赋值?
if (mCallback != null) {
mCallback.invalidateDrawable(this);
}
}
// 全局搜索发现此处对其值进行赋值,那何处引用了此方法呢?
public final void setCallback(Callback cb) {
mCallback = new WeakReference<Callback>(cb);
}
public void invalidateDrawable (Drawable who);
五 以上步骤需要调用其setCallback传入cb才行,具体什么地方传入的呢?具体是在View.setBackgroundDrawable中
@Deprecated
public void setBackgroundDrawable(Drawable background) {
...
// 此处设置Callback,当前View就是其Callback
background.setCallback(this);
...
mBGDrawable = background;
}
六 从步骤五可以获知drawable中的Callback就是当前View自身,View实现Drawable.Callback
public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Callback,AccessibilityEventSource {
public void invalidateDrawable(Drawable drawable) {
if (verifyDrawable(drawable)) {
final Rect dirty = drawable.getBounds();
final int scrollX = mScrollX;
final int scrollY = mScrollY;
// 看到了熟悉的invalidate,之后就是走正常的invalidate流程
invalidate(dirty. left + scrollX, dirty.top + scrollY,
dirty. right + scrollX, dirty.bottom + scrollY);
}
}
}
第六步中进行具体的请求刷新
参考资料:
《Android 内核剖析》 柯元丹 著 13.6.3 refreshDrawableList