1. 写个类继承View
首先写一个类继承View或ViewGroup,并实现其构造方法,View类的构造方法有三种并且有不同的作用,作用分别为:
/**
* 用于代码创建控件
* @param context
*/
public ToggleView(Context context) {
super(context);
}
/**
* 用于在xml里使用, 可指定自定义属性
* @param context
* @param attrs
*/
public ToggleView(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* 用于在xml里使用, 可指定自定义属性, 如果指定了样式, 则走此构造函数
* @param context
* @param attrs
* @param defStyle
*/
public ToggleView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
2. 拷贝包含包名的全路径到xml中
在布局文件中使用该自定义控件,空间名字为“包含包名的全路径”,如:
<com.app.demo.ToggleView
android:id="@+id/toggleView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"/>
3. 界面中找到该控件, 设置初始信息
在未声明自定义属性之前,可在主界面类中找到该控件并设置一些基本信息,在声明了自定义属性后可将其注释直接在xml布局中设置信息:
public class MainActivity extends Activity {
private ToggleView toggleView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toggleView = (ToggleView) findViewById(R.id.toggleView);
toggleView.setSwitchBackgroundResource(R.drawable.switch_background); //设置开关背景
toggleView.setSlideButtonResource(R.drawable.slide_button);//设置滑块背景
toggleView.setSwitchState(true);//设置开关状态
});
}
}
上述三个方法在定义控件的类中编写:
/**
* 设置背景图
* @param switchBackground
*/
public void setSwitchBackgroundResource(int switchBackground) {
switchBackgroupBitmap = BitmapFactory.decodeResource(getResources(), switchBackground);
}
/**
* 设置滑块图片资源
* @param slideButton
*/
public void setSlideButtonResource(int slideButton) {
slideButtonBitmap = BitmapFactory.decodeResource(getResources(), slideButton);
}
/**
* 设置开关状态
* @param mSwitchState
*/
public void setSwitchState(boolean mSwitchState) {
this.mSwitchState = mSwitchState;
}
4. 根据需求绘制界面内容
- Android 的界面绘制流程
基本操作由三个函数完成:measure()、layout()、draw(),其内部又分别包含了onMeasure()、onLayout()、onDraw()三个子方法。
测量 摆放 绘制
measure -> layout -> draw
| | |
onMeasure -> onLayout -> onDraw 重写这些方法, 实现自定义控件
都在onResume()之后执行
View流程
onMeasure() (在这个方法里指定自己的宽高) -> onDraw() (绘制自己的内容)
ViewGroup流程
onMeasure() (指定自己的宽高, 所有子View的宽高)-> onLayout() (摆放所有子View) -> onDraw() (绘制内容)
5. 响应用户的触摸事件
重写触摸事件, 响应用户的触摸.
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
break;
default:
break;
}
// 重绘界面
invalidate(); // 会引发onDraw()被调用, 里边的变量会重新生效.界面会更新
return true; // 消费了用户的触摸事件, 才可以收到其他的事件.
}
6. 创建一个状态更新监听
定义接口,在主界面监听
在attrs.xml声明节点declare-styleable
在xml配置声明的属性/ 注意添加命名空间
命名方式:xmlns: 自定义名字=”http://schemas.android.com/apk/res/包名”
<com.app.demo.ToggleView xmlns:uview="http://schemas.android.com/apk/res/com.app.demo"
android:id="@+id/toggleView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
uview:switch_background="@drawable/switch_background"
uview:slide_button="@drawable/slide_button"
uview:switch_state="true"
android:layout_centerInParent="true"/>
4. 在构造函数中获取并使用
// 获取配置的自定义属性
String namespace = "http://schemas.android.com/apk/res/com.app.demo";
int switchBackgroundResource = attrs.getAttributeResourceValue(namespace , "switch_background", -1);
int slideButtonResource = attrs.getAttributeResourceValue(namespace , "slide_button", -1);
mSwitchstate = attrs.getAttributeBooleanValue(namespace, "switch_state", false);
setSwitchBackgroundResource(switchBackgroundResource);
setSlideButtonResource(slideButtonResource);