SwitchButton开关控件早已经非常流行。有各种各样的样式,SwitchButton开关控件一般用于app软件设置那里,控制缓存、声音、提示、下载等等。是具有很好的UI体验以及用户的习惯性。那么再下面介绍一个SwitchButton开关控件。并附上源码。

源码下载:点击

一、看实现的效果图


二、自定义SwitchButton

这是一个继承CheckBox的SwitchButton类。来实现做这些动画效果的,首先准备好这些图片,然后canvas绘制控件 的边框、背景、以及按钮。绘制时候加上相应的图片。然后用onTouchEvent这个函数,接受当按下,滑动,松开后的效果。那么大概就出来了。接下来看具体的代码。

package com.org.switchbtn;import com.switchbutton.activity.R;import android.content.Context;import android.content.res.Resources;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.PorterDuff;import android.graphics.PorterDuffXfermode;import android.graphics.RectF;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.ViewConfiguration;import android.view.ViewParent;import android.widget.CheckBox;public class SwitchButton extends CheckBox {    private Paint mPaint;    private ViewParent mParent;    private Bitmap mBottom;    private Bitmap mCurBtnPic;    private Bitmap mBtnPressed;    private Bitmap mBtnNormal;    private Bitmap mFrame;    private Bitmap mMask;    private RectF mSaveLayerRectF;    private PorterDuffXfermode mXfermode;    private float mFirstDownY; // 首次按下的Y    private float mFirstDownX; // 首次按下的X    private float mRealPos; // 图片的绘制位置    private float mBtnPos; // 按钮的位置    private float mBtnOnPos; // 开关打开的位置    private float mBtnOffPos; // 开关关闭的位置    private float mMaskWidth;    private float mMaskHeight;    private float mBtnWidth;    private float mBtnInitPos;    private int mClickTimeout;    private int mTouchSlop;    private final int MAX_ALPHA = 255;    private int mAlpha = MAX_ALPHA;    private boolean mChecked = false;    private boolean mBroadcasting;    private boolean mTurningOn;    private PerformClick mPerformClick;    private OnCheckedChangeListener mOnCheckedChangeListener;    private OnCheckedChangeListener mOnCheckedChangeWidgetListener;    private boolean mAnimating;    private final float VELOCITY = 350;    private float mVelocity;    private final float EXTENDED_OFFSET_Y = 15;    private float mExtendOffsetY; // Y轴方向扩大的区域,增大点击区域    private float mAnimationPosition;    private float mAnimatedVelocity;    public SwitchButton(Context context, AttributeSet attrs) {        this(context, attrs, android.R.attr.checkboxStyle);    }    public SwitchButton(Context context) {        this(context, null);    }    public SwitchButton(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        initView(context);    }    private void initView(Context context) {        mPaint = new Paint();        mPaint.setColor(Color.WHITE);        Resources resources = context.getResources();        // get viewConfiguration        mClickTimeout = ViewConfiguration.getPressedStateDuration()                + ViewConfiguration.getTapTimeout();        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();        // get Bitmap        mBottom = BitmapFactory.decodeResource(resources, R.drawable.bottom);        mBtnPressed = BitmapFactory.decodeResource(resources, R.drawable.btn_pressed);        mBtnNormal = BitmapFactory.decodeResource(resources, R.drawable.btn_unpressed);        mFrame = BitmapFactory.decodeResource(resources, R.drawable.frame);        mMask = BitmapFactory.decodeResource(resources, R.drawable.mask);        mCurBtnPic = mBtnNormal;        mBtnWidth = mBtnPressed.getWidth();        mMaskWidth = mMask.getWidth();        mMaskHeight = mMask.getHeight();        mBtnOffPos = mBtnWidth / 2;        mBtnOnPos = mMaskWidth - mBtnWidth / 2;        mBtnPos = mChecked ? mBtnOnPos : mBtnOffPos;        mRealPos = getRealPos(mBtnPos);        final float density = getResources().getDisplayMetrics().density;        mVelocity = (int) (VELOCITY * density + 0.5f);        mExtendOffsetY = (int) (EXTENDED_OFFSET_Y * density + 0.5f);        mSaveLayerRectF = new RectF(0, mExtendOffsetY, mMask.getWidth(), mMask.getHeight()                + mExtendOffsetY);        mXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);    }    @Override    public void setEnabled(boolean enabled) {        mAlpha = enabled ? MAX_ALPHA : MAX_ALPHA / 2;        super.setEnabled(enabled);    }    public boolean isChecked() {        return mChecked;    }    public void toggle() {        setChecked(!mChecked);    }    /**     * 内部调用此方法设置checked状态,此方法会延迟执行各种回调函数,保证动画的流畅度     *      * @param checked     */    private void setCheckedDelayed(final boolean checked) {        this.postDelayed(new Runnable() {            @Override            public void run() {                setChecked(checked);            }        }, 10);    }    /**     * 

* Changes the checked state of this button. *

* * @param checked true to check the button, false to uncheck it */ public void setChecked(boolean checked) { if (mChecked != checked) { mChecked = checked; mBtnPos = checked ? mBtnOnPos : mBtnOffPos; mRealPos = getRealPos(mBtnPos); invalidate(); // Avoid infinite recursions if setChecked() is called from a // listener if (mBroadcasting) { return; } mBroadcasting = true; if (mOnCheckedChangeListener != null) { mOnCheckedChangeListener.onCheckedChanged(SwitchButton.this, mChecked); } if (mOnCheckedChangeWidgetListener != null) { mOnCheckedChangeWidgetListener.onCheckedChanged(SwitchButton.this, mChecked); } mBroadcasting = false; } } /** * Register a callback to be invoked when the checked state of this button * changes. * * @param listener the callback to call on checked state change */ public void setOnCheckedChangeListener(OnCheckedChangeListener listener) { mOnCheckedChangeListener = listener; } /** * Register a callback to be invoked when the checked state of this button * changes. This callback is used for internal purpose only. * * @param listener the callback to call on checked state change * @hide */ void setOnCheckedChangeWidgetListener(OnCheckedChangeListener listener) { mOnCheckedChangeWidgetListener = listener; } @Override public boolean onTouchEvent(MotionEvent event) { int action = event.getAction(); float x = event.getX(); float y = event.getY(); float deltaX = Math.abs(x - mFirstDownX); float deltaY = Math.abs(y - mFirstDownY); switch (action) { case MotionEvent.ACTION_DOWN: attemptClaimDrag(); mFirstDownX = x; mFirstDownY = y; mCurBtnPic = mBtnPressed; mBtnInitPos = mChecked ? mBtnOnPos : mBtnOffPos; break; case MotionEvent.ACTION_MOVE: float time = event.getEventTime() - event.getDownTime(); mBtnPos = mBtnInitPos + event.getX() - mFirstDownX; if (mBtnPos >= mBtnOffPos) { mBtnPos = mBtnOffPos; } if (mBtnPos <= mBtnOnPos) { mBtnPos = mBtnOnPos; } mTurningOn = mBtnPos > (mBtnOffPos - mBtnOnPos) / 2 + mBtnOnPos; mRealPos = getRealPos(mBtnPos); break; case MotionEvent.ACTION_UP: mCurBtnPic = mBtnNormal; time = event.getEventTime() - event.getDownTime(); if (deltaY < mTouchSlop && deltaX < mTouchSlop && time < mClickTimeout) { if (mPerformClick == null) { mPerformClick = new PerformClick(); } if (!post(mPerformClick)) { performClick(); } } else { startAnimation(!mTurningOn); } break; } invalidate(); return isEnabled(); } private final class PerformClick implements Runnable { public void run() { performClick(); } } @Override public boolean performClick() { startAnimation(!mChecked); return true; } /** * Tries to claim the user's drag motion, and requests disallowing any * ancestors from stealing events in the drag. */ private void attemptClaimDrag() { mParent = getParent(); if (mParent != null) { mParent.requestDisallowInterceptTouchEvent(true); } } /** * 将btnPos转换成RealPos * * @param btnPos * @return */ private float getRealPos(float btnPos) { return btnPos - mBtnWidth / 2; } @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); 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(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension((int) mMaskWidth, (int) (mMaskHeight + 2 * mExtendOffsetY)); } private void startAnimation(boolean turnOn) { mAnimating = true; mAnimatedVelocity = turnOn ? -mVelocity : mVelocity; mAnimationPosition = mBtnPos; new SwitchAnimation().run(); } private void stopAnimation() { mAnimating = false; } private final class SwitchAnimation implements Runnable { @Override public void run() { if (!mAnimating) { return; } doAnimation(); FrameAnimationController.requestAnimationFrame(this); } } 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); } private void moveView(float position) { mBtnPos = position; mRealPos = getRealPos(mBtnPos); invalidate(); }}

二、控制辅助类FrameAnimationController

package com.org.switchbtn;import android.os.Handler;import android.os.Message;public class FrameAnimationController {private static final int MSG_ANIMATE = 1000;public static final int ANIMATION_FRAME_DURATION = 1000 / 60;private static final Handler mHandler = new AnimationHandler();private FrameAnimationController() {throw new UnsupportedOperationException();}public static void requestAnimationFrame(Runnable runnable) {Message message = new Message();message.what = MSG_ANIMATE;message.obj = runnable;mHandler.sendMessageDelayed(message, ANIMATION_FRAME_DURATION);}public static void requestFrameDelay(Runnable runnable, long delay) {Message message = new Message();message.what = MSG_ANIMATE;message.obj = runnable;mHandler.sendMessageDelayed(message, delay);}private static class AnimationHandler extends Handler {public void handleMessage(Message m) {switch (m.what) {case MSG_ANIMATE:if (m.obj != null) {((Runnable) m.obj).run();}break;}}}}
三、看最后监听,调用,实现MainActivity

因为自定义SwitchButton这个类是继承checkbox。所以还是用这个接口OnCheckedChangeListener。用这个接口实现监听。

package com.switchbutton.activity;import com.org.switchbtn.SwitchButton;import android.os.Bundle;import android.app.Activity;import android.widget.CompoundButton;import android.widget.CompoundButton.OnCheckedChangeListener;import android.widget.Toast;public class MainActivity extends Activity implements OnCheckedChangeListener{private SwitchButton mMsgNotifySwitch;private SwitchButton mMsgSoundSwitch;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initUI();}private void initUI() {mMsgNotifySwitch = (SwitchButton)findViewById(R.id.message_notify_switch);mMsgSoundSwitch = (SwitchButton)findViewById(R.id.message_sound_switch);mMsgNotifySwitch.setOnCheckedChangeListener(this);mMsgSoundSwitch.setOnCheckedChangeListener(this);}@Overridepublic void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {switch (buttonView.getId()) {case R.id.message_notify_switch:if (isChecked) {Toast.makeText(this, "新消息提醒打开", Toast.LENGTH_SHORT).show();}else {Toast.makeText(this, "新消息提醒关闭", Toast.LENGTH_SHORT).show();}break;case R.id.message_sound_switch:if (isChecked) {Toast.makeText(this, "声音提醒打开", Toast.LENGTH_SHORT).show();}else {Toast.makeText(this, "声音提醒关闭", Toast.LENGTH_SHORT).show();}break;default:break;}}}
四、附上最后的xml

                                                
到这里结束了,欢迎交流学习自定义控件。

源码下载:点击

更多相关文章

  1. Android(安卓)样式的开发
  2. 2.2.3帧布局
  3. Android(安卓)使用ViewPager实现左右循环滑动图片
  4. TextView最大长度限制,超出部分省略号显示
  5. android .9图讲解
  6. android:ellipsize="end"在ConstraintLayout中无效的问题
  7. 还不会View绘制?本文带你get新技能!!
  8. 赵雅智_java的多线程下载移植到android客户端
  9. android 阻尼效果(图片下拉变大)

随机推荐

  1. NDK的扩展
  2. android 保存和读取文件
  3. android---菜单栏选项
  4. android listView控件用法
  5. android 6.0锁屏界面时间位置修改
  6. android 动画框架
  7. Android(安卓)webView 使用back功能返回
  8. android之将图片转化为圆形图片
  9. android 查看so,jar栈内存使用情况
  10. Android(安卓)Cache 解析