可滑动的ToggleButton(开关)

时间:2021-04-20 05:19:23

2013-12-28 17:25:01

网上看到一篇关于可滑动的ToogleButton的文章,有代码,觉得挺好,但是不符合我的要求,因此在他的代码基础上改了一些。(作者看到了勿喷啊,实在找不到原文了,只好把代码下载地址贴出来。)

源码下载地址: http://download.csdn.net/detail/zshq280017423/4240703

先来两张效果图:

可滑动的ToggleButton(开关)

可滑动的ToggleButton(开关)

然后上代码:

最主要的类是SlipButton.java

 package com.util;

 import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener; import com.view.SlipButton.R; public class SlipButton extends View implements OnTouchListener { private float DownX, NowX;// 按下时的x,当前的x
private float btn_on_left = ;
private float btn_off_left = ; private boolean NowChoose = false;// 记录当前按钮是否打开,true为打开,flase为关闭
private boolean isChecked;
private boolean OnSlip = false;// 记录用户是否在滑动的变量
private boolean isChgLsnOn = false; private OnChangedListener ChgLsn;
private Bitmap bg_on;
private Bitmap bg_off;
private Bitmap slip_btn; public SlipButton(Context context) {
super(context);
init();
} public SlipButton(Context context, AttributeSet attrs) {
super(context, attrs);
init();
} private void init() { // 初始化
bg_on = BitmapFactory.decodeResource(getResources(),
R.drawable.split_left_1);
bg_off = BitmapFactory.decodeResource(getResources(),
R.drawable.split_right_1);
slip_btn = BitmapFactory.decodeResource(getResources(),
R.drawable.split_1); btn_off_left = bg_off.getWidth() - slip_btn.getWidth(); setOnTouchListener(this); // 设置监听器,也可以直接复写OnTouchEvent
} @Override
protected void onDraw(Canvas canvas) {// 绘图函数
super.onDraw(canvas); Matrix matrix = new Matrix();
Paint paint = new Paint();
float x; if (NowX < (bg_on.getWidth() / )) { // 滑动到前半段与后半段的背景不同,在此做判断
x = NowX - slip_btn.getWidth() / ;
canvas.drawBitmap(bg_off, matrix, paint);// 画出关闭时的背景
} else {
x = bg_on.getWidth() - slip_btn.getWidth() / ;
canvas.drawBitmap(bg_on, matrix, paint);// 画出打开时的背景
} if (OnSlip) {// 是否是在滑动状态,
if (NowX >= bg_on.getWidth()) {// 是否划出指定范围,不能让游标跑到外头,必须做这个判断
x = bg_on.getWidth() - slip_btn.getWidth() / ;// 减去游标1/2的长度...
} else if (NowX < ) {
x = ;
} else {
x = NowX - slip_btn.getWidth() / ;
}
} else {// 非滑动状态
if (NowChoose) {// 根据现在的开关状态设置画游标的位置
x = btn_off_left;
canvas.drawBitmap(bg_on, matrix, paint);// 初始状态为true时应该画出打开状态图片
} else {
x = btn_on_left;
}
}
if (isChecked) {
canvas.drawBitmap(bg_on, matrix, paint);
x = btn_off_left;
isChecked = !isChecked;
} if (x < ) {// 对游标位置进行异常判断...
x = ;
} else if (x > bg_on.getWidth() - slip_btn.getWidth()) {
x = bg_on.getWidth() - slip_btn.getWidth();
}
canvas.drawBitmap(slip_btn, x, , paint);// 画出游标. } public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {// 根据动作来执行代码
case MotionEvent.ACTION_DOWN:// 按下
if (event.getX() > bg_on.getWidth()
|| event.getY() > bg_on.getHeight()) {
return false;
}
OnSlip = true;
DownX = event.getX();
NowX = DownX;
break; case MotionEvent.ACTION_MOVE:// 滑动
Log.d("David", "event.getX = " + event.getX());
Log.d("David", "event.getY = " + event.getY());
NowX = event.getX();
boolean LastChoose = NowChoose; if (NowX >= (bg_on.getWidth() / )) {
NowChoose = true;
} else {
NowChoose = false;
} if (isChgLsnOn && (LastChoose != NowChoose)) { // 如果设置了监听器,就调用其方法..
ChgLsn.OnChanged(NowChoose);
}
break; case MotionEvent.ACTION_UP:// 松开
OnSlip = false;
break;
default:
}
invalidate();// 重画控件
return true;
} public void SetOnChangedListener(OnChangedListener l) {// 设置监听器,当状态修改的时候
isChgLsnOn = true;
ChgLsn = l;
} public interface OnChangedListener {
abstract void OnChanged(boolean CheckState);
} public void setCheck(boolean isChecked) {
this.isChecked = isChecked;
NowChoose = isChecked;
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int measuredHeight = measureHeight(heightMeasureSpec);
int measuredWidth = measureWidth(widthMeasureSpec);
setMeasuredDimension(measuredWidth, measuredHeight);
} private int measureHeight(int measureSpec) { /*int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec); // Default size if no limits are specified. int result = 500;
if (specMode == MeasureSpec.AT_MOST){
// Calculate the ideal size of your
// control within this maximum size.
// If your control fills the available
// space return the outer bound. result = specSize;
} else if (specMode == MeasureSpec.EXACTLY){
// If your control can fit within these bounds return that value.
result = specSize;
}*/
return bg_on.getHeight();
} private int measureWidth(int measureSpec) { /*int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec); // Default size if no limits are specified.
int result = 500;
if (specMode == MeasureSpec.AT_MOST){
// Calculate the ideal size of your control
// within this maximum size.
// If your control fills the available space
// return the outer bound.
result = specSize;
} else if (specMode == MeasureSpec.EXACTLY){
// If your control can fit within these bounds return that value.
result = specSize;
}*/
return bg_on.getWidth();
}
}

代码比较简单,而且注释比较详细。

说一些几个问题:

1. onTouch()方法中得到的event.getX()和event.getY()是什么值?取值范围是多少?

刚开始我以为不管咱们的SlipButton放在什么位置,得到的event.getX()值因该是相对屏幕的坐标值,~~其实是错误的,event.getX()只有你的触摸点在当前SlipButton view的布局范围之内才会取到值的。但是值的范围可不仅仅是你的SlipButton view的大小哦,因为一旦你的触摸点在view范围之内触摸到,那么触摸点就可以移到View之外的任何地方了,所以取值范围应该是全屏哦,因此我们在代码里做了如下判断:

 if (event.getX() > bg_on.getWidth()
|| event.getY() > bg_on.getHeight()) {
return false;
}

2. measureWidth()和measureHeight()为什么会返回一个固定值?

首先根据用途,我们自定义的SlipButton View根本没有必要去由调用者调整大小,因为这个ToggleButton本身就是起到开关作用的,在应用中应该是一致的,所以我让这两个方法返回固定值。关于onMeasure()方法根详细的描述,请看我的另一篇文章:http://www.cnblogs.com/wlrhnh/p/3479928.html

下载源码,请猛戳这里。