前言
Android里最常用的5大包名
package:
android.animation
android.text
android.view
android.widget
android.graphics
本篇介绍android.graphics当中的drawable
Though usually not visible to the application, Drawables may take a variety
of forms:
- Bitmap: the simplest Drawable, a PNG or JPEG image.
Nine Patch: an extension to the PNG format allows it to
specify information about how to stretch it and place things inside of
it.
Shape: contains simple drawing commands instead of a raw
bitmap, allowing it to resize better in some cases.
Layers: a compound drawable, which draws multiple underlying
drawables on top of each other.
States: a compound drawable that selects one of a set of
drawables based on its state.
Levels: a compound drawable that selects one of a set of
drawables based on its level.
Scale: a compound drawable with a single child drawable,
whose overall size is modified based on the current level.
Drawable方法论
分类
Drawable的子类们
大纲
ShapeDrawable
draw
1 |
|
onDraw
1 | /** |
shape与shapeDrawable
ShapeState
1 | /** |
ColorDrawable
draw
1 |
|
BitmapDrawable
draw
1 |
|
LayerDrawable
draw
1 |
|
LayerState
1 | static class LayerState extends ConstantState { |
VertorDrawable
如何建立矢量树
相关类
建树
1 | eg: |
如何画
VectorDrawable:draw(Canvas ) ->VectorDrawable:updateCachedBitmap(int width, int height)
->VPathRenderer:draw(Canvas canvas, int w, int h, ColorFilter filter)
->VPathRenderer:drawGroupTree(VGroup , Matrix, Canvas, int w, int h, ColorFilter)
->VPathRenderer:drawPath(VGroup, VPath, Canvas, int w, int h, ColorFilter)
step 1: 确定canvas 的宽高
1 |
|
step 2:调用PathRenderer
1 | public void updateCachedBitmap(int width, int height) { |
step 3:递归画路径
1 | public void draw(Canvas canvas, int w, int h, ColorFilter filter) { |
GradientDrawable
Create
create GradientDrawable children’s element
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27if (name.equals("size")) {
a = obtainAttributes(r, theme, attrs, R.styleable.GradientDrawableSize);
updateGradientDrawableSize(a);
a.recycle();
} else if (name.equals("gradient")) {
a = obtainAttributes(r, theme, attrs, R.styleable.GradientDrawableGradient);
updateGradientDrawableGradient(r, a);
a.recycle();
} else if (name.equals("solid")) {
a = obtainAttributes(r, theme, attrs, R.styleable.GradientDrawableSolid);
updateGradientDrawableSolid(a);
a.recycle();
} else if (name.equals("stroke")) {
a = obtainAttributes(r, theme, attrs, R.styleable.GradientDrawableStroke);
updateGradientDrawableStroke(a);
a.recycle();
} else if (name.equals("corners")) {
a = obtainAttributes(r, theme, attrs, R.styleable.DrawableCorners);
updateDrawableCorners(a);
a.recycle();
} else if (name.equals("padding")) {
a = obtainAttributes(r, theme, attrs, R.styleable.GradientDrawablePadding);
updateGradientDrawablePadding(a);
a.recycle();
} else {
Log.w("drawable", "Bad element under <shape>: " + name);
}
Draw
各种Canvas的基本用法组合[*用以学习Canvas的使用场景]
DrawableContainer
三个方法四个参数
Method 1:selectDrawable
1 | public boolean selectDrawable(int idx) { |
Method 2 animate:
1 | void animate(boolean schedule) { |
Method 3 initializeDrawableForDisplay:
1 | /** |
Parameters:
1 | private Drawable mCurrDrawable;//管理前后状态 |
子类们
AnimatableDrawable
xmlTag
Drawable:createFromXmlInner(Resources , XmlPullParser, AttributeSet, Theme)
1
2
3
4
5
6
7
8
9
10
11
12* <pre>
* <!-- Animation frames are wheel0.png through wheel5.png
* files inside the res/drawable/ folder -->
* <animation-list android:id="@+id/selected" android:oneshot="false">
* <item android:drawable="@drawable/wheel0" android:duration="50" />
* <item android:drawable="@drawable/wheel1" android:duration="50" />
* <item android:drawable="@drawable/wheel2" android:duration="50" />
* <item android:drawable="@drawable/wheel3" android:duration="50" />
* <item android:drawable="@drawable/wheel4" android:duration="50" />
* <item android:drawable="@drawable/wheel5" android:duration="50" />
* </animation-list></pre>
* <p>切换流程
1
start -> run -> nextFrame -> setFrame -> scheduleSelf(Runnable,when)
scheduleDrawable的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25// 1.通过 View:setBackgroudDrawable 中的 background.setCallback(this);
// 2.成功 回调View:scheduleDrawable
/**
* Schedules an action on a drawable to occur at a specified time.
*
* @param who the recipient of the action
* @param what the action to run on the drawable
* @param when the time at which the action must occur. Uses the
* {@link SystemClock#uptimeMillis} timebase.
*/
public void scheduleDrawable(Drawable who, Runnable what, long when) {
if (verifyDrawable(who) && what != null) {
//updatimeMillis开机后的经过的时间,不包括深度睡眠的时间,delay 约为duration
final long delay = when - SystemClock.uptimeMillis();
if (mAttachInfo != null) {
mAttachInfo.mViewRootImpl.mChoreographer.postCallbackDelayed(
Choreographer.CALLBACK_ANIMATION, what, who,
Choreographer.subtractFrameDelay(delay));
} else {
//下一个frame,what = animationDrawable run nextFrame
ViewRootImpl.getRunQueue().postDelayed(what, delay);
}
}
}
mRunnig vs Animating
1
2
3
4
5/** Whether the drawable has an animation callback posted. *///是否有callback被posted
private boolean mRunning;
/** Whether the drawable should animate when visible. *///可视状态是否需要继续执行动画
private boolean mAnimating;
LevelListDrawable
xmlTag
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30/**
* A resource that manages a number of alternate Drawables, each assigned a maximum numerical value.
* Setting the level value of the object with {@link #setLevel(int)} will load the image with the next
* greater or equal value assigned to its max attribute.
* A good example use of
* a LevelListDrawable would be a battery level indicator icon, with different images to indicate the current
* battery level.
* <p>
* It can be defined in an XML file with the <code><level-list></code> element.
* Each Drawable level is defined in a nested <code><item></code>. For example:
* </p>
* <pre>
* <level-list xmlns:android="http://schemas.android.com/apk/res/android">
* <item android:maxLevel="0" android:drawable="@drawable/ic_wifi_signal_1" />
* <item android:maxLevel="1" android:drawable="@drawable/ic_wifi_signal_2" />
* <item android:maxLevel="2" android:drawable="@drawable/ic_wifi_signal_3" />
* <item android:maxLevel="3" android:drawable="@drawable/ic_wifi_signal_4" />
* </level-list>
*</pre>
* <p>With this XML saved into the res/drawable/ folder of the project, it can be referenced as
* the drawable for an {@link android.widget.ImageView}. The default image is the first in the list.
* It can then be changed to one of the other levels with
* {@link android.widget.ImageView#setImageLevel(int)}. For more
* information, see the guide to <a
* href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
*
* @attr ref android.R.styleable#LevelListDrawableItem_minLevel
* @attr ref android.R.styleable#LevelListDrawableItem_maxLevel
* @attr ref android.R.styleable#LevelListDrawableItem_drawable
*/onLevelChange
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//private int[] mLows; //use to stroe drawable level zone by pos
//private int[] mHighs; //use to store drawable level zone by pos
/**
invoke by setLevel on any other method and then find the most match child to display
by the mLows and mHighs
**/
protected boolean onLevelChange(int level) {
int idx = mLevelListState.indexOfLevel(level);
if (selectDrawable(idx)) {
return true;
}
return super.onLevelChange(level);
}
StateListDrawable
how to inflater
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59/**
* Lets you assign a number of graphic images to a single Drawable and swap out the visible item by a string
* ID value.
* <p/>
* <p>It can be defined in an XML file with the <code><selector></code> element.
* Each state Drawable is defined in a nested <code><item></code> element. For more
* information, see the guide to <a
* href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
*
* @attr ref android.R.styleable#StateListDrawable_visible
* @attr ref android.R.styleable#StateListDrawable_variablePadding
* @attr ref android.R.styleable#StateListDrawable_constantSize
* @attr ref android.R.styleable#DrawableStates_state_focused
* @attr ref android.R.styleable#DrawableStates_state_window_focused
* @attr ref android.R.styleable#DrawableStates_state_enabled
* @attr ref android.R.styleable#DrawableStates_state_checkable
* @attr ref android.R.styleable#DrawableStates_state_checked
* @attr ref android.R.styleable#DrawableStates_state_selected
* @attr ref android.R.styleable#DrawableStates_state_activated
* @attr ref android.R.styleable#DrawableStates_state_active
* @attr ref android.R.styleable#DrawableStates_state_single
* @attr ref android.R.styleable#DrawableStates_state_first
* @attr ref android.R.styleable#DrawableStates_state_middle
* @attr ref android.R.styleable#DrawableStates_state_last
* @attr ref android.R.styleable#DrawableStates_state_pressed
*/
// use StateSet to initial state array
// store in int[][] mStateSets;
/**
* Extracts state_ attributes from an attribute set.
*
* @param attrs The attribute set.
* @return An array of state_ attributes.
*/
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;
}1
2
3
4
5
6
7
2. onStateChange
```java
View:onTouchEvent()->setPressed()->refreshDrawableState()->drawableStateChange()->setState()
1 | //View |
1 |
|
总结
展示
DrawableContainer 子类分2类:一类主动刷新的,如AnimationDrawable,通过scheduleDrawable的方式控制每个drawable的展示时间,由View的scheduleDrawable控制流程,由自身的nextFrame控制展示的内容;另一类是被动刷新的,如stateListDrawable,由View保存并控制状态变脸,由自身在onStateChange中找出之前inflater保存的 mStateSets[][]最匹配drawable
生成
- 通过updateStateFromTypeArray获得外层属性
- 通过inflateChildrenElement填充Drawable数组
- 自身的state内部类保存选择drawable条件的相关数据,DrawableContainer的state保存所有drawable数组以及显示的bounds,level等参数