Android最全深入解析Android的StateListDrawable,2024年最新哔哩哔哩测试面试题

最后

这里我特地整理了一份《Android开发核心知识点笔记》,里面就包含了自定义View相关的内容

除了这份笔记,还给大家分享 Android学习PDF+架构视频+面试文档+源码笔记,高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料这几块的内容。非常适合近期有面试和想在技术道路上继续精进的朋友。

分享上面这些资源,希望可以帮助到大家提升进阶,如果你觉得还算有用的话,不妨把它们推荐给你的朋友~

喜欢本文的话,给我点个小赞、评论区留言或者转发支持一下呗~

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

R.attr.state_enabled, VIEW_STATE_ENABLED,
R.attr.state_pressed, VIEW_STATE_PRESSED,
R.attr.state_activated, VIEW_STATE_ACTIVATED,
R.attr.state_accelerated, VIEW_STATE_ACCELERATED,
R.attr.state_hovered, VIEW_STATE_HOVERED,
R.attr.state_drag_can_accept, VIEW_STATE_DRAG_CAN_ACCEPT,
R.attr.state_drag_hovered, VIEW_STATE_DRAG_HOVERED
};

VIEW_STATE_SETS是一个静态变量,类型为int[][]。初始化过程:

static {
if ((VIEW_STATE_IDS.length / 2) != R.styleable.ViewDrawableStates.length) {
throw new IllegalStateException(
“VIEW_STATE_IDs array length does not match ViewDrawableStates style array”);
}

final int[] orderedIds = new int[VIEW_STATE_IDS.length];
for (int i = 0; i < R.styleable.ViewDrawableStates.length; i++) {
final int viewState = R.styleable.ViewDrawableStates[i];
for (int j = 0; j < VIEW_STATE_IDS.length; j += 2) {
if (VIEW_STATE_IDS[j] == viewState) {
orderedIds[i * 2] = viewState;
orderedIds[i * 2 + 1] = VIEW_STATE_IDS[j + 1];
}
}
}

final int NUM_BITS = VIEW_STATE_IDS.length / 2;
VIEW_STATE_SETS = new int[1 << NUM_BITS][];
for (int i = 0; i < VIEW_STATE_SETS.length; i++) {
final int numBits = Integer.bitCount(i);
final int[] set = new int[numBits];
int pos = 0;
for (int j = 0; j < orderedIds.length; j += 2) {
if ((i & orderedIds[j + 1]) != 0) {
set[pos++] = orderedIds[j];
}
}
VIEW_STATE_SETS[i] = set;
}
}

上面的代码主要做了3件事:

  1. 系统内部通过声明名为ViewDrawableStates的属性组可以对VIEW_STATE_IDS重排序。

为了简化理解,假设排序后的orderedIds[]和VIEW_STATE_IDS是一样的。

  1. VIEW_STATE_IDS中定义了10中状态,一共有2的10次方1024中组合,所以VIEW_STATE_SETS定义为1024行。

VIEW_STATE_SETS数组的行索引值表示状态组合值,列表时状态属性id。

例如:VIEW_STATE_IDS[0x0011][0]=R.attr.state_window_focusedVIEW_STATE_IDS[0x0011][1]=state_selected

  1. 枚举所有可能性

看下View在获取state使用的get()方法:

public static int[] get(int mask) {
if (mask >= VIEW_STATE_SETS.length) {
throw new IllegalArgumentException(“Invalid state set mask”);
}
return VIEW_STATE_SETS[mask];
}

只是返回了VIEW_STATE_IDS对应的行而已。

View的状态计算过程就介绍完成了。

StateListDrawable初始化

定义 <selector/> 的xml文件被解析后,创建StateListDrawable对象,调用inflate()方法,infalte()方法里调用inflateChildElements()方法解析 <item/>

private void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs,
Theme theme) throws XmlPullParserException, IOException {
final StateListState state = mStateListState;
final int innerDepth = parser.getDepth() + 1;
int type;
int depth;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& ((depth = parser.getDepth()) >= innerDepth
|| type != XmlPullParser.END_TAG)) {
if (type != XmlPullParser.START_TAG) {
continue;
}

if (depth > innerDepth || !parser.getName().equals(“item”)) {
continue;
}

// This allows state list drawable item elements to be themed at
// inflation time but does NOT make them work for Zygote preload.
final TypedArray a = obtainAttributes(r, theme, attrs,
R.styleable.StateListDrawableItem);
Drawable dr = a.getDrawable(R.styleable.StateListDrawableItem_drawable);
a.recycle();

final int[] states = extractStateSet(attrs);

// Loading child elements modifies the state of the AttributeSet’s
// underlying parser, so it needs to happen after obtaining
// attributes and extracting states.
if (dr == null) {
while ((type = parser.next()) == XmlPullParser.TEXT) {
}
if (type != XmlPullParser.START_TAG) {
throw new XmlPullParserException(
parser.getPositionDescription()

  • ": tag requires a ‘drawable’ attribute or "
  • “child tag defining a drawable”);
    }
    dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
    }

state.addStateSet(states, dr);
}
}

以解析单个<item/>为例,解析过程如下:

  • extractStateSet()方法解析<item/>定义的android:state_xxx属性,返回states:

