Android在低电量时候充电图标不动,修改办法:
<item android:maxLevel="0">
<animation-list
xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item android:drawable="@drawable/stat_sys_battery_charge_anim0" android:duration="2000" />
<item android:drawable="@drawable/stat_sys_battery_charge_anim1" android:duration="1000" />
<item android:drawable="@drawable/stat_sys_battery_charge_anim2" android:duration="1000" />
<item android:drawable="@drawable/stat_sys_battery_charge_anim3" android:duration="1000" />
<item android:drawable="@drawable/stat_sys_battery_charge_anim4" android:duration="1000" />
<item android:drawable="@drawable/stat_sys_battery_charge_anim5" android:duration="1000" />
</animation-list>
</item>
下面分析一下:
一、StatusBarPolicy.java中:
private final void updateBattery(Intent intent) {
final int id = intent.getIntExtra("icon-small", 0); //获取Icon的id
int level = intent.getIntExtra("level", 0);
mService.setIcon("battery", id, level); //在这里,是决定显示充电图标的地方,mService:StatusBarManager
……
}
一、StatusBarManager.java中:
public void setIcon(String slot, int iconId, int iconLevel) {
try {
mService.setIcon(slot, mContext.getPackageName(), iconId, iconLevel);
} catch (RemoteException ex) {
// system process is dead anyway.
throw new RuntimeException(ex);
}
}
mService:IStatusBarService,其实现函数在CommandQuene.java中,我们进入这个文件:
二、CommandQuene.java:
public void setIcon(int index, StatusBarIcon icon) {
synchronized (mList) {
int what = MSG_ICON | index;
mHandler.removeMessages(what);
mHandler.obtainMessage(what, OP_SET_ICON, 0, icon.clone()).sendToTarget();
}
}
在这里,其实是将需要设置的Icon通过Message 发送出去,在处理函数中实现设置:
private final class H extends Handler {
public void handleMessage(Message msg) {
final int what = msg.what & MSG_MASK;
switch (what) {
case MSG_ICON: {
final int index = msg.what & INDEX_MASK;
final int viewIndex = mList.getViewIndex(index);
switch (msg.arg1) {
case OP_SET_ICON: {
StatusBarIcon icon = (StatusBarIcon)msg.obj;
StatusBarIcon old = mList.getIcon(index);
if (old == null) {
mList.setIcon(index, icon);
mCallbacks.addIcon(mList.getSlot(index), index, viewIndex, icon);
} else {
mList.setIcon(index, icon);
mCallbacks.updateIcon(mList.getSlot(index), index, viewIndex,
old, icon);
}
break;
}
case OP_REMOVE_ICON:
if (mList.getIcon(index) != null) {
mList.removeIcon(index);
mCallbacks.removeIcon(mList.getSlot(index), index, viewIndex);
}
break;
}
break;
}
……
}
实际处理也就是OP_SET_ICON这个位置:若是Icon存在的,则进行更新:
四、StatusBarService.java:
public void updateIcon(String slot, int index, int viewIndex,
StatusBarIcon old, StatusBarIcon icon) {
StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex);
view.set(icon);
}
这段代码说明,从statusIconList对象中获取这个Icon,通过StatusBarIconView进行设置:这里也充分说明了,
StatusBarIcon实际上相当于一个存储,用来存储Icon信息:iconPackage、iconId、iconLevel等等。而StatusBarIconList则是这样的,它根据conifg.xml将StatusBarIcon添加进这个数组里去。在需要时取出。
五、StatusBarIconView.java:
public boolean set(StatusBarIcon icon) {
final boolean iconEquals = mIcon != null
&& streq(mIcon.iconPackage, icon.iconPackage)
&& mIcon.iconId == icon.iconId;
final boolean levelEquals = iconEquals
&& mIcon.iconLevel == icon.iconLevel;
final boolean visibilityEquals = mIcon != null
&& mIcon.visible == icon.visible;
final boolean numberEquals = mIcon != null
&& mIcon.number == icon.number;
mIcon = icon.clone();
//当icon不同时获取Icon并设置
if (!iconEquals) {
Drawable drawable = getIcon(icon);
if (drawable == null) {
Slog.w(StatusBarService.TAG, "No icon for slot " + mSlot);
return false;
}
setImageDrawable(drawable);
}
if (!levelEquals) {
setImageLevel(icon.iconLevel);
}
if (!numberEquals) {
if (icon.number > 0) {
if (mNumberBackground == null) {
mNumberBackground = getContext().getResources().getDrawable(
R.drawable.ic_notification_overlay);
}
placeNumber();
} else {
mNumberBackground = null;
mNumberText = null;
}
invalidate();
}
if (!visibilityEquals) {
setVisibility(icon.visible ? VISIBLE : GONE);
}
return true;
}
在这里特别说一下getIcon(),这更能从侧面反映出StatusBarIcon是存储作用,
r = context.getResources();
return r.getDrawable(icon.iconId);
我们看看getDrawable:实际就是loadDrawable:这其实有解析xml文件的操作:
在public static Drawable getIcon(Context context, StatusBarIcon icon)函数中,
关键代码是:return r.getDrawable(icon.iconId);
实际上,我们获得一个xml文件的id时,要先将其解析,这时就会调用Drawable.createFromXml(),在这里又会调用loadDrawable(value, id),这个函数才是对XML的解析。
if (file.endsWith(".xml")) 是判断要解析的文件是否是xml文件,然后调用下面的函数:
public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs)
throws XmlPullParserException, IOException {
Drawable drawable;
final String name = parser.getName();//根据名字来解析文件
if (name.equals("selector")) {
drawable = new StateListDrawable();
} else if (name.equals("level-list")) { //为level-list时就创建一个LevelListDrawable实例
drawable = new LevelListDrawable();
} else if (name.equals("layer-list")) {
drawable = new LayerDrawable();
} else if (name.equals("transition")) {
drawable = new TransitionDrawable();
} else if (name.equals("color")) {
drawable = new ColorDrawable();
} else if (name.equals("shape")) {
drawable = new GradientDrawable();
} else if (name.equals("scale")) {
drawable = new ScaleDrawable();
} else if (name.equals("clip")) {
drawable = new ClipDrawable();
} else if (name.equals("rotate")) {
drawable = new RotateDrawable();
} else if (name.equals("animated-rotate")) {
drawable = new AnimatedRotateDrawable();
} else if (name.equals("animation-list")) {
drawable = new AnimationDrawable();
} else if (name.equals("inset")) {
drawable = new InsetDrawable();
} else if (name.equals("bitmap")) {
drawable = new BitmapDrawable();
if (r != null) {
((BitmapDrawable) drawable).setTargetDensity(r.getDisplayMetrics());
}
} else if (name.equals("nine-patch")) {
drawable = new NinePatchDrawable();
if (r != null) {
((NinePatchDrawable) drawable).setTargetDensity(r.getDisplayMetrics());
}
} else {
throw new XmlPullParserException(parser.getPositionDescription() +
": invalid drawable tag " + name);
}
drawable.inflate(r, parser, attrs);//这一步十分关键,开始解析
return drawable;
}
drawable.inflate(r, parser, attrs)创建一个LevelListDrawable后就开始解析xml文件:drawable.inflate(r, parser, attrs);在inflate()中,
TypedArray a = r.obtainAttributes(attrs,
com.android.internal.R.styleable.LevelListDrawableItem);
low = a.getInt(
com.android.internal.R.styleable.LevelListDrawableItem_minLevel, 0);
int high = a.getInt(
com.android.internal.R.styleable.LevelListDrawableItem_maxLevel, 0);
int drawableRes = a.getResourceId(
com.android.internal.R.styleable.LevelListDrawableItem_drawable, 0);
a.recycle();
再往下:可以看到若是LevelDrawable中还包含别的drawable,同样要进行解析。比如电池充电动画就属于这种类型,那么,就要解析AnimationDrawable.
之后,解析出来的LevelListDrawable中的Item添加到DrawableContainer中去,也就是addChild()。
六:AnimationImageView.java:
public void setImageDrawable(Drawable drawable) {
super.setImageDrawable(drawable); //调用父类方法,将drawable设置进ImageView中
updateAnim();
}
ImageView中的setImageDrawable(drawable)原型如下:
public void setImageDrawable(Drawable drawable) {
if (mDrawable != drawable) {
mResource = 0;
mUri = null;
/*************************************************************************************************************/
/*到此,都只是将包含有动画的Drawable设置进去,那么经过什么样的处理之后才获得其包含的animation-list的?
因为,在 updateAnim()中,需要 getDrawable(),然后根据这个drawable是否是AnimationDrawable才决定动画与否 */
/*************************************************************************************************************/
updateDrawable(drawable);
requestLayout();
invalidate();
}
}
看看以下函数:
private void updateDrawable(Drawable d) {
if (mDrawable != null) { //这一段意味着若有变化,之前的Drawable的callback和
mDrawable.setCallback(null); //Message都会除去和它的关联。
unscheduleDrawable(mDrawable);
}
mDrawable = d;
if (d != null) {
d.setCallback(this); //新的drawable设置callback
if (d.isStateful()) { //新的drawable是否是isStateful
d.setState(getDrawableState());//设置state
}
d.setLevel(mLevel); //设置level
mDrawableWidth = d.getIntrinsicWidth();
mDrawableHeight = d.getIntrinsicHeight();
applyColorMod();
configureBounds();
}
}
看看setLevel()作用:
public final boolean setLevel(int level) {
if (mLevel != level) {
mLevel = level;
return onLevelChange(level);
}
return false;
}
需要知道的是:com.android.internal.R.drawable.stat_sys_battery_charge.xml文件对应的是LevelListDrawable,因此,这时onLevelChange()必然是LevelListDrawable.java中重写Drawable的函数,这样我们就看到以下的函数实现:
protected boolean onLevelChange(int level) {
int idx = mLevelListState.indexOfLevel(level);
if (selectDrawable(idx)) {
return true;
}
return super.onLevelChange(level);
}
来看看OnlevelChanged()中的语句:
int idx = mLevelListState.indexOfLevel(level);
实际上就是获得LevelListDrawable中的Item对应Id,之后才选择Drawable:
selectDrawable(idx);选中这个drawable Item并将其设置为current drawable
也就是正在使用的Drawable设置为由level指定的drawable:
public boolean selectDrawable(int idx)
{
if (idx == mCurIndex) {
return false;
}
if (idx >= 0 && idx < mDrawableContainerState.mNumChildren) {
Drawable d = mDrawableContainerState.mDrawables[idx];//刚才在解析时添加进来的
if (mCurrDrawable != null) {
mCurrDrawable.setVisible(false, false);
}
mCurrDrawable = d;
mCurIndex = idx;
if (d != null) {
d.setVisible(isVisible(), true);
d.setAlpha(mAlpha);
d.setDither(mDrawableContainerState.mDither);
d.setColorFilter(mColorFilter);
d.setState(getState());
d.setLevel(getLevel());
d.setBounds(getBounds());
}
} else {
if (mCurrDrawable != null) {
mCurrDrawable.setVisible(false, false);
}
mCurrDrawable = null;
mCurIndex = -1;
}
invalidateSelf();
return true;
}