1、原理和效果图
总共有两张图片,一张背景图片,一张遮罩图片。背景图片有开关字样,通过遮住一个字来实现开关按钮。继承自View控件,通过Canvas和Paint结合来实现图片的绘制。
通过canvas的drawBitmap方法和距离左边的位置来绘制图片,调用invalidate方法来不断的更新UI,就可以实现滑动的效果。
public class MyToggleButton extends View implements OnClickListener{
/**
* 做为背景的图片
*/
private Bitmap backgroundBitmap;
/**
* 可以滑动的图片
*/
private Bitmap slideBtn;
private Paint paint;
/**
* 滑动按钮的左边届
*/
private float slideBtn_left;
/**
* 当前开关的状态
* true 为开
*/
private boolean currState = false;
/**
* 判断是否发生拖动,如果拖动了,就不再响应 onclick 事件
*/
private boolean isDrag = false;
/**
* 在代码里面创建对象的时候,使用此构造方法
*/
public MyToggleButton(Context context) {
super(context);
}
/**
* 在布局文件中声名的view,创建时由系统自动调用。
* @param context 上下文对象
* @param attrs 属性集
*/
public MyToggleButton(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
/**
* 初始化
*/
private void initView() {
//初始化图片
backgroundBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.switch_background);
slideBtn = BitmapFactory.decodeResource(getResources(), R.drawable.slide_button);
//初始化 画笔
paint = new Paint();
paint.setAntiAlias(true); // 打开抗矩齿
//添加onclick事件监听
setOnClickListener(this);
}
/*
* view 对象显示的屏幕上,有几个重要步骤:
* 1、构造方法 创建 对象。
* 2、测量view的大小。 onMeasure(int,int);
* 3、确定view的位置 ,view自身有一些建议权,决定权在 父view手中。 onLayout();
* 4、绘制 view 的内容 。 onDraw(Canvas)
*/
/**
* 测量尺寸时的回调方法
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
/**
* 设置当前view的大小
* width :view的宽度
* height :view的高度 (单位:像素)
*/
try {
setMeasuredDimension(backgroundBitmap.getWidth(),backgroundBitmap.getHeight());
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
/**
* 绘制当前view的内容
*/
protected void onDraw(Canvas canvas) {
// super.onDraw(canvas);
// 绘制 背景
/*
* backgroundBitmap 要绘制的图片
* left 图片的左边届
* top 图片的上边届
* paint 绘制图片要使用的画笔
*/
canvas.drawBitmap(backgroundBitmap, 0, 0, paint);
//绘制 可滑动的按钮
canvas.drawBitmap(slideBtn, slideBtn_left, 0, paint);
}
@Override
/**
* onclick 事件在View.onTouchEvent 中被解析。
* 系统对onclick 事件的解析,过于简陋,只要有down 事件 up 事件,系统即认为 发生了click 事件
*
*/
public void onClick(View v) {
if(!isDrag){ //如果没有拖动,才执行改变状态的动作
currState = !currState;
flushState();
}
}
/**
* down 事件时的x值
*/
private int firstX;
/**
* touch 事件的上一个x值
*/
private int lastX;
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
firstX = lastX =(int) event.getX();
isDrag = false;
break;
case MotionEvent.ACTION_MOVE:
//判断是否发生拖动
if(Math.abs(event.getX()-firstX)>5){
isDrag = true;
}
//计算 手指在屏幕上移动的距离
int dis = (int) (event.getX() - lastX);
//将本次的位置 设置给lastX
lastX = (int) event.getX();
//根据手指移动的距离,改变slideBtn_left 的值
slideBtn_left = slideBtn_left+dis;
break;
case MotionEvent.ACTION_UP:
//在发生拖动的情况下,根据最后的位置,判断当前开关的状态
if (isDrag) {
int maxLeft = backgroundBitmap.getWidth() - slideBtn.getWidth(); // slideBtn左边届最大值
/*
* 根据 slideBtn_left 判断,当前应是什么状态
*/
if (slideBtn_left > maxLeft / 2) { // 此时应为 打开的状态
currState = true;
} else {
currState = false;
}
flushState();
}
break;
}
flushView();
return true;
}
/**
* 刷新当前状态
*/
private void flushState() {
if(currState){
slideBtn_left = backgroundBitmap.getWidth()-slideBtn.getWidth();
}else{
slideBtn_left = 0;
}
mToggleSwitch.openOrClose(currState);
flushView();
}
/**
* 刷新当前视力
*/
private void flushView() {
//对 slideBtn_left 的值进行判断 ,确保其在合理的位置 即 0<=slideBtn_left <= maxLeft
int maxLeft = backgroundBitmap.getWidth()-slideBtn.getWidth(); // slideBtn 左边届最大值
//确保 slideBtn_left >= 0
slideBtn_left = (slideBtn_left>0)?slideBtn_left:0;
//确保 slideBtn_left <=maxLeft
slideBtn_left = (slideBtn_left<maxLeft)?slideBtn_left:maxLeft;
/*
* 刷新当前视图 导致 执行onDraw执行
*/
invalidate();
}
public void setmToggleSwitch(ToggleSwitch mToggleSwitch) {
this.mToggleSwitch = mToggleSwitch;
}
private ToggleSwitch mToggleSwitch;
public interface ToggleSwitch {
/**
* true 为开 ,false 为关
* @param open
*/
public void openOrClose(boolean open);
}
}
2、布局文件
<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"
tools:context=".MainActivity" >
<com.example.MyToggleButton
android:id="@+id/my_toggle_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
/>
</RelativeLayout>
3、调用
myToggleButton = (MyToggleButton) findViewById(R.id.my_toggle_btn);
myToggleButton.setmToggleSwitch(new ToggleSwitch() {
@Override
public void openOrClose(boolean open) {
if (open) {
Toast.makeText(MainActivity.this, "打开", Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(MainActivity.this, "关闭", Toast.LENGTH_SHORT).show();
}
}
});