创建自定义控件根据控件的需求主要有一下几种方案:
1、如果现有的控件已经具备了你想要的功能,那么修改或者扩展它们的外观或行为。通过override事件处理函数和onDraw,仍然调用父类的方法,在你定制控件时就不需要重新实现它的功能。
2、组合控件来创建原子的、可重用的widgets,它会引发一些相互关联的控件的功能性发生变化。例如,你可以创建一个下拉的combobox,通过组合一个TextView和一个Button,当点击Button时,显示一个浮动的ListView。
3、当你需要一个完全不同的界面,而不能通过改变和组合现有的控件来达到的时候,选择创建一个全新的控件。
CheckableImageButton
Android自带的ImageButton是不支持像CheckBox, RadioButton拥有的check(选中)状态的,Android提供的组件还算丰富,我们能用这些组件快速开发一个简单的应用程序,但在比较复杂项目中就会感觉捉襟见肘了,但幸好在Android系统上开发者能自由定制自己的UI组件,来弥补现有组件的不足。
创建一个CheckableImageButton需要做的工作:
1. 添加资源文件 res/values/attrs.xml,添加自定义组件CheckableImageButton,声明is_checked和personality属性,以后就能通过这两个属性在XML文件中指定相关属性的值。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- custom checkable imageButton -->
<declare-styleable name="CheckableImageButton">
<attr name="is_checked" format="boolean"/>
<attr name="personality">
<enum name="radio" value="0"/>
<enum name="check" value="1"/>
</attr>
</declare-styleable>
</resources>
2. 创建ImageButton的背景,使用Selector Drawable。
<?xml version="1.0" encoding="UTF-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:jimi="http://schemas.android.com/apk/res/com.seclock.jimi">
<item android:state_pressed="true" android:drawable="@drawable/transparent" />
<item jimi:is_checked="true" android:drawable="@drawable/checkable_image_btn_state_checked" />
<item android:drawable="@drawable/transparent" />
</selector>
jimi:is_checked="true"是(1)中自定义的属性
3. 创建布局文件,可以指定自定义属性的值。
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
xmlns:jimi="http://schemas.android.com/apk/res/com.seclock.jimi"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<you.package.name.CheckableImageButton
android:layout_width="fill_parent"
android:layout_height="fill_parent"
jimi:is_check="true"
jimi:personality="radio"
/>
</LinearLayout>
4. 创建自定义类,继承至ImageButton。让自定义类有ImageButton的所有功能。在构造方法中通过TypedArray读取自定义属性的值。
5. 实现Checkable接口。
接口如下:
/**
* Change the checked state of the view
*
* @param checked The new checked state
*/
void setChecked(boolean checked);
/**
* @return The current checked state of the view
*/
boolean isChecked();
/**
* Change the checked state of the view to the inverse of its current state
*
*/
void toggle();
自定义类全部代码:
public class CheckableImageButton extends ImageButton implements Checkable {
private static final String DEBUG_TAG = CheckableImageButton.class
.getSimpleName();
private static final int PERSONALITY_RADIO_BUTTON = 0;
private static final int PERSONALITY_CHECK_BUTTON = 1;
private static final int[] CHECKED_STATE_SET = { R.attr.checked };
private boolean mChecked;
private int personality;
private boolean mBroadcasting;
private OnCheckedChangeListener mOnCheckedChangeListener;
public CheckableImageButton(Context context) {
super(context);
}
public CheckableImageButton(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
public CheckableImageButton(Context context, AttributeSet attrs) {
super(context, attrs);
// 获取自定义属性的值
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.checkedImageButton);
mChecked = a.getBoolean(R.styleable.checkedImageButton_checked, false);
personality = a.getInt(R.styleable.checkedImageButton_personality,
PERSONALITY_RADIO_BUTTON);
setChecked(mChecked);
// Give back a previously retrieved StyledAttributes, for later re-use.
a.recycle();
}
@Override
public boolean performClick() {
// 拦截点击事件处理check
if (personality == PERSONALITY_CHECK_BUTTON) {
toggle();
} else if (personality == PERSONALITY_RADIO_BUTTON) {
setChecked(true);
}
return super.performClick();
}
@Override
public void setChecked(boolean checked) {
Log.d(DEBUG_TAG, "setChecked:" + checked);
if (mChecked != checked) {
mChecked = checked;
// 状态改变刷新视图
refreshDrawableState();
}
if (mBroadcasting) {
return;
}
mBroadcasting = true;
if (null != mOnCheckedChangeListener) {
mOnCheckedChangeListener.onCheckedChanged(this, mChecked);
}
mBroadcasting = false;
}
@Override
public boolean isChecked() {
return mChecked;
}
@Override
public void toggle() {
setChecked(!mChecked);
}
@Override
public int[] onCreateDrawableState(int extraSpace) {
int[] states = super.onCreateDrawableState(extraSpace + 1);
if (isChecked()) {
mergeDrawableStates(states, CHECKED_STATE_SET);
}
return states;
}
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
// invalidate();
}
public static interface OnCheckedChangeListener {
/**
* interface definition for a callback to be invoked when the checked
* image button changed
*
* @param button
* @param isChecked
* */
public void onCheckedChanged(CheckableImageButton button,
boolean isChecked);
}
/**
* @Title: 保存状态.
* @author Anders
*/
static class SaveState extends BaseSavedState {
boolean checked;
public SaveState(Parcel in) {
super(in);
checked = (Boolean) in.readValue(null);
}
public SaveState(Parcelable superState) {
super(superState);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeValue(checked);
}
public static final Parcelable.Creator<SaveState> CREATOR = new Creator<CheckableImageButton.SaveState>() {
@Override
public SaveState[] newArray(int size) {
return new SaveState[size];
}
@Override
public SaveState createFromParcel(Parcel source) {
return createFromParcel(source);
}
};
}
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superParcelable = super.onSaveInstanceState();
SaveState ss = new SaveState(superParcelable);
ss.checked = isChecked();
return ss;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
SaveState ss = (SaveState) state;
super.onRestoreInstanceState(ss.getSuperState());
setChecked(ss.checked);
}
public OnCheckedChangeListener getmOnCheckedChangeListener() {
return mOnCheckedChangeListener;
}
public void setmOnCheckedChangeListener(
OnCheckedChangeListener mOnCheckedChangeListener) {
this.mOnCheckedChangeListener = mOnCheckedChangeListener;
}
}