在IPhone中,滑动开关控件很常见,并且效果也很好,可是在Android平台下,却没有自带的这样的控件,仅仅有功能类似的ToggleButton控件。本篇文章主要介绍自己定义的滑动开关控件的实现与使用。在实现的过程中,也參考了其它类似自己定义控件的实现,同一时候对代码进行了优化。
首先看实现的效果图
以下解说这个自己定义控件怎样实现
/** * 滑动控件 * * @Time 2014-6-17 下午2:35:17 */ public class SlipSwitch extends View implements OnTouchListener { // 开关开启时的背景,关闭时的背景,滑动button private Bitmap switch_on_bg, switch_off_bg, slip_Btn; // 是否正在滑动 private boolean isSlipping = false; // 当前开关状态,true为开启,false为关闭 private boolean isSwitchOn = false; // 手指按下时的水平坐标X,当前的水平坐标X private float previousX, currentX; // 开关监听器 private OnSwitchListener onSwitchListener; // 是否设置了开关监听器 private boolean isSwitchListenerOn = false; // 矩阵 private Matrix matrix = new Matrix(); // 画笔 private Paint paint = new Paint(); // 滑动button的左边坐标 private float left_SlipBtn; // 松开前开关的状态 private boolean previousSwitchState; public SlipSwitch(Context context) { super(context); init(); } public SlipSwitch(Context context, AttributeSet attrs) { super(context, attrs); init(); } //初始化 protected void init() { setOnTouchListener(this); setSwitchState(true); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 手指滑动到左半边的时候表示开关为关闭状态,滑动到右半边的时候表示开关为开启状态 if (currentX < (switch_on_bg.getWidth() / 2)) { canvas.drawBitmap(switch_off_bg, matrix, paint); } else { canvas.drawBitmap(switch_on_bg, matrix, paint); } // 推断当前是否正在滑动 if (isSlipping) { if (currentX > switch_on_bg.getWidth()) { left_SlipBtn = switch_on_bg.getWidth() - slip_Btn.getWidth(); } else { left_SlipBtn = currentX - slip_Btn.getWidth() / 2; } } else { // 依据当前的开关状态设置滑动button的位置 if (isSwitchOn) { left_SlipBtn = switch_off_bg.getWidth(); } else { left_SlipBtn = 0; } } // 对滑动button的位置进行异常推断 if (left_SlipBtn < 0) { left_SlipBtn = 0; } else if (left_SlipBtn > switch_on_bg.getWidth() - slip_Btn.getWidth()) { left_SlipBtn = switch_on_bg.getWidth() - slip_Btn.getWidth(); } canvas.drawBitmap(slip_Btn, left_SlipBtn, 0, paint); } // 告诉父控件,要占多大的空间 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(switch_on_bg.getWidth(), switch_on_bg.getHeight()); } @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { // 滑动 case MotionEvent.ACTION_MOVE: currentX = event.getX(); break; // 按下 case MotionEvent.ACTION_DOWN: isSlipping = true; previousX = event.getX(); currentX = previousX; break; // 松开 case MotionEvent.ACTION_UP: isSlipping = false; previousSwitchState = isSwitchOn; if (event.getX() >= (switch_on_bg.getWidth() / 2)) { isSwitchOn = true; } else { isSwitchOn = false; } // 假设设置了监听器,则调用此方法 if (isSwitchListenerOn && (previousSwitchState != isSwitchOn)) { onSwitchListener.onSwitched(isSwitchOn); } break; } // 又一次绘制控件 invalidate(); return true; } protected void setImageResource(int switchOnBkg, int switchOffBkg, int slipBtn) { switch_on_bg = BitmapFactory .decodeResource(getResources(), switchOnBkg); switch_off_bg = BitmapFactory.decodeResource(getResources(), switchOffBkg); slip_Btn = BitmapFactory.decodeResource(getResources(), slipBtn); } protected void setSwitchState(boolean switchState) { isSwitchOn = switchState; } protected boolean getSwitchState() { return isSwitchOn; } protected void updateSwitchState(boolean switchState) { isSwitchOn = switchState; invalidate(); } public void setOnSwitchListener(OnSwitchListener listener) { onSwitchListener = listener; isSwitchListenerOn = true; } // 监听器接口 public interface OnSwitchListener { abstract void onSwitched(boolean isSwitchOn); } }
在这个实现过程中,有几个方法至关重要。
一个是onMeasure(),这种方法主要是告诉父控件,自己定义控件要占多大的控件,我们把背景图片的宽高设置就可以。
另外一个onDraw(),这种方法负责自己定义界面的绘制,当手指按下、滑动、松开的时候,这种方法都须要对更改后的界面进行又一次的绘制。
最后一个方法便是onTouch(),由于自己定义控件实现了OnTouchListener接口,所以要重写这种方法。当手指在屏幕点击和滑动的时候,就会出发这个事件,我们须要依据用户操作的不同,对按下、放开、滑动等事件,进行不一样的处理。可是不管怎样处理,在方法的最后,我们都要调用invalidate();方法,对界面进行刷新,我们能够看到这种方法的介绍
Invalidate the whole view. If the view is visible, onDraw(android.graphics.Canvas) will be called at some point in the future. This must be called from a UI thread. To call from a non-UI thread,
call postInvalidate().
意思就是说,假设控件可见,我们在调用这种方法之后,系统会调用onDraw方法进行界面的刷新,并且这种方法必须在主线程调用,假设在非主线程想完毕界面刷新的功能,我们能够调用postInvalidate()这种方法实现。
并且onTouch()的返回值为true,我们能够看一下这种方法的介绍
好了,通过这几个方法,我们就实现了一个简单的自己定义的滑动开关控件,以下我们看一下怎样使用这个自己定义的控件。
布局文件
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="@android:color/white" android:gravity="center" android:orientation="vertical" > <edu.qust.SlipSwitch android:id="@+id/main_myslipswitch" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
Activity中的代码
public class MainActivity extends Activity { private SlipSwitch slipswitch; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); slipswitch = (SlipSwitch) findViewById(R.id.main_myslipswitch); slipswitch.setImageResource(R.drawable.bkg_switch, R.drawable.bkg_switch, R.drawable.btn_slip); slipswitch.setOnSwitchListener(new OnSwitchListener() { @Override public void onSwitched(boolean isSwitchOn) { if (isSwitchOn) { Toast.makeText(MainActivity.this, "开关已经开启", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(MainActivity.this, "开关已经关闭", Toast.LENGTH_SHORT).show(); } } }); } }
使用很easy,只是多介绍了
使用到的素材文件