在 Android 开发中,View 和 Drawable 之间关系十分紧密,例如我们经常用 Drawable 作为一个 View 的背景。View 常常会有状态的改变,例如被按下、例如禁用,而不同的状态下 Drawable 也常有不同的表现。今天要探索的问题是 View 的状态改变是如何影响 Drawable 的表现的。
以下将简单介绍我们平时如何在 View 上使用 Drawable,做到在不同状态下表现不一样。接着分析系统源码探索其中的原理。最后以系统的控件和自定义控件 2 个例子来验证和实践在 View 中自定义状态的做法。
本文的源码分析基于 Android API Level 23,并省略掉部分与本文关系不大的代码。
在代码中加入了个人对源码的理解,以注释形式呈现。
本文最后的 DEMO 项目源码托管到
Github
上。
1
2 3 4 5 6
|
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="#CCCCCC" android:state_enabled="false"/> <item android:drawable="#666666" android:state_pressed="true"/> <item android:drawable="#999999"/> </selector>
|
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
|
public void setChecked(boolean checked) { if (mChecked != checked) { mChecked = checked; refreshDrawableState(); } } @Override protected void drawableStateChanged() { super.drawableStateChanged(); if (mButtonDrawable != null) { int[] myDrawableState = getDrawableState(); mButtonDrawable.setState(myDrawableState); invalidate(); } } private static final int[] CHECKED_STATE_SET = { R.attr.state_checked }; @Override protected int[] onCreateDrawableState(int extraSpace) { final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); if (isChecked()) { mergeDrawableStates(drawableState, CHECKED_STATE_SET); } return drawableState; }
|
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 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
|
public class CheckableLinearLayout extends LinearLayout implements Checkable { private boolean mIsChecked = false; private Drawable mCheckboxDrawable; private static final int[] CHECKED_STATE_SET = { android.R.attr.state_checked }; public CheckableLinearLayout(Context context) { super(context); init(); } public CheckableLinearLayout(Context context, AttributeSet attrs) { super(context, attrs); init(); } public CheckableLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { mCheckboxDrawable = getResources().getDrawable(R.drawable.qmui_s_dialog_check_mark); setWillNotDraw(false); } @Override protected void drawableStateChanged() { super.drawableStateChanged(); if (mCheckboxDrawable != null) { int[] drawableState = getDrawableState(); mCheckboxDrawable.setState(drawableState); invalidate(); } } @Override protected int[] onCreateDrawableState(int extraSpace) { final int[] drawableState = super.onCreateDrawableState(extraSpace + CHECKED_STATE_SET.length); if (isChecked()) { mergeDrawableStates(drawableState, CHECKED_STATE_SET); } return drawableState; } @Override public void setChecked(boolean checked) { if (mIsChecked != checked) { mIsChecked = checked; refreshDrawableState(); } } @Override public boolean isChecked() { return mIsChecked; } @Override public void toggle() { setChecked(!isChecked()); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mCheckboxDrawable != null) { int left = QMUIDisplayHelper.dpToPx(5); mCheckboxDrawable.setBounds(left, getPaddingTop(), left + mCheckboxDrawable.getIntrinsicWidth(), getPaddingTop() + mCheckboxDrawable.getIntrinsicHeight()); mCheckboxDrawable.draw(canvas); } }
|