/**
* SwitchButton开关控件早已经非常流行。有各种各样的样式,SwitchButton开关控件一般用于app软件设置那里,控制缓存、声音、提示、下载等等。是具有很好的UI体验以及用户的习惯性
核心代码,实现Touch事件
得到系统默认点击延迟、触摸滑动界值
2、绘制工作
这里用到了图像混合Xfermode,更多知识详情请看:http://blog.csdn.net/weiwozhiyi/article/details/50749622
ACTION_MOVE:
ACTION_UP:
这个自定义CheckBox复写了performClick方法
4、为滑动增加动态效果(这里通过速度来进行改变位置)
结束,炫酷的SwitchButton就这样简易的完成。
示例:
drawable.xml
当然也可以在代码中进行设置
它里面还封装了,这样有趣的方法
* SwitchButton开关控件早已经非常流行。有各种各样的样式,SwitchButton开关控件一般用于app软件设置那里,控制缓存、声音、提示、下载等等。是具有很好的UI体验以及用户的习惯性
*/
Part 1、使用自定义View来实现SwitchButton
步骤:
- /*
- 1、准备好图片资源:一个背景图和一个滑块,并在onDraw里面绘制出来
- 2、在onMeasure里面将自定义View设置为背景图片的大小
- 3、为自定义View设置点击事件,通过根据当前的状态来设置偏移量,别忘记调用invalidate进行刷新哦
- 4、为自定义View设置触摸事件,这里注意的是一定要在上面调用super.onTouchEvent方法不然onClick就不会执行,
- 这说明View的onClick事件是由onTouch事件来调用的,所以这里要进行区分,什么情况调用onTouch,什么情况调用onClick事件
- */
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- super.onTouchEvent(event);//这里要调用这个方法,不然onClick不会被调用
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- firstX = startX = lastX = event.getX();
- isDrag = false;//每次进行触摸开始都要将是否触摸设置为false
- break;
- case MotionEvent.ACTION_MOVE:
- lastX = event.getX();
- float dx = lastX - startX;
- //判断如果偏移量大于5的话将isDrag设置为true,否则设置为false
- if (Math.abs(event.getX() - firstX) > 5) {
- isDrag = true;
- }
- leftDis += dx;
- startX = event.getX();
- break;
- case MotionEvent.ACTION_UP:
- if (isDrag) {//当手指弹起,如果是Drag的状态需要判断当前移动的位置来判断自动将滑块移动到左边或者右边
- if (leftDis > (MAX_MOVE_DISTANCE / 2)) {//滑动到右边
- status = false;
- } else {
- status = true;
- }
- flushStatus();
- }
- break;
- }
- flushLimit();
- return true;
- }
Part 2、使用自定义CheckBox来实现SwitchButton
详情请浏览:http://www.androidchina.net/3160.html这里只是总结一下
查看代码其实大概逻辑和上面的是一样的
步骤
1、准备工作
准备图片
- // 得到图片
- mBottom = BitmapFactory.decodeResource(resources, R.mipmap.bottom);//背景的图片
- mBtnPressed = BitmapFactory.decodeResource(resources, R.mipmap.btn_pressed);//按压时候的滑块
- mBtnNormal = BitmapFactory.decodeResource(resources, R.mipmap.btn_unpressed);//没有按压时候的滑块
- mFrame = BitmapFactory.decodeResource(resources, R.mipmap.frame);//边框
- mMask = BitmapFactory.decodeResource(resources, R.mipmap.mask);//蒙版图片
- // 得到ViewConfiguration类
- mClickTimeout = ViewConfiguration.getPressedStateDuration()
- + ViewConfiguration.getTapTimeout();//点击的延迟
- mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();//得到手指滑动的时候要大于这个值才开始滑动
- @Override
- protected void onDraw(Canvas canvas) {
- canvas.saveLayerAlpha(mSaveLayerRectF, mAlpha, Canvas.MATRIX_SAVE_FLAG
- | Canvas.CLIP_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG
- | Canvas.FULL_COLOR_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
- // 绘制蒙板
- canvas.drawBitmap(mMask, 0, mExtendOffsetY, mPaint);
- //给谁设置谁就是源图,下面的即为目标图,/** [Sa * Da, Sc * Da] */相交的部分显示源图,不想交部分由于Da是为0所以显示透明色
- mPaint.setXfermode(mXfermode);
- // 绘制底部图片
- canvas.drawBitmap(mBottom, mRealPos, mExtendOffsetY, mPaint);
- mPaint.setXfermode(null);
- // 绘制边框
- canvas.drawBitmap(mFrame, 0, mExtendOffsetY, mPaint);
- // 绘制按钮
- canvas.drawBitmap(mCurBtnPic, mRealPos, mExtendOffsetY, mPaint);//这里绘制的是当前的图片
- canvas.restore();
- }
3、实现触摸和点击事件的功能
ACTION_DOWN:
这里代码中调用了这个方法,请求父View不去打断Touch事件,虽然只是请求但更加确保子View能顺利的执行OnTouchEvent
- /**
- * Tries to claim the user's drag motion, and requests disallowing any
- * ancestors from stealing events in the drag.
- * 试图去请求用户拖拽动作,请求不允许父View去打断Touch事件
- */
- private void attemptClaimDrag() {
- mParent = getParent();
- if (mParent != null) {
- mParent.requestDisallowInterceptTouchEvent(true);
- }
- }
- //然后就是对这个位置进行边界处理
- if (mBtnPos >= mBtnOffPos) {
- mBtnPos = mBtnOffPos;
- }
- if (mBtnPos <= mBtnOnPos) {
- mBtnPos = mBtnOnPos;
- }
- //判断是否是打开的状态,如果滑块的位置大于一半
- mTurningOn = mBtnPos > (mBtnOffPos - mBtnOnPos) / 2 + mBtnOnPos;
- //得到真实的位置
- mRealPos = getRealPos(mBtnPos);
- time = event.getEventTime() - event.getDownTime();//事件发生到按压产生事件流的时间差
- if (deltaY < mTouchSlop && deltaX < mTouchSlop && time < mClickTimeout) {
- //如果偏移量小于系统给定的值则认为是一次点击状态
- if (mPerformClick == null) {
- mPerformClick = new PerformClick();
- }
- if (!post(mPerformClick)) {//将这个Runnable添加到消息队列,返回true添加成功,失败返回false
- } else { performClick();
- }
- startAnimation(!mTurningOn);//开始进行动画,在这个发放需要判断是关闭的动画还是开启的动画
- }
- @Override
- public boolean performClick() {
- startAnimation(!mChecked);
- return true;//返回true标志这个ClickListener将被调用
- }
- private void doAnimation() {
- mAnimationPosition += mAnimatedVelocity * FrameAnimationController.ANIMATION_FRAME_DURATION
- / 1000;
- //对当前动画的位置进行限制,超出边界将停止动画
- if (mAnimationPosition <= mBtnOnPos) {//打开状态
- stopAnimation();
- mAnimationPosition = mBtnOnPos;
- setCheckedDelayed(true);
- } else if (mAnimationPosition >= mBtnOffPos) {//当动画的位置超过了背景图的边界
- stopAnimation();//停止动画
- mAnimationPosition = mBtnOffPos;
- setCheckedDelayed(false);
- }
- moveView(mAnimationPosition);
- }
Part 3、Github上提供的SwitchButton
更多详情请查看 github地址:https://github.com/kyleduo/SwitchButton
首先你要添加依赖
- dependencies {
- compile 'com.kyleduo.switchbutton:library:1.4.4'
- }
在xml里面进行设置
- <!--
- app:kswTextOff="Off" 设置关闭时候文字显示
- app:kswTextOn="On" 设置打开时候文字显示
- app:kswFadeBack="false" 默认情况下是true,当设置为true是背景颜色会随着滑动逐渐变浅
- app:kswThumbRadius="2dp" 设置正方形四角的弧的半径
- app:kswBackRadius="2dp" 设置背景图形的四角弧的半径
- app:kswBackMeasureRatio="2.2" 背景图形和滑块的比例
- app:kswTintColor="#FF0000" 滑块的背景
- app:kswBackDrawable="@drawable/" 设置背景的图形,这里也可以使用选择器来动态改变
- app:kswThumbDrawable="@drawable/" 设置滑块的图形,这里也可以使用选择来动态改变
- app:kswAnimationDuration="300" 设置滑动的时长
- android:enabled="false" 设置SwitchButton不可用
- android:checked="true" 设置SwitchButton当前打开状态
- -->
- <com.kyleduo.switchbutton.SwitchButton
- android:id="@+id/sb_custom_miui"
- style="@style/SwitchButtonStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- app:kswBackDrawable="@drawable/miui_back_drawable"
- app:kswBackMeasureRatio="2"
- app:kswThumbDrawable="@drawable/miui_thumb_drawable"
- app:kswThumbHeight="18dp"
- app:kswThumbWidth="18dp"
- />
- <?xml version="1.0" encoding="utf-8"?>
- <selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false" android:state_checked="true">
- <!--不可用但可但是状态下的图形-->
- <shape android:shape="oval">
- <solid android:color="#FFA75E"/>
- <stroke
- android:width="1px"
- android:color="#DB8056"/>
- </shape>
- </item>
- <item android:state_enabled="false">
- <!--不可用的图形-->
- <shape android:shape="oval">
- <solid android:color="#E1E1E1"/>
- <stroke
- android:width="1px"
- android:color="#ECECEC"/>
- </shape>
- </item>
- <item android:state_checked="true">
- <!--可点击的图形-->
- <shape android:shape="oval">
- <solid android:color="#FF7201"/>
- <stroke
- android:width="1px"
- android:color="#DB5517"/>
- </shape>
- </item>
- <item>
- <!--默认情况-->
- <shape android:shape="oval">
- <solid android:color="#E0E0E0"/>
- <stroke
- android:width="1px"
- android:color="#D0D0D0"/>
- </shape>
- </item>
- </selector>
样式有了,再来看看监听事件
- // check in check
- mForceOpenSb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- if (mForceOpenControlSb.isChecked()) {
- toast("Call mForceOpenSb.setChecked(true); in on CheckedChanged");
- mForceOpenSb.setChecked(true);
- }
- }
- });
- /**
- * mToggleSb.toggle(); 正常情况下的开关,可以接受事件
- * mToggleSb.toggleNoEvent(); 正常情况下的开关,不可接受
- * mToggleSb.toggleImmediately(); 立即执行开关,可接受
- * mToggleSb.toggleImmediatelyNoEvent(); 立即执行开关,不可接受
- *
- * mCheckedSb.setChecked(); 手动开关,缓慢执行,可接受
- * mCheckedSb.setCheckedNoEvent(); 缓慢执行,不可接受
- * mCheckedSb.setCheckedImmediately(); 立即执行,可接受
- * mCheckedSb.setCheckedImmediatelyNoEvent(); 立即执行,不可接受
- */