int[] extractStateSet(AttributeSet attrs) {
int j = 0;
final int numAttrs = attrs.getAttributeCount();
int[] states = new int[numAttrs];
for (int i = 0; i < numAttrs; i++) {
final int stateResId = attrs.getAttributeNameResource(i);
switch (stateResId) {
case 0:
break;
case R.attr.drawable:
case R.attr.id:
// Ignore attributes from StateListDrawableItem and
// AnimatedStateListDrawableItem.
continue;
default:
states[j++] = attrs.getAttributeBooleanValue(i, false)
? stateResId : -stateResId;
}
}
states = StateSet.trimStateSet(states, j);
return states;
}

android:state_xxx为true,用属性id表示;反之,用属性id的负值表示。

  • 解析<item/>android:drawable属性,创建Drawable对象。

  • 通过 state.addStateSet(states, dr)将states和drawable联系起来。

state为StateListState对象,addStateSet()实现如下:

int addStateSet(int[] stateSet, Drawable drawable) {
final int pos = addChild(drawable);
mStateSets[pos] = stateSet;
return pos;
}

mStateSets是int[][]数组,上面方法把Drawable对象和stateSet建立了一一对应的关系。

看下addChild()的实现,主要工作把Drawable对象存入数组:

public final int addChild(Drawable dr) {
final int pos = mNumChildren;
if (pos >= mDrawables.length) {
growArray(pos, pos+10);
}

dr.mutate();
dr.setVisible(false, true);
dr.setCallback(mOwner);

mDrawables[pos] = dr;
mNumChildren++;
mChildrenChangingConfigurations |= dr.getChangingConfigurations();

invalidateCache();

mConstantPadding = null;
mCheckedPadding = false;
mCheckedConstantSize = false;
mCheckedConstantState = false;

return pos;
}

至此,StateListDrawable对象初始化完成。

match过程

View的states计算好了,StateListDrawable也初始化完成了,接下来就是match,找到对应Drawable的过程了。

View计算完当前states会将StatelistDrawable对象也设置为当前state:

protected void drawableStateChanged() {
final int[] state = getDrawableState();
boolean changed = false;

final Drawable bg = mBackground;
if (bg != null && bg.isStateful()) {
changed |= bg.setState(state);
}

if (changed) {
invalidate();
}
}

由于StateListDrawable没有重写setState()方法,所有看下Drawable的setState()实现:

public boolean setState(@NonNull final int[] stateSet) {
if (!Arrays.equals(mStateSet, stateSet)) {
mStateSet = stateSet;
return onStateChange(stateSet);
}
return false;
}

StateListDrawable重写了onStateChange()方法,又回到StateListDrawable了:

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;
}

最关键的地方来了。mStateListState是StateListState对象,很熟悉是不是,在StateListDrawable初始化过程有提到过。indexOfStateSet()实现:

int indexOfStateSet(int[] stateSet) {
final int[][] stateSets = mStateSets;
final int N = getChildCount();
for (int i = 0; i < N; i++) {
if (StateSet.stateSetMatches(stateSets[i], stateSet)) {
return i;
}
}
return -1;
}

很简单,也很暴力,直接用解析到的mStateSets的每一行和被设置的stateSet进行匹配。匹配规则由stateSetMatches()方法决定:

public static boolean stateSetMatches(int[] stateSpec, int[] stateSet) {
if (stateSet == null) {
return (stateSpec == null || isWildCard(stateSpec));
}
int stateSpecSize = stateSpec.length;
int stateSetSize = stateSet.length;
for (int i = 0; i < stateSpecSize; i++) {
int stateSpecState = stateSpec[i];
if (stateSpecState == 0) {
// We’ve reached the end of the cases to match against.
return true;
}
final boolean mustMatch;
if (stateSpecState > 0) {
mustMatch = true;
} else {
// We use negative values to indicate must-NOT-match states.
mustMatch = false;
stateSpecState = -stateSpecState;
}
boolean found = false;
for (int j = 0; j < stateSetSize; j++) {
final int state = stateSet[j];
if (state == 0) {
// We’ve reached the end of states to match.
if (mustMatch) {
// We didn’t find this must-match state.
return false;
} else {
// Continue checking other must-not-match states.
break;
}
}
if (state == stateSpecState) {
if (mustMatch) {
found = true;

最后

总之啊,家里没矿的同学们,如果你们想以后的日子过得好一些,多想想你们的业余时间怎么安排吧;

技术方面的提升肯定是重中之重,但是技术外的一些“软实力”也不能完全忽视,很多时候升职确实是因为你的技术足够强,但也与你的“软实力”密切相关

在这我也分享一份大佬自己收录整理的 Android学习PDF+架构视频+面试文档+源码笔记 ,还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料这些都是我闲暇还会反复翻阅并给下属员工学习的精品资料。在脑图中,每个知识点专题都配有相对应的实战项目,可以有效的帮助大家掌握知识点。

总之也是在这里帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习

相信自己,没有做不到的,只有想不到的

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

一起学习

[外链图片转存中…(img-ctG87AGu-1715246116407)]

[外链图片转存中…(img-fGypVErw-1715246116407)]

相信自己,没有做不到的,只有想不到的

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值