效果图
思考
因为是自定义view,不是viewGroup,所以在进行了onMeasure()的测量方法之后,所以不需要onLayout方法,但是必须实现onDraw()方法
程序如何设计呢?
- 需要提供设置ToggleButton开关背景图片的方法
- 需要提供设置ToggleButton滑动背景图片的方法
- 需要提供设置ToggleButton开关状态的方法
- 需要提供设置ToggleButton状态改变的监听器
如何实现开关随手指滑动的呢?这里需要重写onTouchEvent()方法。
如果用户正在滑动,则去获取当前手指滑动位置的X坐标currentX,然后去动态的绘制滑动的图片位置。具体判断如下图:
如果用户抬起手指,则去判断currentX与背景图片中心点的X坐标,然后去设置开关的状态。具体判断如下图:
控件画好了之后,得提供开关状态改变的接口,供外界使用。这里要注意:仅当状态改变时,才调用。所以调用之前要加个判断。
步骤
1. 布局:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.example.togglebutton.MyToggleButton
android:id="@+id/my_toggle_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
</RelativeLayout>
2. 主函数(调用上面布局显示自定义控件的函数)
public class MainActivity extends Activity {
private MyToggleButton myToggleButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myToggleButton = (MyToggleButton) this.findViewById(R.id.my_toggle_button);
myToggleButton.setSwitchBackgroundRes(R.drawable.switch_background);//设置开关的背景图片
myToggleButton.setSlideBackgroundRes(R.drawable.slide_button_background);//设置滑动的图片
myToggleButton.setToggleState(ToggleState.Open);//设置开关的状态
myToggleButton.setOnToggleStateChangeListener(new OnToggleStateChangeListener() {
@Override
public void onToggleStateChange(ToggleState state) {
Toast.makeText(MainActivity.this, state==ToggleState.Open?"开启":"关闭", 0).show();
}
});
}
}
3. 自定义的ToggleButton
public class MyToggleButton extends View {
private ToggleState toggleState = ToggleState.Open; //当前开关的状态
private Bitmap switchBg; //开关的背景图片
private Bitmap slideBg; //滑动的图片
private int currentX; //当前手指的X坐标
private boolean isSliding = false; //是否处于滑动状态
public MyToggleButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public MyToggleButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyToggleButton(Context context) {
super(context);
}
public enum ToggleState{
Open,Close
}
/**
* 设置开关的背景图片
* @param switchBackground
*/
public void setSwitchBackgroundRes(int switchBackground) {
switchBg = BitmapFactory.decodeResource(getResources(), switchBackground);
}
/**
* 设置滑动块的图片
* @param slideButtonBackground
*/
public void setSlideBackgroundRes(int slideButtonBackground) {
slideBg = BitmapFactory.decodeResource(getResources(), slideButtonBackground);
}
/**
* 设置开关的状态
* @param state
*/
public void setToggleState(ToggleState state) {
toggleState = state;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(switchBg.getWidth(), switchBg.getHeight());
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制背景图片 left top 都是相对于自身的坐标系 不是屏幕坐标系
canvas.drawBitmap(switchBg, 0, 0, null);
if(isSliding){ //绘制手指滑动时的滑动块图片
int left = currentX - slideBg.getWidth()/2;
if(left < 0)
left = 0;
if(left > switchBg.getWidth() - slideBg.getWidth()){
left = switchBg.getWidth() - slideBg.getWidth();
}
canvas.drawBitmap(slideBg, left, 0, null);
}else{ //绘制停止滑动时的滑动块图片
if(toggleState == ToggleState.Open){
canvas.drawBitmap(slideBg, switchBg.getWidth() - slideBg.getWidth(), 0, null);
}else{
canvas.drawBitmap(slideBg, 0, 0, null);
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
currentX = (int) event.getX();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isSliding = true;
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
isSliding = false;
if(currentX < switchBg.getWidth()/2){ //滑块置为左边,关闭状态
if(toggleState != ToggleState.Close){
toggleState = ToggleState.Close;
if(listener != null){
listener.onToggleStateChange(toggleState);
}
}
}else{ //滑块置为右边,打开状态
if(toggleState != ToggleState.Open){
toggleState = ToggleState.Open;
if(listener != null){
listener.onToggleStateChange(toggleState);
}
}
}
break;
}
invalidate();
return true;
}
private OnToggleStateChangeListener listener;
public void setOnToggleStateChangeListener(OnToggleStateChangeListener listener){
this.listener = listener;
}
public interface OnToggleStateChangeListener{
void onToggleStateChange(ToggleState state);
}
4. 注意:
设置的开关的背景图片以及滑动块图片在绘制时都需要转换成bitmap方便测量和绘制
onTouchEvent()方法执行结束前,需要调用invalidate()方法去重新onDraw(); 并且return true表示已经处理。